Playbook 介绍
Playbooks 与 ad-hoc 相比,是一种完全不同的运用 ansible 的方式,是非常之强大的。简单来说,playbooks 是一种简单的配置管理系统与多机器部署系统的基础。与现有的其他系统有不同之处,且非常适合于复杂应用的部署。
Playbooks 可用于声明配置,更强大的地方在于: 在 playbooks 中可以编排有序的执行过程,甚至于做到在多组机器间来回有序的执行特别指定的步骤,并且可以同步或异步的发起任务。
Playbook 语言的示例
Playbooks 的格式是 YAML,语法做到最小化,意在避免 playbooks 成为一种编程语言或是脚本,但它也并不是一个配置模型或过程的模型。
playbook 由一个或多个 plays
组成,它的内容是一个以 plays
为元素的列表。在 play 之中,一组机器被映射为定义好的角色。在 ansible 中,play 的内容被称为 tasks,即任务。在基本层次的应用中,一个任务是一个对 ansible 模块的调用。
这里有一个 playbook,其中仅包含一个 play:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- hosts: webservers
vars:
http_port: 80
max_clients: 200
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum: pkg=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
Playbook 基础
主机与用户
可以为 playbook 中的每一个 play,个别地选择操作的目标机器是哪些,以哪个用户身份去完成要执行的步骤(called tasks)。
hosts 行的内容是一个或多个组或主机的 patterns,以逗号为分隔符。remote_user 就是账户名:
1
2
3
- hosts: webservers
remote_user: root在每一个 task 中,可以定义自己的远程用户:
1
2
3
4
5
6
7
- hosts: webservers
remote_user: root
tasks:
- name: test connection
ping:
remote_user: yourname支持从 sudo 执行命令:
1
2
3
4
- hosts: webservers
remote_user: yourname
sudo: yes同样的,可以仅在一个 task 中使用 sudo 执行命令,而不是在整个 play 中使用 sudo:
1
2
3
4
5
6
- hosts: webservers
remote_user: yourname
tasks:
- service: name=nginx state=started
sudo: yes也可以登陆后,sudo 到不同的用户身份,而不是使用 root:
1
2
3
4
5
- hosts: webservers
remote_user: yourname
sudo: yes
sudo_user: postgres如果你需要在使用 sudo 时指定密码,可在运行 ansible-playbook 命令时加上选项
--ask-sudo-pass (-K)
。如果使用 sudo 时,playbook 疑似被挂起,可能是在 sudo prompt 处被卡住,这时可执行 Control-C 杀死卡住的任务,再重新运行一次。
Tasks 列表
每一个 play 包含了一个 task 列表(任务列表)。一个 task 在其所对应的所有主机上(通过 host pattern 匹配的所有主机)执行完毕之后,下一个 task 才会执行。有一点需要明白的是(很重要),在一个 play 之中,所有 hosts 会获取相同的任务指令,这是 play 的一个目的所在,也就是将一组选出的 hosts 映射到 task。
在运行 playbook 时(从上到下执行),如果一个 host 执行 task 失败,这个 host 将会从整个 playbook 的 rotation 中移除。如果发生执行失败的情况,请修正 playbook 中的错误,然后重新执行即可。
每个 task 的目标在于执行一个 moudle,通常是带有特定的参数来执行。在参数中可以使用变量(variables)。
modules 具有 ”幂等” 性,意思是如果你再一次地执行 moudle(译者注:比如遇到远端系统被意外改动,需要恢复原状),moudle 只会执行必要的改动,只会改变需要改变的地方。所以重复多次执行 playbook 也很安全。
对于 command
module 和 shell
module,重复执行 playbook,实际上是重复运行同样的命令。如果执行的命令类似于 chmod
或者 setsebool
这种命令,这没有任何问题。也可以使用一个叫做 creates
的 flag 使得这两个 module 变得具有 “幂等” 特性(不是必要的)。
每一个 task 必须有一个名称 name,这样在运行 playbook 时,从其输出的任务执行信息中可以很好的辨别出是属于哪一个 task 的。如果没有定义 name,action
的值将会用作输出信息中标记特定的 task。
如果要声明一个 task,以前有一种格式: action: module options
(可能在一些老的 playbooks 中还能见到)。现在推荐使用更常见的格式:module: options
,本文档使用的就是这种格式。
下面是一种基本的 task 的定义,service moudle 使用
key=value
格式的参数,这也是大多数 module 使用的参数格式:1
2
3tasks:
- name: make sure apache is running
service: name=httpd state=running比较特别的两个 modudle 是
command
和shell
,它们不使用key=value
格式的参数,而是这样:1
2
3tasks:
- name: disable selinux
command: /sbin/setenforce 0使用
command
module 和shell
module 时,我们需要关心返回码信息,如果有一条命令,它的成功执行的返回码不是0,你或许希望这样做:1
2
3tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true或者这样
1
2
3
4tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand
ignore_errors: True如果
action
行看起来太长,可以使用 space(空格)或者 indent(缩进)隔开连续的一行:1
2
3
4tasks:
- name: Copy ansible inventory file to client
copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts
owner=root group=root mode=0644在 action 行中可以使用变量。假设在 ‘vars’ 那里定义了一个变量 ‘vhost’,可以这样使用它:
1
2
3tasks:
- name: create a virtual host file for {{ vhost }}
template: src=somefile.j2 dest=/etc/httpd/conf.d/{{ vhost }}这些变量在 tempates 中也是可用的,稍后会讲到.
在一个基础的 playbook 中,所有的 task 都是在一个 play 中列出,稍后将介绍一种更合理的安排 task 的方式: 使用 include:
指令。
Action Shorthand
在 0.8 及以后的版本中,ansible 更喜欢使用如下的格式列出 modules:
1
template: src=templates/foo.j2 dest=/etc/foo.conf
在早期的版本中,使用以下的格式:
1
action: template src=templates/foo.j2 dest=/etc/foo.conf
早期的格式在新版本中仍然可用,并且没有计划将这种旧的格式弃用。
Handlers
上面我们曾提到,module 具有 ”幂等” 性,所以当远端系统被人改动时,可以重放 playbooks 达到恢复的目的。playbooks 本身可以识别这种改动,并且有一个基本的 event system(事件系统),可以响应这种改动。
(当发生改动时)notify
actions 会在 playbook 的每一个 task 结束时被触发,而且即使有多个不同的 task 通知改动的发生,notify
actions 只会被触发一次。举例来说,比如多个 resources 指出因为一个配置文件被改动,所以 apache 需要重新启动,但是重新启动的操作只会被执行一次。
这里有一个例子,当一个文件的内容被改动时,重启两个 services:
1
2
3
4
5- name: template configuration file
template: src=template.j2 dest=/etc/foo.conf
notify:
- restart memcached
- restart apachenotify
下列出的即是 handlers.
Handlers 也是一些 task 的列表,通过名字来引用,它们和一般的 task 并没有什么区别。Handlers 是由通知者进行 notify
,如果没有被 notify
,handlers 不会执行。不管有多少个通知者进行了 notify
,等到 play 中的所有 task 执行完成之后,handlers 也只会被执行一次。
这里是一个 handlers 的示例:
1
2
3
4
5handlers:
- name: restart memcached
service: name=memcached state=restarted
- name: restart apache
service: name=apache state=restarted
Handlers 最佳的应用场景是用来重启服务,或者触发系统重启操作。除此以外很少用到了。
值得指出的是,handlers 会在 ‘pre_tasks’, ‘roles’(Roles 将在后面讲述), ‘tasks’, 和 ‘post_tasks’ 之间自动执行。
如果你想立即执行所有的 handler 命令,在1.2及以后的版本,你可以这样做:
1
2
3
4tasks:
- shell: some tasks go here
- meta: flush_handlers
- shell: some other tasks在以上的例子中,任何在排队等候的 handlers 会在执行到 ‘meta’ 部分时,优先执行。这个技巧在有些时候也能派上用场。
执行一个 Playbook
既然现在已经学习了 playbook 的语法,那要如何运行一个 playbook 呢?这很简单,这里的示例是并行的运行 playbook,并行的级别是10(译者注:是10个并发的进程?):
1
ansible-playbook playbook.yml -f 10
Ansible-Pull(拉取配置而非推送配置)
我们可不可以将 ansible 的体系架构颠倒过来,让托管节点从一个 central location 做 check in 获取配置信息,而不是推送配置信息到所有的托管节点?是可以的.Ansible-pull
是一个小脚本,它从 git 上 checkout 一个关于配置指令的 repo,然后以这个配置指令来运行 ansible-playbook.
假设你对你的 checkout location 做负载均衡,ansible-pull 基本上可以无限的提升规模。
可执行 ansible-pull --help
获取详细的帮助信息.
也有一个叫做 clever playbook 的东西: clever playbook 。这个可以通过 crontab 来配置 ansible-pull(from push mode).
提示与技巧
在 playbook 执行输出信息的底部,可以找到关于托管节点的信息。也可看到一般的失败信息,和严重的 “unreachable” 信息. 这两个是分开计数的。
如果你想看到执行成功的 modules 的输出信息,使用 --verbose
flag(否则只有执行失败的才会有输出信息).这在 0.5 及以后的版本中可用。
- 在执行一个 playbook 之前,想看看这个 playbook 的执行会影响到哪些 hosts,你可以这样做:
1 | ansible-playbook playbook.yml --list-hosts |