我们之前的例子都是使用ansible <host-pattern> [-m module_name] [-a args]
这种语法来完成的。对于这种每次使用一个模块,只能执行一个任务的方式,称为ad-hoc
(点对点模式),相当与被控节点在bash中执行一句shell命令。如果想要简单使用ansible的话,ad-hoc配合shell脚本可以满足大部分情况了。
ansible之所以能成为当今自动化运维的一杆大旗是基于它提供了另一种任务方式——playbook
。
playbook是剧本的意思,而之前提到的inventory就像是演员表,ansible的程序执行可以形象的看成拍电影,其中playbook中的每一个play就相当于是电影的每个片段,每一个play都可以有多个任务(tasks),相当于电影片段中的每一幕。每个play中可以定义专属的变量,对应电影片段中的场景布置,每个play中都需要指定执行该play的主机,即当期上场的演员名单。等等的这些组织多个任务多种行为的方式,正是ansible强大的地方——“编排”,而编写这些playbook的我们,即是整个电影的导演。
playbook示例
环境清单
主机名 | IP地址 | 操作系统版本 | 内核版本 | 角色 |
---|---|---|---|---|
slions_pc1 | 192.168.100.10 | CentOS 7.6.1810 | 3.10.0-957.el7.x86_64 | 控制节点 |
slions_pc2 | 192.168.100.11 | CentOS 7.6.1810 | 3.10.0-957.el7.x86_64 | 被控节点 |
slions_pc3 | 192.168.100.12 | CentOS 7.6.1810 | 3.10.0-957.el7.x86_64 | 被控节点 |
所有的主机上都已启动sshd服务并保持默认配置(监听22端口)。
为了后续控制目标节点方便些,事先在控制节点将所有节点的DNS解析配置好了。并且配置了控制节点到被控节点间的免密。
1 | [root@slions_pc1 ~]# cat /etc/hosts |
inventory文件
1 | [root@slions_pc1 ansible]# cat /etc/ansible/hosts |
playbook文件
1 | [root@slions_pc1 ansible]# cat demo.yml |
使用ansible-playbook命令执行这个playbook:
1 | [root@slions_pc1 ansible]# ansible-playbook demo.yml |
从输出可以直观的看到,执行完”play 1”之后,执行”play 2”,在一个 play 之中,所有 hosts 会获取相同的任务指令,且PLAY和TASK后面都指明了play的名称、task的名称。
最后输出的是每个主机执行任务的状态统计,比如某个主机节点执行成功的任务有几个,失败的有几个。
playbook语法:yaml
Playbooks 的格式是YAML,它以非常简洁的方式实现了json格式的事件描述,如果之前接触过kubernetes,对yaml应该就非常熟悉了。具体的语法可以百度查看,以下列举一些常用的规则。
- 使用缩进表示层级关系
- 缩进不允许使用tab建,只能使用空格键
- 缩进空格数目不重要,只要相同层级的元素左对齐即可,程序判别配置的级别是通过缩进结合换行实现的
- 在单一一个
playbook
文件中,可以连续三个连子号(---
)区分多个play
- 使用#号注释代码
YAML
文件内容和Linux
系统大小写判断方式保持一致,是区分大小写的
YAML支持三种数据结构:
- 对象:key/value格式,也称为哈希结构、字典结构或关联数组
- 数组:也称为列表
- 标量(scalars):单个值
对象
一组键值对,使用冒号隔开key和value。注意,冒号后必须至少一个空格。
1 | name: slions |
等价于json:
1 | { |
数组
1 |
|
等价于json:
1 | ["cat","pig"] |
也可以使用行内数组(内联语法)的写法:
1 |
|
再例如:
1 |
|
等价于json:
1 | [ |
将对象和数组混合:
1 |
|
等价于json:
1 | { |
字典
1 |
|
等价于json:
1 | { |
也可以使用行内对象的写法:
1 | --- |
字符串续行
字符串可以写成多行,从第二行开始,必须至少有一个单空格缩进。换行符会被转为空格。
1 |
|
等价于json:
1 | { |
也可以使用>
换行,它类似于上面的多层缩进写法。此外,还可以使用|
在换行时保留换行符。
1 |
|
等价于json:
1 | {'str1': 'hello world', 'str2': 'hello\nworld\n'} |
空值
YAML中某个key有时候不想为其赋值,可以直接写key但不写value,另一种方式是直接写null,还有一种比较少为人知的方式:波浪号~。
下面几种方式全是等价的:
1 | key1: |
YAML中的单双引号和转义
YAML中的字符串是可以不用使用引号包围的,但是如果包含了特殊符号,则需要使用引号包围。
单引号包围字符串时,会将特殊符号保留。
双引号包围字符串时,反斜线需要额外进行转义。
例如,下面几对书写方式是等价的:
1 | - key1: '\.yml' |
等价于json:
1 | [ |
playbook写法
将下面这个ad-hoc模式的ansible任务改成等价的playbook模式:
1 | ansible leader -m copy -a 'src=/etc/passwd dest=/tmp' |
playbook:
1 |
|
playbook中,每个play都需要放在数组中,所以在playbook的顶层使用列表的方式- xxx
:来表示这是一个play(此处是- hosts:
)。
每个play都必须包含hosts
和tasks
指令。
hosts
指令用来指定要执行该play的目标主机,可以是主机名,也可以是主机组,还支持正则表达式或者是变量的形式来更灵活的指定目标主机。tasks
指令用来指定这个play中包含的任务,可以是一个或多个任务,任务也需要放在play的数组中,所以tasks指令内使用- xxx
:的方式来表示每一个任务(此处是- copy:
)。
gather_facts
是一个play级别的指令设置,它是一个负责收集目标主机信息的任务,由setup模块提供。默认情况下,每个play都会先执行这个特殊的任务,收集完信息之后才开始执行其它任务。但是,收集目标主机信息的效率很低,如果能够确保playbook中不会使用到所收集的信息,可以显式指定gather_facts: false
来禁止这个默认执行的收集任务,这对效率的提升是非常可观的。
此外每个play和每个task都可以使用name
指令来命名,也建议尽量为每个play和每个task都命名,且名称具有唯一性。
所以上面的playbook可以改写为:
1 |
|
playbook中的主机
play中的hosts指令通过pattern的方式来筛选节点,pattern的指定方式有以下几种规则:
直接指定inventory中定义的主机名或者是主机组名,如
hosts: slions_pc1
、hosts: leader
指定主机组名时,可使用索引的方式表示组中的第几个主机,如
hosts: leader[0]
可以使用冒号或者逗号来分开多个pattern,如
hosts: slions_pc3:leader
支持范围表示,如:
hosts: slions_pc[1:3]
支持通配符,如:
hosts: *
支持正则表达式,需使用
~
开头,如:hosts: ~slions_pc(1|2)
pattern前面加一个&符号表示取交集,如
leader:&worker
会匹配同时存在于leader和worker中的主机pattern前面加一个!符号表示取差集,如
leader:!worker
匹配存在与leader组但不在worker组中的主机
playbook模块参数的传递方式
tasks中的模块参数有几种写法,都是等价的,保持整体统一就好。
1 | - name: this is a play |