playbook的语法∶YAML
ansible的playbook采用yaml语法,它以非常简洁的方式实现了json格式的事件插述。y aml之于json就像markdown之于html一样,极度简化了json的书写。
YAML文件后缀通常为.yaml或.yml。
YAML在不少工具里都使用,学习它是"一次学习、终生受益"的,所以很有必要把yaml 的语法格式做个梳理,系统性地去学—学。
YAML的基本语法规则如下∶
(1)使用缩进表示层级关系
(2)缩进时不允许使用Tab键,只允许使用空格
(3)缩进的空格数目不重要,只要相同层级的元素左对齐即可
(4)yaml文件以"---"作为文档的开始,以表明这是一个yaml文件(即使没有使用"---"开头,也不会有什么影响)
(5)# 表示主释,从这个字符一直到行尾,都会被解析器忽略
(6)字符串不用加引号,但在可能产生歧义时,需加引号(单双引号皆可),比如引用变量时
(7)布尔值非常灵活,不分区大小写的truefalse、yes/no、on/off、y/n、0和1都允许
YAML支持三种数据结构∶
(1)对象∶key/value格式,也称为哈希结构、字典结构或关联数组
(2)数组∶也称为列表
(3)标量(scalars)∶单个值
可以去找一些在线YAML转换JSON网站,比如 http//:yaml-online-parser.appspot.com 通过在线转换可以验证或查看自己所写的YAML是否出错以及哪里出错。
对象
一组键值对,使用冒号隔开key和value。注意,冒号后必须至少一个空格。
name: "lulei"
等价于json∶
{
"name": "lulei"
}
数组
---
- Shell
- Perl
- Python
等价于json∶
["Shel","Perl",Python]
也可以使用行内数组(内联语法)的写法∶
---
["Shell","Perl","Python"]
再例如∶
---
- langl:Shell
- lang2:Perl
- lang3:Python
等价于json∶
[
{"lang1": "Shel"),
{"lang2": "Per"},
{"lang3": "Python"}
]
将对象和数组混合∶
---
languages:
- Shell
- Perl
- Python
等价于json∶
{
"languages":["Shel",Per","Python"]
}
字典
---
person1:
name: xiaohong
age: 18
gender: male
person2:
name: xiaofang
age: 19
gender: female
等价于json∶
{
"person2":{
"gender: "female",
"age": 19,
"name": "xiaofanggao"
},
"person1":{
"gender": "male",
"age": 18,
"name": "junmajinlong"
}
}
也可以使用行内对象的写法∶
---
person1:{name: junmajinlong,age: 18,gender: male}
复合结构
---
- person1:
name:junmajinlong
age: 18
langs:
-Perl
- Ruby
- person2:
name: xiaofanggao
age: 19
langs:
- Python
- Javascript
等价于json∶
[
{
"langs:[
"Perl",
"Ruby",
"Shell"
],
"person: null,
"age": 18,
"name":"junmainlong
},
{
"person2": null,
"age": 19,
"langs": [
"Python",
"Javascript
],
"name":"xiaofanggao"
}
]
playbook的写法
了解YAML写法之后,就可以来写Ansible的playbook了。
playbook可以包含一个或多个play,每个play可以包含一个或多个任务,且每个play都需要指定要执行该play的目标主机。
于是,将下面这个ad-hoc模式的ansible任务改成等价的playbook模式∶
$ ansible nginx-m copy-a'src=/etc/passwd dest=/tmp'
假设这个playbook的文件名为copyyml,其内容如下∶
---
- name: first play
hosts: all
gather_facts: false
tasks:
- name: copy /etc/passwd to /tmp
copy: src=/etc/passwd dest=/tmp
- name: register vars
shell: hostname
register: system_status
- name: display vars
debug: msg={{system_status.stdout}}
再来解释一下这个playbook文件的含义。
playbook中,每个play都需要放在数组中,所以在playbook的顶层使用列表的方式- xxx∶来表示这是一个play(此处是 - name :
也可以使用- hosts : 开头)每个play都必须包含 hosts和 tasks指令。
hosts指令用来指定要执行该play的目标主机,可以是主机名,也可以是主机组,还支持其它方式来更灵活的指定目标主机。
tasks指令用来指定这个play中包含的任务,可以是一个或多个任务,任务也需要放在play的数组中,所以tasks指令内使用-xx∶的方式来表示每一个任务(此处是copy :)
gather_facts是一个play级别的指令设置,它是一个负责收集目标主机信息的任务,由setup模块提供。默认情况下,每个play都会先执行这个特殊的任务,收集完信息之后才开始执行其它任务。但是,收集目标主机信息的效率很低,如果能够确保playbo0k 中不会使用到所收集的信息,可以显式指定 gather_facts;no来禁止这个默认执行的收集任务,这对效率的提升是非常可观的。
此外每个play和每个task都可以使用name指令来命名,也建议尽量为每个play和每个task都命名,且名称具有唯一性。
默认的任务执行策略
最后,再来简单探究一下默认情况下Ansble是以什么样的策略去控制多个节点执行多个任务的。
假设有10个目标节点要执行某个play中的个任务∶tA、IB、tC。
默认情况下,会从10个目标节点中选择5个节点作为第一批次的节点执行任务A,第一批次的5个节点都执行tA完成后,将选择剩下的5个节点作为第二批次执行任务A。所有节点都执行完任务A后,第一批次的5节点开始执行任务B,然后第二批次的5个节点执行任务B。所有节点都执行完任务B后,第一批次的5节点开始执行任务C,然后第二批次的5个节点执行任务C。
这里提到的5个节点的数量5,是由配置文件中fork指令的值决定的,默认值为5。
$ grep 'fork'/etc/ansiblel/ansible.cfg
forks=5
fork指令用来指定Ansible要创建几个子进程来执行任务,每个节点默认对应一个ansibl e-playbook进程和ssh进程,指定fork=5,表示创建5个ansible-playbook子进程。所以,fork的值也代表了一次选中多少个节点执行任务。例如,将hosts指令指定为all,并将gather_facts指令取消注释,因为这个任务执行比较慢,方便我们去观察进程列表。
- name:first play
hosts: all
#gather_facts:false
执行该playbook。
$ ansible-playbook test.yaml
然后再另外一个终满上去查看进程列表∶
$ pstree -c | grep 'ansible'
│'ansible-playboo-+-ansible-playboo---ssh
|-ansible-playboo---ssh
|-ans1ble-playboo---ssh
|-ansible-playboo---ssh
|-ansible-playboo---ssh
| `-(ansible-playboo}
如果某个节点连接失败或执行某个任务失败,则该节点将不再执行该play中的后续任务(但会执行后续的play)。