之前的模块都是使用Ad-hoc方式(Ad-hoc方式是一种可以快速输入的命令,而且不需要保存起来的命令)点对点命令执行,可以管理远程主机,但如果服务器数量比较多,配置信息也比较多,可以利用Ansible PlayBook编写剧本,以更加简便的方式实现任务处理的自动化和流程化。
playbook是Ansible的配置、部署和编排的语言。Playbook可以定制配置,他们可以按照你指定的操作步骤有序执行,支持同步和异步方式。形象点的说就是:如果ansible的各模块(能实现各种功能)是车间里的各工具;playbook就是指导手册,目标远程主机就是库存和原料对象。
playbook可作为一个适合部署复杂应用程序的基础。
PlayBook是由一个或多个“play”组成的列表,在play中的内容被我们称之为tasks,也叫任务,也就是说多个tasks组成了一个play,task调用Ansible的各种模块(module)。将多个play组织在一个playbook剧本中,组成一个完整的流程控制集合,完成一个复杂的工作。
下面就是一个只包含了2个play的playbook:
说明:
playbook中的每一个play的目的都是为了让某个或某些主机以某个指定的用户身份执行任务。
hosts用于指定要执行指定任务的主机,其可以是一个或多个由冒号分隔主机组。
vars 执行对应任务时,携带的到远端主机的变量信息
remote_user则用于指定远程主机上的执行任务的用户。
play的主体部分是task list。task list中的各任务按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个任务后再开始第二个。在运行自上而下某playbook时,如果中途发生错误,所有已执行任务都将回滚,因此,在更正playbook后重新执行一次即可。
task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致。
每个task都应该有其name,name 是我们对当前任务的说明,用于playbook的执行结果输出,建议其内容尽可能清晰地描述任务执行步骤。如果未提供name,则执行的模块名将用于输出。
定义task的action(动作)可以使用module_name:module_args”。
一个name只能包括一个task。
在众多模块中,只有command和shell模块仅需要给定一个列表而无需使用“key=value”格式
注:
如果命令或脚本的退出码不为零,可以使用如下方式替代:
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true
或者使用ignore_errors来忽略错误信息:
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand
ignore_errors: True
playbook是基于YAML语言格式配置,通过YAML格式来进行描述定义的,扩展名是yml或yaml。
总结:简单来说playbook就是一个用yaml语法把多个模块堆起来的一个文件而已,把不同的模块按照顺序编排在剧本中,ansible就会按照剧本一步一步的执行,最终达到我们的目的。
更多playbook官方说明参考:
Intro to playbooks — Ansible Documentation
Playbook采用YAML语言编写,每一个Ansible的Playbook都是一个YAML格式的文件,因此要学习编写剧本(playbook),我们先来了解YAML语法的基本用法。
1、yaml文件以
---
开头,以表明这是一个yaml文件,就像xml文件在开头使用宣称它是xml文件一样。但即使没有使用
---
开头,也不会有什么影响。2、yaml中使用"#"作为注释符。
3、大小写敏感
4、使用缩进表示层级关系:YAML使用一个固定的缩进风格表示层级结构,同样的缩进代表同样的级别。缩进时不允许使用Tab键,只允许使用空格。
注:缩进的空格数目没有限制,但要求同一层级左对齐。判断是否是同一级别是通过缩进结合换行来实现的。
5、冒号,以冒号结尾的除外,其他所有冒号后面必须有空格。
6、短横线,表示列表项,使用一个短横杠加一个空格。多个项使用同样的缩进级别作为同一列表。7、关于布尔值的书写格式,即true/false的表达方式。其实playbook中的布尔值类型非常灵活,可分为两种情况:
模块的参数:这时布尔值作为字符串被ansible解析。接受yes/on/1/true/no/off/0/false。例如上面示例中的update_cache=yes。
非模块的参数:这时布尔值被yaml解释器解析,完全遵循yaml语法。接受不区分大小写的true/yes/on/y/false/no/off/n。例如gather_facts: True。
建议遵循ansible的官方规范,模块的布尔参数采用yes/no,非模块的布尔参数采用True/False。
8、YAML文件扩展名通常为yml或yaml
对于Ansible, 每一个YAML文件都是从一个列表(列表list,又称为序列 sequence)开始。列表中的每一项都是一个键值对,通常它们被称为一个 “哈希” 或 “字典”或映射。所以, 我们需要知道如何在YAML中编写列表和字典。
列表中的所有成员都开始于相同的缩进级别, 并且使用一个 "- " 作为开头(一个减号和一个空格)。
具体在ansible playbook中,列表所描述的是局部环境,它不一定要有名称,只要使用"- ",它就表示圈定一个范围,范围内的项都属于该列表。例如:
一个字典是由一个简单的 键: 值 的形式组成(这个冒号后面必须是一个空格),它一般当作列表项的属性。
k/v的值可同行写也可换行写。同行使用:分隔
v可是个字符串,也可是另一个列表
YAML列表与字典是可以自由组合在一起的,它们之间可以相互嵌套,通过非常灵活的组合,可以帮助我们描述更加复杂的对象属性。
此外, Ansible使用“{{ var }}”来引用变量.。如果一个值以 “{” 开头, YAML 将认为它是一个字典, 所以我们必须引用它, 像这样foo: "{{ var }}"。
下面我们来看一个playbook的例子
在编写剧本之前,我们先来回顾两个简单的ad-hoc命令,比如如下两条命令:
[root@ansible-server ~]# ansible web_servers -m ping
[root@ansible-server ~]# ansible web_servers -m file -a "path=/tmp/test211 state=directory"
上述命令表示使用ping模块去ping主机组web_servers中的主机,然后再用file模块在主机组web_servers的主机上创建目录,那么,如果把上述命令转换成playbook的表现形式,该如何书写呢?示例如下
如上所示,第一行使用三个减号作为开始,在YAML语法中,"---"表示文档开始。
第二行使用"- "作为开头(注意:减号后面有空格),YAML使用"- "表示一个列表块的开始,从上例可以看出,"- "后面使用hosts关键字指定了要操作的主机,hosts关键字对应的值为web_servers,表示我们要在web_servers主机组上进行操作,"hosts: web_servers"是一个键值对,注意,在YAML语法中使用冒号映射键值对时,'冒号'后面必须有'空格',这也是语法,没有为什么,记住就好了。
第三行,使用remote_user关键字可以指定在进行远程操作时使用哪个用户进行操作,'remote_user: root'表示以远程主机的root用户进行操作,上图中,remote_user关键字与hosts关键字对齐,表示它们是平级的,之前的学习中提到过,在YAML语法中进行缩进时,不能使用tab键进行缩进,必须使用空格。
第四行,使用tasks关键字指明要进行操作的任务列表,之后的行都属于tasks键值对中的值。
之后的行都属于tasks任务列表中的任务,可以看出,整个任务列表一共有两个任务组成,每个任务都以"- "开头,每个任务都有自己的名字,任务名使用name关键字进行指定,第一个任务使用ping模块,使用ping模块时没有指定任何参数。第二个任务使用file模块,使用file模块时,指定了path参数与state参数的值。
好了,aa.yml已经编写完成了,但是,我们还没有运行这个剧本,运行剧本需要使用'ansible-playbook'命令,示例如下
如上图所示,playbook执行后返回了一些信息,这些信息是这次剧本运行的概况。
'PLAY [web_servers]'表示这次运行的playbook中有一个'play',是针对web_servers这个主机组运行的,一个'playbook'是由一个或多个'play'组成的,我们打个比方,一个'剧本'是由一个或多个'桥段'组成的,每个桥段都有不同的场景、人物、故事,所有的桥段组合在一起,组成一个完整的剧本,剧本就是playbook,桥段就是play,而上例中,整个剧本中只有一个桥段,也就是说,上例的playbook中,我们只写了一个play。
从上述信息可以看出,仅有的这个play是针对web_servers运行的,这个play一共包含三个任务,第一个任务的名字叫做'Gathering Facts',第二个任务的名字叫做'Ping the host',第三个任务的名字叫做'make directory test',看到此处你会发现,我们在playbook中明明只写了两个任务,为什么最后执行时却有三个任务呢?这是因为,每个play在执行时,都会先执行一个默认任务,这个默认任务就是'Gathering Facts','Gathering Facts'任务会收集当前play对应的目标主机的相关信息,收集完这些基础信息后,才会执行我们指定的任务,执行完默认任务后,开始执行'Ping the host'任务和'make directory test'任务,如果在执行playbook之前,/data/testfile目录已经存在于远程主机中,'make directory test'任务返回的信息是绿色的,如果对应的目录并不存在,'make directory test'任务返回的信息应该是黄色的,这是因为幂等性的缘故,前文已经解释过,此处不再赘述。
当playbook中的所有play执行完毕后,在返回信息的'PLAY RECAP'中可以对所有目标主机的执行情况进行概要描述。
PlayBook核心元素及常用参数
1、PlayBook核心元素包括
target:定义PlayBook的远程主机组
variables:定义PlayBook使用的变量
tasks:定义远程主机上执行的任务列表
handlers:处理器,当某条件满足时,触发执行的操作。例如修改配置文件之后,启动跟handlers任务重启相关联的服务
roles :角色,用于层次性、结构化地组织playbook。roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。
注:handlers和roles会在后面的实例中详细介绍
2、target常用参数详解
hosts:运行指定任务的目标主机,可以是一个主机组、主机、多个主机,中间以冒号分隔,也可以用all参数表示所有主机。
remote_user:指定远程主机上的执行任务的用户。
gather_facks:获取远程主机facts基础信息
注:remote_user参数以前称为user。它在ansibe1.4中被重命名,以使其与用户模块(用于在远程系统上创建用户)区别。
注:gather_facts: True 表示在远程主机运行setup模块,获取远程主机的facts,facts就是变量,内建变量 。每个主机的各种信息,cpu颗数、内存大小等,会存在facts中的某个变量中,调用后返回很多对应主机的信息。
3、variable常用参数详解
vars:在yaml文件中定义变量赋值
。
定义格式,变量名:变量值vars_files:指定变量文件,在文件中定义变量
4、tasks常用参数详解
name:任务显示名称也是屏幕显示信息
module_name: module_args: 需要使用的模块名字: 模块参数
关于task说明:
1、Play的主体部分是task列表,task列表中的各任务按次序逐个在hosts中指定的主机上执行,即在所有主机上完成第一个任务后再开始第二个任务。
2、在运行playbook时(从上到下执行),如果一个host执行task失败,整个tasks都会回滚,请修正playbook 中的错误,然后重新执行即可。
3、每一个task必须有一个名称name,这样在运行playbook时,从其输出的任务执行信息中可以很好的辨别出是属于哪一个task的。 如果没有定义name,‘action’的值将会用作输出信息中标记特定的task。
4、定义一个task,常见的格式:” module_name: module_args” 例如:yum: name=httpd state=absent update_cache=yes
5、ansible的自带模块中,command模块和shell模块无需使用key=value格式
5、handler: 常用参数详解
notify: “notify”这个action可用于在每个play的最后被触发,这样可以避免多次有改变发生时每次都执行指定的操作,取而代之,仅在所有的变化发生完成后一次性地执行指定操作。
handler: 也是task的列表。在notify中列出的操作称为handler,也即notify中调用handler中定义的操作。
1、ansible-playbook --check playbook.yaml #只检测可能会发生的改变,但不真执行操作
2、ansible-playbook --list-hosts playbook.yaml #列出运行任务的主机
3、ansible-playbook --syntax-check playbook.yaml #检查yaml文件的语法是否正确
4、ansible-playbook -t TAGS_NAME playbook.yaml #表示跳过标签前的步骤,直接从标签位置开始。-t选项执行指定的tag标签任务
注:通过tags参数给指定的任务定义一个调用标识。
格式:
- name: NAME
module: arguments
tags: TAG_ID
例如:
5、ansible-playbook playbook.yaml #运行