zoukankan      html  css  js  c++  java
  • 三个层面学playbook(核心)

    三个层面学playbook(核心)

    ansible-playbook是ansible工具中的核心,对比ad-hoc(ansible)命令,可以把playbook理解为一系列动作的组成,结果传递、判断等等,这些是ansible命令无法完成的。
    ansible中如何使用yaml编写playbook,这对于很多人来说不容易搞懂,特别是层级缩进关系方面。
    我打算从三个层面讲playbook编写,正如4.7所说,按照playbook、play、tasks三个层面理解
    playbook关键字
    附上官网关关键字链接,本文对其中一部分举例,注意只是一部分介绍,我也不懂全部,介绍这些只为了理解playbook书写规则,规则掌握了,后续学习基本不会迷路

    # 先附上一部分综合例子,便于后面理解
    ---
    - hosts: lzcx
      # gather_facts: False
      connection: local
      become: yes
      remote_user: root
      vars:
        var1: value1
        var2: value2
      tasks:
        - debug: msg="{{var2}} {{var3}}"
          vars:
            var2: value3
            var3: value4
          when: ansible_distribution_major_version == "7"
        - name: print something
          shell: echo {{item}}
          with_items:
            - zhangsan
            - lisi
            - wangwu
          register: hi
          ignore_errors: True
        - debug: msg="{% for i in hi.results %} {{i.stdout}} {% endfor %}"
    

    5.1、playbook层面

    playbook层面,因为一个playbook一般一个play,便于维护,这里不作详细介绍。

    5.2、play层面(重要)

    play层面,对应元素hosts, tasks, 变量vars, connection, remote_user, become、name, 等等,这里介绍常见的元素。

    5.2.1、hosts指定主机

    指定inventory中的主机或主机组,一个play对应一个hosts,一般yaml文件里面就一个hosts,定义在开头
    取值方面,多个值按逗号","分割,支持2.5节里面的匹配,具体查看2.5节

    5.2.2、gether_facts

    获取远程主机信息,关闭时能加快playbook执行速度,前提是不需要获取主机信息,当需要调用主机信息相关变量,可以用setup模块查看,例如"ansible_all_ipv4_addresses",不能关闭该选项
    本章开始的gether_facts注释打开,则when语句行需要注释

    5.2.3、connection

    说明文档原文"connection type to use (default=smart)",默认会根据环境自动设置,一般不用自己设置

    5.2.4、become

    是否切换为其他用户,值为yes|no

    5.2.5、become_user

    切换的用户

    5.2.6、vars定义变量

    需要注意的是vars是可以存在了play层面和tasks层面的元素,同时tasks层面的vars变量会覆盖play层面的同名变量,这一点比较特殊,参考 5.3.1.4、 vars定义变量

    5.3、tasks层面(重要)

    5.3.1、变量

    ansible中定义变量的⽅式有很多种,⼤致有:(1)将模块的执⾏结果注册为变量;(2)直接定义字典类型的变量;(3)role中⽂件内定义变量;(4)命令⾏传递变量;(5)借助with_items迭代将多个task的结果赋值给⼀个变量;(6)inventory中的主机或主机组变量;(7)内置变量

    5.3.1.2、register注册变量

    使⽤register选项,可以将当前task的输出结果赋值给⼀个变量,注意,模块的输出结果是json格式的,所以,引⽤变量时要指定引⽤的对象

    ---
        - hosts: lzcx
          tasks:
            - shell: echo 'hahaha'
              register: temp
            - debug: var=temp.stdout
    

    5.3.1.3、 set_fact定义变量

    set_fact和register的功能很相似,也是将值赋值给变量。它更像shell中变量的赋值⽅式,可以将某个变量的值赋值给另⼀个变量,也可以将字符串赋值给变量

    ---
        - hosts: lzcx
          tasks:
            - shell: echo haha
              register: say_ha
            - set_fact: var1="{{say_ha.stdout}}"
            - set_fact: var2="your name is"
            - debug: msg="{{var2}} {{var1}}"
    

    5.3.1.4、 vars定义变量

    可以在play或task层次使⽤vars定义字典型变量。如果同名,则task层次的变量覆盖play层次的变量

    ---
        - hosts: lzcx
          vars:
            var1: value1
            var2: value2
          tasks:
            - debug: msg="{{var1}} {{var2}}"
              vars:
                var2: value2.2          # tasks层次的变量会覆盖play层次的同名变量
    

    5.3.1.5、 roles中的变量

    由于role是整合playbook的,它有默认的⽂件组织结构。其中有⼀个⽬录vars,其内的main.yml⽤于定义变量。还有defaults⽬录内的main.yml则是定义role默认变量的,默认变量的优先级最低

    5.3.1.6、 命令行传递变量

    ansible和ansible-playbook命令的"-e"选项都可以传递变量,传递的⽅式有两种: -e key=value 和 -e @var_file 。注意,当key=value⽅式传递变量时,如果变量中包含特殊字符,必须防⽌其被shell解析

    ansible localhost -m shell -a "echo {{say_hi}}" -e 'say_hi="hello world"'
    ansible localhost -m shell -a "echo {{say_hi}}" -e @/tmp/var_file1.yml
    
    # /tmp/var_file1.yml 内容
    ---
        say_hi: zhangsan lisi
    

    5.3.1.7、 借助with_items叠加变量

    ansible中可以借助with_items实现列表迭代的功能,作⽤于变量注册的⾏为上,就可以实现将多个结果赋值给同⼀个变量
    例如下⾯的playbook中,给出了3个item列表,并在shell模块中通过固定变量"{{item}}"分别迭代,第⼀次迭代的是haha,第⼆次迭代的是heihei,第三次迭代的是hehe,也就实现了3次循环。最后,将结果注册为变量hi_var。还可以使⽤for循环遍历列表

    #
    ---
        - hosts: lzcx
          connection: local
          remote_user: root
          become: yes
          tasks:
            - name: test with items
              shell: echo "{{item}}"
              with_items:
                - haha
                - heihei
                - hehe
              register: hi_var
            - debug: var=hi_var.results[0].stdout
            - debug: var=hi_var.results[1].stdout
            - debug: var=hi_var.results[2].stdout
            - debug: msg="{% for i in hi_var.results %} {{i.stdout}} {% endfor %}"
    

    每次迭代的过程中,调⽤item的模块都会将结果保存在⼀个key为results的数组中。因此,引⽤迭代后注册的变量时,需要在变量名中加上results,并指定数组名。例如上⾯的 hi_var.results[N].stdout
    建议使用循环输出格式

    5.3.1.8、 inventory中的主机变量和组变量

    在inventory⽂件中可以为主机和主机组定义变量,不仅包括内置变量赋值,还包括⾃定义变量赋值。
    主机变量优先级⾼于主机组变量,给定的主机组变量优先级⾼于all特殊组

    # 自定义临时清单
    cat /tmp/hosts
    192.168.1.214 ansible_ssh_port=22 var1=1   # 主机内置变量,这类变量不能调用
    [centos7]
    192.168.1.214 var1=2    # 主机变量,优先级最高
    [centos7:vars]
    var1=2.2                    # 主机组变量,优先级次之
    var2=3
    [all:vars]
    var2=4                      # 所有组变量,优先级最低
    # 执行查看变量优先级
    ansible 192.168.1.214 -i /tmp/hosts -m shell -a 'echo "{{var1}} {{var2}}"'
    # 结果
    192.168.1.214 | CHANGED | rc=0 >>
    2 3
    

    5.3.1.9、内置变量

    ansible除了inventory中内置的⼀堆不可被引⽤的设置类变量,还有⼏个全局都可以引⽤的内置变量,主要有以下⼏个:
    inventory_hostname、inventory_hostname_short、groups、groups_names、hostvars、play_hosts、inventory_dir、ansible_version

    1. inventory_hostname和inventory_hostname_short
      分表代表的是inventory中被控节点的主机名和主机名的第⼀部分,如果定义的是主机别名,则变量的值也是别名
    2. groups和group_names
      group_names返回的是主机所属主机组,如果该主机在多个组中,则返回多个组,如果它不在组中,则返回ungrouped这个特殊组
      groups变量则是返回其所在inventory⽂件中所有组和其内主机名。注意,该变量对每个控制节点都返回⼀次,所以返回的内容可能⾮常多
    3. hostvars
      该变量⽤于引⽤其他主机上收集的facts中的数据,或者引⽤其他主机的主机变量、主机组变量。其key为主机名或主机组名。但注意,在引⽤其他主机facts中数据时,要求被引⽤主机进⾏了facts收集动作,或者有facts缓存。否则都没收集,当然⽆法引⽤其facts数据。也就是说,当被引⽤主机没有facts缓存时,ansible的控制节点中必须同时包含引⽤主机和被引⽤主机。
      除了引⽤其他主机的facts数据,还可以引⽤其他主机的主机变量和主机组变量,且不要求被引⽤主机有facts数据,因为主机变量和主机组变量是在ansible执⾏任务前加载的
    4. play_hosts和inventory_dir
      play_hosts代表的是当前play所涉及inventory内的所有主机名列表
      inventory_dir是所使⽤inventory所在的⽬录
    5. ansible_version

    5.3.2、循环

    ansible中的循环都是借助迭代来实现的。基本都是以"with_"开头。以下是常见的⼏种循环

    5.3.2.1、with_items迭代列表(重要)

    参考 5.3.1.7,

    5.3.2.2、with_dict迭代字典项

    使⽤"with_dict"可以迭代字典项。迭代时,使⽤"item.key"表⽰字典的key,"item.value"表⽰字典的值
    另⼀种情况,字典是已存储好的。例如ansible facts中的ansible_eth0.ipv4,这种情况下,with_dict处可以直接指定该字典的key

    5.3.2.3、with_fileglob迭代⽂件

    例如,拷贝⼀堆⽤通配符匹配出来的⽂件到各远程主机上,注意,通配符⽆法匹配"/",因此⽆法递归到⼦⽬录中,也就⽆法迭代⼦⽬录中的⽂件

    5.3.2.4、with_lines迭代⾏(重要)

    可以将命令⾏的输出结果按⾏迭代。例如,find⼀堆⽂件出来,copy⾛

    ---
        - hosts: localhost
          tasks:
            - copy: src="{{item}}" dest=/tmp/yaml
              with_lines:
                - find /tmp -type -f -name "*.yml"
    

    5.3.2.4、 with_nested嵌套迭代

    嵌套迭代是指多次迭代列表项

    ---
    - hosts: localhost
      tasks:
        - debug: msg="{{item[0]}} & {{item[1]}}"
          with_nested:
            - [a, b]
            - [1, 2, 3]
    

    5.3.3、 条件判断when

    在ansible中,只有when语句可以实现条件判断。when判断的对象是task,所以和task在同⼀列表层次。它的判断结果决定它所在task是否执⾏,⽽不是它下⾯的task是否执⾏。when中引⽤变量的时候不需要加{{ }}符号。

    tasks: 
     - name: config the yum repo for centos 6
       yum_repository:
         name: epel
         description: epel
         baseurl: http://mirrors.aliyun.com/epel/6/$basearch/
         gpgcheck: no
       when: ansible_distribution_major_version == "6"
    

    when语句支持逻辑操作,或(or)、与(and)也可以分行写、非(not)
    支持直接引用定义了布尔值的变量
    可以使⽤jinja2的 defined 来测试变量是否已定义,使⽤ undefined 可以取反表⽰未定义

    tasks:
      - shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
        when: foo is defined
      - fail: msg="Bailing out. this play requires 'bar'"
        when: bar is undefined
    

    5.3.4、报错处理failed_when

    该选项类似编程语言中的try语句,能够抓取报错信息,让playbook执行完成

    ---
    - hosts: lzcx
      tasks:
        - name: this command prints FAILED when it fails
          command: /usr/bin/example-command -x -y -z
          register: command_result
          failed_when: "FAILED in command_result.stdout"
    

    很明显,例子中的命令不存在,即执行失败,failed_when通过调用注册变量打印错误信息,此时会生成yaml文件同名的retry后缀格式文件

    5.3.5、命名name

    用于给模块自定义命名,如果没有设置,默认输出模块名

    5.3.6、ignore_errors

    忽略报错信息

    ---
    - hosts: lzcx
      tasks:
        - name: this command prints FAILED when it fails
          command: /usr/bin/example-command -x -y -z
          register: command_result
          ignore_errors: True
          failed_when: "FAILED in command_result.stdout"
    

    此时不会生成yaml文件同名的retry后缀格式文件

    5.3.7、tag标签

    可以为playbook中的每个任务都打上标签,标签的主要作⽤是可以在ansible-playbook中设置只执⾏哪些被打上tag的任务或忽略被打上tag的任务,以下是ansible-playbook中关于tag的选项

    --list-tags # list all available tags
    t TAGS, --tags=TAGS # only run plays and tasks tagged with these values
    --skip-tags=SKIP_TAGS # only run plays and tasks whose tags do not match these values

    tasks:
      - name: make sure apache is running
        service: name=httpd state=started
        tags: apache
      - name: make sure mysql is runnig
        service: name=mysqld state=started
        tags: mysql
    

    5.3.8、include

    include的设计是为了让playbook模块化,将任务中的playbook分拆,细化各个部分,便宜维护和复用
    可以将task列表和handlers独⽴写在其他的⽂件中,然后在某个playbook⽂件中使⽤include来包含它们。除此之外,还可以写独⽴的playbook⽂件,使⽤include来包含这个⽂件。也即是说,include可以导⼊两种⽂件:导⼊task、导⼊playbook。除此之外还可以传入变量给include
    注意:nsible2.4之前,引入外部task或play只有include方法,在2.4之后,新增了includes和imports方法,具体查看官网说明includes和imports
    用法一

    # 引入task列表文件,a.yml
    ---
    - name: execute ntpdate
      shell: /usr/sbin/ntpdate ntp1.aliyun.com
    - name: execute the variables
      debug: msg="{{hi}} {{haha}}"
    

    同级目录下调用a.yml

    ---
    - hosts: localhost
      tasks:
        - include: a.yml hi="Hello world"    # 传入参数
          vars:
            haha: "ni hao ya"                    # 用vars传递参数
    

    用法二

    # 引入整个playbook,由于是引入整个playbook,加载里面的play,需要注意层级关系
    - name: this is a play at the top level of a file
      hosts: localhost
      become: yes
      remote_user: root
      tasks:
        - name: say hi
          tags: foo
          shell: echo "hahaha"
    - include: webserver.yml   # 注意层级关系
    - include: dbserver.yml
    

    从上面两个例子可以看出,include和vars元素一样,是可以跨层级出现的,两者跨的层级不同,vars是跨play和tasks;include是跨tasks和playbook

    5.3.9、notify和handlers

    notify:ansible中⼏乎所有的模块都具有幂等性,这意味着被控主机的状态是否发⽣改变是能被捕捉的,即每个任务的changed=true或changed=false
    ansible在捕捉到changed=true时,可以触发notify组件,notify是⼀个组件,并⾮⼀个模块,它可以直接定义action,其主要⽬的是调⽤handler
    notify这个action可用于在每个play的最后被触发,这样可以避免多次有改变发生时每次都执行指定的操作,取而代之,仅在所有的变化发生完成后一次性地执行指定操作
    handlers:handlers是一些 task 的列表,通过名字来引用,它们和一般的 task 并没有什么区别,handlers里面的name必须要和notify一致,最佳的应用场景是用来重启服务,或者触发系统重启操作

    tasks:
      - name: template configuration file
        template: src=template.j2 dest=/etc/foo.conf
        notify:
          - restart memcached
          - restart apache
        handlers:
          - name: restart memcached
            service: name=memcached state=restarted
          - name: restart apache
            service: name=apache state=restarted
    

    5.3.10、总结

    ansible中的playbook到此介绍,基础部分相信能够搞懂了,至少版本新特性,进阶高级用法等等,对于掌握规则来说不难,规则掌握之后,跟着官网例子学习就可以了

  • 相关阅读:
    数据结构C语言实现----直接插入排序
    c++primer笔记十六、模板与泛型编程
    c++primer笔记十五、面向对象程序设计
    c++primer笔记十四、重载运算和类型转换
    c++primer笔记十三、拷贝控制
    c++primer笔记十二、动态内存
    c++primer笔记十一、关联容器
    c++primer笔记十、泛型算法
    c++primer笔记九、顺序容器
    c++primer笔记八、 IO
  • 原文地址:https://www.cnblogs.com/AutoSmart/p/10273232.html
Copyright © 2011-2022 走看看