2.3 Ansible Playbook
Playbook 是 Ansible 进行配置管理的组件,虽然 Ansible 的日常 AD-Hoc 命令功能很强大,可以完成一些基本的配置管理工作,但是 AD-Hoc 命令无法支撑复杂环境的 配置管理工作。在我们实际使用 Ansible 的工作中,大部分时间都是在编写 Playbook 。
简单地说,playbook是一个非常简单的配置管理和多机部署系统的基础,不像任何已经存在的系统,它非常适合部署复杂的应用程序。
Playbook 基本语法
Ansible 的 Playbook 文件格式 YAML 语法,所以需要对 YAML 语法有一定的了解。
下面通过一个安装部署 Nginx 的案例来进行讲解
---
- 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
其中,notify 是触发 handlers,如果同步后,文件的 MD5 值有变化会触发 restart apache 这个 handler
handlers 是定义一个 handler 状态让 httpd 服务重启,handler 的名称是 restart apache 。
Playbook 也可以定义多个步骤,在一个 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
Playbook 主机和用户
对于 Playbook 中的每个步骤,您可以选择基础设施中的哪些机器作为目标,以及哪些远程用户作为完成步骤(称为任务)的对象。
hosts 这一行是一个或多个组或主机模式的列表,用冒号分隔,如使用模式文档中所述。remote_user只是用户帐户的名称:
---
- hosts: webservers
remote_user: root
远程用户也可以写在每个任务中
---
- hosts: webservers
remote_user: root
tasks:
- name: test connection
ping:
remote_user: username
也支持使用其他用户
---
- hosts: webservers
remote_user: username
become: yes
也可以使用关键字 become
在每个任务里来代替放在全局里。
---
- hosts: webservers
remote_user: yourname
tasks:
- service:
name: nginx
state: started
become: yes
become_method: sudo
也可以使用其他的提权模式
---
- hosts: webservers
remote_user: yourname
become: yes
become_method: su
Playbook 任务列表
每个 Playbook 都包含一个任务列表,在继续执行下一个任务之前,与主机模式匹配的所有机器将按顺序一次执行一个任务。重要的是要理解,在一个剧本中,所有主机都将获得相同的任务指令。
当从上到下运行 playbook 时,带有失败任务的主机将从整个 playbook 的轮转中删除。如果失败,只需纠正playbook文件并重新运行。
每个任务的目标都是执行一个具有非常特定参数的模块。如上所述,变量可以用于模块的参数中。
模块应该是幂等的,也就是说,在一个序列中运行一个模块多次应该与只运行一次具有相同的效果。实现等幂性的一种方法是让模块检查其所需的最终状态是否已经实现,如果已经实现,则退出而不执行任何操作。如果 playbook 使用的所有模块都是幂等的,那么 playbook 本身很可能也是幂等的,因此重新运行 playbook 应该是安全的。
每个任务都应该有一个 name
,它包含在运行 playbook 的输出中。这是一个人类可读的输出,因此提供每个任务步骤的良好描述非常有用。但是,如果没有提供名称,则将使用提供给action
的字符串作为输出。
任务可能使用一些旧的格式,action: module options
,但是建议使用格式 module: options
。这种推荐格式在整个文档中都使用,但是您可能会在一些 Playbook 中遇到更旧的格式。
一个基本的格式就像这样,和大多数模块一样,service
模块也是 key=value
参数
tasks:
- name: make sure apache is running
service:
name: httpd
state: started
command
和 shell
两个模块会取一些参数列表,而不是使用 key=value
参数,简单使用如下:
tasks:
- name: enable selinux
command: /sbin/setenforce 1
command
和 shell
两个模块会关心返回码,所有如果你有一个命令执行成功的返回码不为零,那么你可以这样做。
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true
或者这样
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand
ignore_errors: True
如果 action 的行太长了,你可以用空格来打断他,并且在任何有缩进的地方继续
tasks:
- name: Copy ansible inventory file to client
copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts
owner=root group=root mode=0644
变量也可以被使用在 action 行,例如在下面的例子里定义了 vhost
这个变量
tasks:
- name: create a virtual host file for {{ vhost }}
template:
src: somefile.j2
dest: /etc/httpd/conf.d/{{ vhost }}
Action
Ansible 更喜欢这样的格式
template:
src: templates/foo.j2
dest: /etc/foo.conf
早期的版本是这样,现在也还可以工作,但是更推荐上一种。
action: template src=templates/foo.j2 dest=/etc/foo.conf
Handlers
正如我们所提到的,模块应该是幂等的,并且可以在它们对远程系统进行更改时进行中继。Playbook 认识到这一点,并有一个基本的事件系统,可以用来响应变化。
这些 notify
动作在一个 playbook 中每个任务块的末尾触发,即使由多个不同的任务通知,也只会触发一次。
例如,多个资源可能表明apache需要重新启动,因为它们更改了配置文件,但是 apache 只会被触发一次,以避免不必要的重新启动
这里有例子
- name: template configuration file
template:
src: template.j2
dest: /etc/foo.conf
notify:
- restart memcached
- restart apache
上边例子里 notify
列出的任务被称为 handlers
Handlers 其实是一个任务列表,实际上与常规任务没有什么不同,它们由全局惟一名称引用,并由 notify 程序通知。如果没有通知处理程序,它将不会运行。不管有多少任务通知一个处理程序,它都只运行一次,即在一个特定的 play 中完成所有任务之后
这里有一个 handlers 的示例
handlers:
- name: restart memcached
service:
name: memcached
state: restarted
- name: restart apache
service:
name: apache
state: restarted
在 Ansible 2.2 之后,handlers 可以使用 listen
参数被监听,
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"
这种用法使触发多个处理程序变得容易得多。它还将处理程序与其名称解耦,使处理程序更容易在 Playbook 和 Role 之间共享 (特别是在使用来自共享源(如Galaxy)的第三方角色时)。
在下一节的时候会介绍 Role ,现在有如下内容需要注意:
- 在pre_tasks、tasks和post_tasks部分中通知的处理程序将在通知它们的部分的末尾自动刷新;
- 角色部分中通知的处理程序将在任务部分的末尾自动刷新,但在任何任务处理程序之前。
执行一个 Playbook
现在已经学会了 Playbook 的语法,那么如何执行一个 Playbook,非常简单,使用下列命令就可以并行的执行。
$ ansible-playbook playbook.yml -f 10