三个层面学playbook(核心)
ansible-playbook是ansible工具中的核心,对比ad-hoc(ansible)命令,可以把playbook理解为一系列动作的组成,结果传递、判断等等,这些是ansible命令无法完成的。
ansible中如何使用yaml编写playbook,这对于很多人来说不容易搞懂,特别是层级缩进关系方面。 我打算从三个层面讲playbook编写,正如4.7所说,按照playbook、play、tasks三个层面理解 附上官网关关键字链接,本文对其中一部分举例,注意只是一部分介绍,我也不懂全部,介绍这些只为了理解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。还可以使⽤f or循环遍历列表#--- - 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/hosts192.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- inventory_hostname和inventory_hostname_short 分表代表的是inventory中被控节点的主机名和主机名的第⼀部分,如果定义的是主机别名,则变量的值也是别名
- groups和group_names group_names返回的是主机所属主机组,如果该主机在多个组中,则返回多个组,如果它不在组中,则返回 ungrouped这个特殊组 groups变量则是返回其所在inventory⽂件中所有组和其内主机名。注意,该变量对每个控制节点都返回⼀次,所以返回的内容可能⾮常多
- hostvars 该变量⽤于引⽤其他主机上收集的facts中的数据,或者引⽤其他主机的主机变量、主机组变量。其key为主机名或主机组名。但注意,在引⽤其他主机facts中数据时,要求被引⽤主机进⾏了facts收集动作,或者有facts缓存。否则都没收集,当然⽆法引⽤其facts数据。也就是说,当被引⽤主机没有facts缓存时,ansible的控制节点中必须同时包含引⽤主机和被引⽤主机。 除了引⽤其他主机的facts数据,还可以引⽤其他主机的主机变量和主机组变量,且不要求被引⽤主机有facts数据,因为主机变量和主机组变量是在ansible执⾏任务前加载的
- play_hosts和inventory_dir play_hosts代表的是当前play所涉及inventory内的所有主机名列表 inventory_dir是所使⽤inventory所在的⽬录
- 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 f acts中的ansible_eth0.ipv4,这种情况下,with_dict处可以直接指定该字典的key5.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方法,具体查看官网说明用法一:# 引入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到此介绍,基础部分相信能够搞懂了,至少版本新特性,进阶高级用法等等,对于掌握规则来说不难,规则掌握之后,跟着官网例子学习就可以了