playbook
文章目录
ansible playbook简介
playbook 是 ansible 用于配置,部署,和管理被控节点的剧本
通过 playbook 的详细描述,执行其中的一系列 tasks(任务) ,可以让远端主机达到预期的状态。
playbook 字面意思,即剧本,现实中由演员按照剧本表演,在Ansible中,这次由计算机进行表演,由计算机安装,部署应用,提供对外服务,以及组织计算机处理各种各样的事情。
playbook基础组件
- Hosts:运行执行任务(task)的目标主机
- remote_user:在远程主机上执行任务的用户
- tasks:任务列表
- handlers:任务,与tasks不同的是只有在接受到通知时才会被触发
- templates:使用模板语言的文本文件,使用jinja2语法。
- variables:变量,变量替换{{ variable_name }}
整个playbook是以task为中心,表明要执行的任务。hosts和remote_user表明在哪些远程主机以何种身份执行。其他组件让其能够更加灵活。
Ansible playbook与临时命令
临时命令可以作为一次性命令对一组目标主机运行一项简单的任务。不过,若要真正发挥Ansible的力量,需要了解如何使用playbook以便轻松重复的方式对一组目标主机执行多项复杂的任务。
play是针对清单中选定的主机运行的一组有序任务。playbook是一个文本文件,其中包含由一个或多个按特定顺序运行的play组成的列表。
Play可以将一系列冗长而复杂的手动管理任务转变为可轻松重复的例程,并且具有可预测的成功成果。在playbook中,可以将play内的任务序列保存为人类可读并可立即运行的形式。根据任务的编写方式,任务本身记录了部署应用或基础架构所需的步骤。
临时命令写法
ansible 192.168.220.7 -m user -a 'name=tom uid=1007 state=present'
playbook中的写法
--- # 标记文件开头
- name: test # 项目的名字
hosts: 192.168.220.8 # 执行任务的主机IP/组
tasks: # 任务列表
- name: create a user and uid # 任务描述
user: # 使用user模块
name: tom # 创建用户的名字
uid: 1007 # 指定用户的uid
state: present # 状态为存在
作为play中的一部分,tasks属性按顺序实际列出要在受管主机上运行的任务。列表中各项任务本身是一个键值对集合。
上面创建用户的play为例,play中唯一任务有两个键:
- name是记录任务用途的可选标签。最好命名所有的任务,从而帮助记录自动流程中的每一步用途。
- user是要为这个任务运行的模块。其参数作为一组键值对传递,它们是模块的子项(name、uid和state)。
Playbook是以YAML格式编写的文本文件,通常使用扩展名yml保存。Playbook使用空格字符缩进来表示其数据结构。YAML对用于缩进的空格数量没有严格的要求,但有两个基本的规则:
- 处于层次结构中同一级别的数据元素(例如同一列表中的项目)必须具有相同的缩进量。
- 如果项目属于其他项目的子项,其缩进量必须大于父项
只有空格字符可用于缩进,不允许使用tab键。约定俗成的缩进量一般是一级2个空格。
Playbook开头的一行由三个破折号(—)组成,这是文档开始标记。其末尾可能使用三个圆点(…)作为文档结束标记,尽管在实践中这通常会省略。
含有多个任务的tasks属性示例:
tasks:
- name: create a user and uid
user:
name: tom
uid: 1007
state: present
- name: create a group
group:
name: jerry
gid: 1456
state: present
- name: create user
user:
name: kerry
uid: 1011
state: present
playbook中play和任务列出的顺序很重要,因为Ansible会按照相同的顺序运行它们。
运行playbook
absible-playbook命令可用于运行playbook,创建一个文件以 .yml 结尾。该命令在控制节点上执行,要运行的playbook的名称则作为参数传递。
ansible-palybook test.yml
运行playbook时,将生成输出来显示所执行的play和任务。输出中也会报告执行的每一项任务的结果。
以下示例中显示了一个简单的playbook的内容,后面是运行它的结果
[root@localhost ansible]# vi test.yml
[root@localhost ansible]# cat test.yml # 查看playbook内容
---
- name: test
hosts: 192.168.220.8
tasks:
- name: create a user and uid
user:
name: tom
uid: 1007
state: present
- name: create a group
group:
name: jerry
gid: 1456
state: present
- name: create user
user:
name: kerry
uid: 1011
state: present
[root@localhost ansible]# ansible-playbook test.yml # 运行playbook
PLAY [test] *************************************************************************************
TASK [Gathering Facts] **************************************************************************
ok: [192.168.220.8]
TASK [create a user and uid] ********************************************************************
ok: [192.168.220.8]
TASK [create a group] ***************************************************************************
ok: [192.168.220.8]
TASK [create user] ******************************************************************************
changed: [192.168.220.8]
PLAY RECAP **************************************************************************************
192.168.220.8 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
请注意,在playbook运行时,屏幕中会显示每个play和任务的name键的值。(Gathering Facts任务是一项特别的任务,setup模块通常在play启动时自动运行这项任务。)对于含有多个play和任务的playbook,设置name属性后可以更加轻松地监控playbook执行的进展。
通常而言,Ansible Playbook中的任务是幂等的,而且能够安全地多次运行playbook。如果目标受管主机已处于正确的状态,则不应进行任何更改。如果再次运行这个playbook,所有任务都会以状态OK传递,且不报告任何更改。
提高输出的详细程度
ansible-playbook命令提供的默认输出不提供详细的任务执行信息。ansible-playbook -v命令提供了额外的信息,总共有四个级别。
配置Playbook执行的输出详细程序
选项 | 描述 |
---|---|
-v | 显示任务结果 |
-vv | 任务结果和任务配置都会显示 |
-vvv | 包含关于与受管主机连接的信息 |
-vvvv | 增加了连接插件相关的额外详细程序选项,包括受管主机上用于执行脚本的用户以及所执行的脚本 |
验证语法
在执行playbook之前,最好要进行验证,确保其内容的语法正确无误。ansible-playbook命令提供了一个–syntax-check选项,可用于验证playbook的语法。
演示一个playbook成功通过语法验证:
[root@localhost ansible]# ansible-playbook --syntax-check test.yml
playbook: test.yml
语法验证失败时,将报告语法错误。输出中包含语法问题在playbook中的大致位置:
[root@localhost ansible]# ansible-playbook --syntax-check test.yml
ERROR! We were unable to read either as JSON nor YAML, these are the errors we got from each:
JSON: Expecting value: line 1 column 1 (char 0)
Syntax Error while loading YAML.
mapping values are not allowed in this context
The error appears to be in '/etc/ansible/test.yml': line 3, column 8, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
-name: test
hosts: 192.168.220.8
^ here
执行空运行
可以使用**-C选项对playbook执行空运行。这会使Ansible报告在执行该playbook时将会发生什么更改,但不会对受管主机进行任何实际的更改。**
演示一个playbook的空运行,它包含单项任务,注意该空运行报告此任务会对受管主机产生的更改。
[root@localhost ansible]# ansible-playbook -C test.yml
PLAY [test] *************************************************************************************
TASK [Gathering Facts] **************************************************************************
ok: [192.168.220.8]
TASK [create a user and uid] ********************************************************************
changed: [192.168.220.8]
TASK [create a group] ***************************************************************************
changed: [192.168.220.8]
TASK [create user] ******************************************************************************
changed: [192.168.220.8]
PLAY RECAP **************************************************************************************
192.168.220.8 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
执行多个play
缩写多个play
Playbook是一个YAML文件,含有由一个或多个play组成的列表。记住一个play按顺序列出了要对清单中的选定主机执行的任务。因此,如果一个playbook中有多个play,每个play可以将其任务应用到单独的一组主机。
在编排可能涉及对不同主机执行不同任务的复杂部署时,这会大有帮助。我们可以这样进行编写:对一组主机运行一个play,完成后再对另一组主机运行另一个play。
缩写包含多个play的playbook非常简单。Playbook中的各个play编写为playbook中的顶级列表项。各个play是含有常用play关键字的列表项。
示例显示了含有两个play的简单playbook。第一个play针对192.168.220.8运行,第二个play则针对192.168.220.9运行。
# Run two taks
---
- name: First paly
hosts: 192.168.220.8
tasks:
- name: delete user
user:
name: tom
state: absent
- name: Second play
hosts: 192.168.220.9
tasks:
- name: create user
user:
name: tom
uid: 1234
state: present
play中的远程用户和特权升级
Play可以将不同的远程用户或特权升级设置用于play,取代配置文件中指定的默认设置。这些在play本身中与hosts或tasks关键字相同的级别上设置。
用户属性
playbook中的任务通常通过与受管主机的网络连接来执行。与临时命令相同,用于任务执行的用户帐户取决于Ansible配置文件/etc/ansible/ansible.cfg中的不同关键字。运行任务的用户可以通过remote_user关键字来定义。不过,如果启用了特权升级,become_user等其他关键字也会发生作用。
如果用于任务执行的Ansible配置中定义的远程用户不合适,可以通过在play中使用remote_user关键字覆盖。
remote_user: 远程用户
特权升级属性
Ansible也提供额外的关键字,从而在playbook内定义特权升级参数。become布尔值关键字可用于启用或禁用特权升级,无论它在Ansible配置文件中的定义为何。它可取yes或true值来启用特权升级,或者取no或false值来禁用它。
become:yes
如果启用了特权升级,则可以使用become_method关键字来定义特定play期间要使用的特权升级方法。
指定sudo用于特权升级:
become_method: sudo
此外,启用了特权升级时,become_user关键字可定义特定play上下文内要用于特权升级的用户帐户。
become_user: privileged_user
示例:
---
- name: Create users with normal users # 使用普通用户创建用户(要开启特权升级)
hosts: 192.168.220.8
remote_user: tom # 远程用户
become: yes # 特权升级开启
tasks:
- name: create user
user:
name: lerry
uid: 4915
state: present
查找模块
Ansible随附打包的大量模块为管理员提供了许多用于常见管理任务的工具。通过模块索引,可以很轻松的找到对应的模块。
对于每一个模块,Ansible官网提供了其功能摘要,以及关于如何通过模块的选项来调用各项具体功能的说明。文档还提供了实用的示例,演示各个模块的用法,以及任务中关键字的设置方法。
前面我们用到过ansible-doc -l命令。这将显示模块名称列表以及其功能的概要。
anisble-doc -l //列出当前ansible所支持的所有模块
使用 ansible-doc [module name] 命令来显示模块的详细文档。与Ansible官网一样,该命令提供模块功能的概要、其不同选项的详细信息,以及示例。
ansible-doc service //显示service模块的帮助文档
使用 ansible-doc 命令可以查找和了解如何使用模块。尽管 command 、 shell 和 raw 模块的用法可能看似简单,但在可能时,应尽量避免在playbook中使用它们因为它们可以取胜任意命令,因此使用这些模块时很容易写出非幂等的playbook。当使用shell模块运行任务时,是非幂等的,每次运行时,都会重写文件,即使里面已经有需要的内容。
[root@localhost ansible]# cat teach.yml //查看playbook中的内容,使用shell模块
---
- name: test
hosts: 192.168.220.8
tasks:
- name: Add SELinux = disabled
shell: echo "SELINUX=disabled" >> /etc/selinux/config
[root@localhost ansible]# ansible-playbook teach.yml //执行playbook
PLAY [test] *************************************************************************************
TASK [Gathering Facts] **************************************************************************
ok: [192.168.220.8]
TASK [Add SELinux = disabled] *******************************************************************
changed: [192.168.220.8]
PLAY RECAP **************************************************************************************
192.168.220.8 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescu
ed=0 ignored=0
[root@localhost ansible]# ansible 192.168.220.8 -m shell -a 'cat /etc/selinux/config' //查看结尾增加了
192.168.220.8 | CHANGED | rc=0 >>
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
SELINUX=disabled
# SELINUXTYPE= can take one of these three values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targeted
SELINUX=disabled
[root@localhost ansible]# ansible-playbook teach.yml //再执行一次playbook
PLAY [test] *************************************************************************************
TASK [Gathering Facts] **************************************************************************
ok: [192.168.220.8]
TASK [Add SELinux = disabled] *******************************************************************
changed: [192.168.220.8]
PLAY RECAP **************************************************************************************
192.168.220.8 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescu
ed=0 ignored=0
[root@localhost ansible]# ansible 192.168.220.8 -m shell -a 'cat /etc/selinux/config' //又增加了,无幂等性
192.168.220.8 | CHANGED | rc=0 >>
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
SELINUX=disabled
# SELINUXTYPE= can take one of these three values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targeted
SELINUX=disabled
SELINUX=disabled
使用lineinfile模块就不会出现这种情况,lineinfile模块会测试来了解是否达到了需要的状态,如果达到了,就不会进行修改。shell模块容许非常大的灵活性,但需要格外小心,从而确保它以幂等方式运行。
幂等的playbook可以重复运行,确保系统处于特定的状态,而不会破坏状态已经正确的系统。
[root@localhost ansible]# cat teach.yml
---
- name: test
hosts: 192.168.220.8
tasks:
- name: Add SELinux = disabled
lineinfile:
path: /etc/selinux/config
line: SELinux = disabled
YAML注释
注释可以用于提高可读性。在YAML中,井号(#)右侧的所有内容都是注释。如果注释的左侧有内容,请在该编号符号的前面加一个空格。
---
- name: test
hosts: 192.168.220.8 # This is a line of comments(这是一行注释)
tasks:
- name: Add SELinux = disabled
lineinfile:
path: /etc/selinux/config
line: SELinux = disabled
YAML字符串
YAML中的字符串通常不需要放在引号里,即使字符串中包含空格。字符串可以用双引号或单引号括起。
This is a string
"This is a string"
'This is a string'
编写多行字符串有两种方式。可以使用管道符表示要保留字符串中的换行字符
---
- name: test
hosts: 192.168.220.8
tasks:
- name: Add SELinux = disabled
lineinfile:
path: /etc/selinux/config
line: |
SELinux = permissive
SELinux = disabled
SELinux = enabled
[root@localhost ansible]# ansible 192.168.220.8 -m shell -a 'cat /etc/selinux/config' //查看效果
.....
# SELINUXTYPE= can take one of these three values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targeted
SELinux = permissive # 效果
SELinux = disabled
SELinux = enabled
要编写多行字符串,还可以使用大于号字符来表示换行字符转换成空格并且行内的引导空白将被删除。这种方法通常用于将很长的字符串在空格字符处断行,使它们跨占多行来提高可读性。
---
- name: test
hosts: 192.168.220.8
tasks:
- name: Add SELinux = disabled
lineinfile:
path: /etc/selinux/config
line: >
SELinux = permissive
SELinux = disabled
SELinux = enabled
[root@localhost ansible]# ansible 192.168.220.8 -m shell -a 'cat /etc/selinux/config' //查看效果
.....
# SELINUXTYPE= can take one of these three values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targeted
SELinux = permissive SELinux = disabled SELinux = enabled # 效果
过时的“键=值” playbook简写
某些playbook可能使用较旧的简写方法,通过将模块的键值对放在与模块名称相同的行上来定义任务。例如,你可能会看到这种语法:
tasks:
- name: shorthand form
service: name=httpd enabled=yes state=started
通常我们应该将这样的语法编写为如下所示:
tasks:
- name: normal form
service:
name: httpd
enabled: yes
state: started
通常我们应避免简写形式,而使用普通形式。
普通形式的行数较多,但更容易操作。任务的关键字垂直堆叠,更容易区分。阅读play时,眼睛直接向一扫视,左右运动较少。而且,普通语法是原生的YAML。
你可能会在文档和他人提供的旧playbook中看到这种语法,而且这种语法仍然可以发挥作用。