后续此博客不再更新,欢迎大家搜索关注微信公众号“测开之美”,测试开发工程师技术修炼小站,持续学习持续进步。
学习playbook前,推荐先阅读:学习Ansible[5]:YAML文件格式浅析。
Playbook是Ansible的配置、部署、编排语言,可以描述远端机器的执行策略,或一般IT流程中的一组步骤。如果Ansible的模块是车间里的工具,playbook就是操作手册,资源清单中的主机就是操作的原材料。
playbook的基本功能是管理远端机器的配置、部署,更高级点可以对涉及滚动更新的多层发布任务进行排序,将操作委派给其他主机,同时与监视服务器和负载平衡器进行交互。
Github上的ansible/ansible-examples示例是Ansible提供的官方示例,推荐阅读。
一个playbook,是一个或多个play的列表。一个play,建立起一组机器和一些任务的映射。一个任务,是对Ansible模块的一次调用。组合多个play成为一个playbook,可以实现复杂的功能,比如在webservers
组上执行某些任务,再在dbservers
组上执行另一些任务,最后在webservers
组上执行剩下的任务,等等。一个简单的包含一个play的playbook示例:
---
- hosts: webservers
vars:
http_port: 80
max_clients: 200
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum:
name: httpd
state: latest
- name: write the apache config file
template:
src: /srv/httpd.j2
dest: /etc/httpd.conf
notify:
- restart apache
- name: ensure apache is running
service:
name: httpd
state: started
handlers:
- name: restart apache
service:
name: httpd
state: restarted
包含多个play的playbook例子:
---
- hosts: webservers
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum:
name: httpd
state: latest
- name: write the apache config file
template:
src: /srv/httpd.j2
dest: /etc/httpd.conf
- hosts: databases
remote_user: root
tasks:
- name: ensure postgresql is at the latest version
yum:
name: postgresql
state: latest
- name: ensure that postgresql is started
service:
name: postgresql
state: started
play和任务的执行顺序,与在playbook中的定义顺序一样,都是从上到下,所以上面的例子可以实现在不同的机器组间切换不同的任务。下面深入介绍playbook的基础。
每个play,都要明确针对哪些机器(hosts
)、以何种身份(remote_user
)完成指定的任务。
hosts
的值,是一个或多个机器组或机器pattern的列表,用冒号分割。remote_user
是登录远端机器的用户名。一个play级的主机配置片段举例:
# YAML snippet
---
- hosts: webservers
remote_user: root
remote_user
可以在每个任务中单独定义:
# YAML snippet
---
- hosts: webservers
remote_user: root
tasks:
- name: test connection
ping:
remote_user: mars
play级的提升权限:
# YAML snippet
---
- hosts: webservers
remote_user: mars
become: yes
任务级的提升权限(可以同时设定提升权限的方法,比如su
):
# YAML snippet
---
- hosts: webservers
remote_user: mars
tasks:
- service:
name: nginx
state: started
become: yes
become_method: sudo
play级的切换到其他用户:
# YAMl snippet
---
- hosts: webservers
remote_users: mars
become: yes
become_user: loo
如果切换用户或sudo
需要密码,执行ansible-playbook
命令时要加--ask-become-pass
参数。运行有用户切换场景的playbook,ansible-book
程序hang住很有可能是切换用户或sudo
时鉴权出现问题,直接Ctrol+C
杀死ansible-playbook
,重新提供--ask-become-pass
参数即可。
play级的order
参数控制任务在所有机器的执行顺序:
# YAMl snippet
---
- hosts: all
order: sorted
gather_facts: False
tasks:
- debug:
var: inventory_hostname
order
参数取值范围:
hosts
参数列表中的顺序执行。hosts
参数列表中的反序执行。hosts
参数中机器名字典序执行。hosts
参数中机器名字反字典序执行。hosts
参数中机器随机序执行。playbook执行过程中,如果一个机器运行任务失败,这个机器将不再参与后续的任务执行。
模块应该是幂等的,要实现幂等,可以检查模块的最终状态是否符合预期。如果playbook使用的所有模块都是幂等的,整个playbook就是幂等的,多次运行playbook就是安全的。
一个简单的任务定义的示例片段:
# YAML snippet
tasks:
- name: make sure apache is running
service:
name: httpd
state: started
name
参数描述这个任务的功能,执行playbook时会输出到日志中。
大多数模块(比如上例中的service
模块)接受key=value
格式的参数,对于command
、shell
这种直接接收参数列表的模块,可以按照如下格式定义:
# YAML snippet
tasks:
- name: enable selinux
command: /sbin/setenforce 1
command
和shell
模块关注命令的返回值,如果命令成功执行时的返回值不是0,用如下方法规避:
# YAML snippet
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true
或者:
# YAML snippet
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand
ignore_errors: True
如果参数过长,可以在任意空格处换行,在下一行缩进继续定义参数:
# YAML snippet
tasks:
- name: Copy ansible inventory file to client
copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts
owner=mars group=mars mode=0644
任务的定义可以使用play级定义的变量,比如假设在play的vars
部分提前定义了一个变量vhost
:
# YAML snippet
tasks:
- name: use variable {{ vhost }}
template:
src: somefile.j2
dest: /etc/conf/{{ vhost }}
旧版本的Ansible定义任务使用action: module arguments
的形式,现在已经不推荐使用这种形式,例如:
# YAML snippet
action: template src=somefile.j2 dest=/etc/conf/somefile.j2
推荐使用上面的module: arguments
形式:
# YAML snippet
- name: copy files
template:
src: somefile.j2
dest: /etc/conf/somefile.j2
playbook的通知机制(notify),可以在一个play结束时执行指定的任务(即使被多个不同的任务多次通知)。例如,多个任务都修改了Apache的配置并通知Apache重启,Apache仅在这个play结束时重启一次,例如:
# YAML snippet
tasks:
- name: template configuration file
template:
src: template.j2
dest: /etc/apache.conf
notify:
- restart apache
handlers:
- name: restart apache
service:
name: apache
state: restarted
notify
中定义的任务列表是处理器(handler)的列表。处理器也是任务,有全局唯一的名字,被通知触发执行,如果没有被通知则不会执行。
除了通过处理器名字触发处理器,处理器可以监听topic消息,被topic消息触发:
# YAML snippet
handlers:
- name: restart memcached
service:
name: memcached
state: restarted
listen: "restart web services"
- name: restart apache
service:
name: apache
state: restarted
listen: "restart web services"
tasks:
- name: restart everything
command: echo "this task will restart the web services"
notify: "restart web services"
细心的读者会发现notify
的值也可以是标量。使用topic消息的方式将处理器与名字本身解耦,在剧本、角色间共享处理器场景下非常方便。
- 多个处理器一起触发时,按照定义的顺序执行,而不是触发顺序。
- 处理器名字和topic消息都存在于全局命名空间。
- 如果两个处理器名字相同,只有一个处理器会被触发。
- 不能触发一个定义在include中的触发器。
如果执行某个任务前,需要先执行排队中的处理器任务,可以用如下方式清空并执行排队中的处理器任务:
# YAML snippet
tasks:
- shell: some tasks
- meta: flush_handlers
- shell: some other tasks
编写完playbook的YAML文件后,运行playbook非常简单,假设1.yml
内容为:
---
- hosts: example
remote_user: mars
gather_facts: false
tasks:
- name: list files
shell: pwd && ls -al
以10并发运行playbook:
$ ansible-playbook 1.yml -f 10
PLAY [example] *****************************************************************
TASK [list files] **************************************************************
changed: [a.example.com]
changed: [b.example.com]
PLAY RECAP *********************************************************************
a.example.com : ok=1 changed=1 unreachable=0 failed=0
b.example.com : ok=1 changed=1 unreachable=0 failed=0
检查playbook的语法:ansible-playbook --syntax-check 1.yml
。
查看模块执行过程中的输出:ansible-playbook 1.yml --verbose
。
查看一个playbook会影响哪些机器:
$ ansible-playbook 1.yml --list-hosts
playbook: 1.yml
play #1 (example): example TAGS: []
pattern: [u'example']
hosts (2):
a.example.com
b.example.com