一、初识playbook
前文中,我们介绍了一些ansible的常用模块,聪明如你,一定已经掌握了这些模块的使用方法。
那么现在,我们来想象一个工作场景:
假设,我们想要在所有主机上安装nginx并启动,我们可以在ansible主机中执行如下3条命令
ansible all -m yum_repository -a 'name=aliEpel description="alibaba EPEL" baseurl=https://mirrors.aliyun.com/epel/$releasever\Server/$basearch/'
ansible all -m yum -a 'name=nginx disable_gpg_check=yes enablerepo=aliEpel'
ansible all -m service -a "name=nginx state=started"
我们通过上述3条命令,先确定配置了对应的yum源,然后使用yum模块安装了nginx,最后使用service模块启动了nginx,最终达到了我们的目的。
但是实际工作中,每次都要执行三次太麻烦了。如果命令越多,后期每次执行命令就越多条。我们可以把这么多条命令整合成一个脚本。直接执行这个脚本就行。这个脚本就是playbook.虽然playbook的功能与脚本类似,但是剧本并不是简单的将ad-hoc命令按照顺序堆砌在一个可执行文件中,编写剧本需要遵循YAML语法.
先看一个示例:
这是两条命令,使用ping模块去ping主机,然后再用file模块在主机上创建目录
ansible all -m ping
ansible all -m file -a "path=/testdir/test state=directory"
转换成playbook为:
[root@ansible-master ansible]# cat ceshi.yml
---
- hosts: slave
remote_user: root
tasks:
- name: ping slave hosts
ping:
- name: mkdir directory
file:
#除下面path,state冒号形式,也可以使用键值对方式,列如file: path=/testdir/teststate=directory
path: /testdir/test
state: directory
[root@ansible-master ansible]#
注:(1)第一行使用三个横杠作为开始,在YAML语法中,”—“表示文档开始
(2)第二行使用”- “作为开头(注意:横杠后面有空格)YAML使用”- “表示一个块序列的节点
(3)第三行,使用remote_user关键字可以指定在进行远程操作时使用哪个用户进行操作
(4)在YAML语法中进行缩进时,不能使用tab键进行缩进,必须使用空格
(5)每个任务的name都写在前面,这个不分先后,也可以放在这个任务的后面也可以。顺 序没有要求,但是缩进有严格要求
执行结果:
[root@ansible-master ansible]# ansible-playbook ceshi.yml
PLAY [slave] ************************************************************************************
TASK [Gathering Facts] **************************************************************************
ok: [192.168.174.145]
ok: [192.168.174.144]
TASK [ping slave hosts] *************************************************************************
ok: [192.168.174.145]
ok: [192.168.174.144]
TASK [mkdir directory] **************************************************************************
changed: [192.168.174.144]
changed: [192.168.174.145]
PLAY RECAP **************************************************************************************
192.168.174.144 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.174.145 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
可以使用模拟执行来看看结果: ansible-playbook --check test.yml
但是模拟执行的结果报错不能全信。playbook中,可能前后存在依赖关系。模拟执行会报错
二、handlers的用法
应用场景:
当我们修改了某些程序的配置文件以后,可能需要重启应用程序,如果使用playbook来实现这个简单的功能,该怎样编写playbook呢?以nginx作为示例。
假设我们想要将nginx中的某个server的端口从8080改成8088,并且在修改配置以后重启nginx,那么我们可以编写如下剧本:
---
- hosts: slave
remote_user: root
tasks:
- name: Modify the configuration
lineinfile:
path=/etc/nginx/conf.d/test.zsythink.net.conf
regexp="listen(.*) 8080 (.*)"
line="listen\1 8088 \2"
backrefs=yes
backup=yes
- name: restart nginx
service:
name=nginx
state=restarted
第一次执行playbook后,文件被修改,服务重启,第二次执行playbook,由于幂等性,文件修改将被跳过,但是服务仍然重启了。我们想要达到的效果是只有配置文件被修改时才会重启服务。handlers就是来解决这种问题的。
---
- hosts: test70
remote_user: root
tasks:
- name: Modify the configuration
lineinfile:
path=/etc/nginx/conf.d/test.zsythink.net.conf
regexp="listen(.*) 8080(.*)"
line="listen\1 8088\2"
backrefs=yes
backup=yes
notify:
restart nginx
handlers:
- name: restart nginx
service:
name=nginx
state=restarted
有当tasks中的任务”真正执行”以后(真正的进行实际操作,造成了实际的改变),handlers中被调用的任务才会执行
默认情况下,所有task执行完毕后,才会执行各个handler,并不是执行完某个task后,立即执行对应的handler,如果你想要在执行完某些task以后立即执行对应的handler,则需要使用meta模块,示例如下:
---
- hosts: test70
remote_user: root
tasks:
- name: task1
file: path=/testdir/testfile
state=touch
notify: handler1
- name: task2
file: path=/testdir/testfile2
state=touch
notify: handler2
- meta: flush_handlers
- name: task3
file: path=/testdir/testfile3
state=touch
notify: handler3
handlers:
- name: handler1
file: path=/testdir/ht1
state=touch
- name: handler2
file: path=/testdir/ht2
state=touch
- name: handler3
file: path=/testdir/ht3
state=touch
在task2后面有meta,表示执行到这里的时候就立即执行前面两个任务的handlers。
我们还可以在一个task中一次性notify多个handler,怎样才能一次性notify多个handler呢
则需要借助另一个关键字,它就是’listen’,你可以把listen理解成”组名”,我们可以把多个handler分成”组”,当我们需要一次性notify多个handler时,只要将多个handler分为”一组”,使用相同的”组名”即可
示例:
---
- hosts: test70
remote_user: root
tasks:
- name: task1
file: path=/testdir/testfile
state=touch
notify: handler group1
handlers:
- name: handler1
listen: handler group1
file: path=/testdir/ht1
state=touch
- name: handler2
listen: handler group1
file: path=/testdir/ht2
state=touch
作业:使用ansible的playbook写一个剧本。内容为:
task1:ping两个slave主机
task2:安装httpd,如果安装成功就启动httpd服务,停止httpd服务,再启动httpd服务,需要立即执行
task3:查看80端口是否被监听
答案如下:
这里添加dns服务器,因为幂等性,重复执行会跳过(通过校验文件md5值)
[root@ansible-master ansible]# cat httpd.yml
---
- hosts: slave
remote_user: root
tasks:
- name: add dns server1
shell: echo 'nameserver 114.114.114.114' >> /etc/resolv.conf
- name: add dns server2
shell: echo 'nameserver 8.8.8.8' >> /etc/resolv.conf
# - name: clean yum cache
# shell: yum clean all
# - name: remake yum cache
# shell: yum makecache
- name: ping all host to check connection
ping:
- name: install the httpd service
yum:
name: httpd
state: installed
notify: group1
- meta: flush_handlers
- name: check port 80 state
shell: netstat -anp |grep 80
handlers:
- name: stop httpd
listen: group1
service : name=httpd state=stopped
- name: start httpd
listen: group1
service : name=httpd state=started
- name: enable httpd
listen: group1
service : name=httpd enabled=yes
[root@ansible-master ansible]#
执行结果:
可见meta已经生效了。http安装任务结束后就立即进行handlers。后续再继续未完成的task
[root@ansible-master ansible]# ansible-playbook httpd.yml
PLAY [slave] ************************************************************************************
TASK [Gathering Facts] **************************************************************************
ok: [192.168.174.144]
ok: [192.168.174.145]
TASK [add dns server1] **************************************************************************
changed: [192.168.174.144]
changed: [192.168.174.145]
TASK [add dns server2] **************************************************************************
changed: [192.168.174.144]
changed: [192.168.174.145]
TASK [ping all host to check connection] ********************************************************
ok: [192.168.174.145]
ok: [192.168.174.144]
TASK [install the httpd service] ****************************************************************
changed: [192.168.174.144]
changed: [192.168.174.145]
RUNNING HANDLER [stop httpd] ********************************************************************
ok: [192.168.174.144]
ok: [192.168.174.145]
RUNNING HANDLER [start httpd] *******************************************************************
changed: [192.168.174.145]
changed: [192.168.174.144]
RUNNING HANDLER [enable httpd] ******************************************************************
changed: [192.168.174.144]
changed: [192.168.174.145]
TASK [check port 80 state] **********************************************************************
changed: [192.168.174.144]
changed: [192.168.174.145]
PLAY RECAP **************************************************************************************
192.168.174.144 : ok=9 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.174.145 : ok=9 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
三、tags用法
场景:
你写了一个很长的playbook,其中有很多的任务,不过在实际使用这个剧本时,你可能只是想要执行其中的一部分任务而已,或者,你只想要执行其中一类任务而已,而并非想要执行整个剧本中的全部任务,这个时候我们该怎么办呢?我们可以借助tags实现这个需求。
见名知义,tags可以帮助我们对任务进行’打标签’的操作,当任务存在标签以后,我们就可以在执行playbook时,借助标签,指定执行哪些任务,或者指定不执行哪些任务了,这样说可能不够直观,我们来看一个小示例:
给任务打上标签(这里理解为任务与任务组的关系。具有相同标签的task为一个组。和主机清单文件中类似,一个任务可以赋予多个标签====一个主机可以加入多个主机组)
---
- hosts: slave
remote_user: root
tasks:
- name: task1
file:
path: /testdir/t1
state: touch
tags: t1
- name: task2
file: path=/testdir/t2
state=touch
tags: t2
- name: task3
file: path=/testdir/t3
state=touch
tags: t3
执行命令:
只执行t2标签的task
ansible-playbook --tags=t2 testtag.yml
借助标签,除了能够指定”需要执行的任务”,还能够指定”不执行的任务”,示例命令如下。
ansible-playbook --skip-tags='t2' testtag.yml
当tags写在play中而非task中时,play中的所有task会继承当前play中的tags,而上例中,两个任务都会继承httpd标签,同时还有拥有自己的标签,如下所示。标签被下面所有task继承了。
---
- hosts: slave
remote_user: root
tags: t1
tasks:
- name: task1
file:
path: /testdir/t1
state: touch
- name: task2
file: path=/testdir/t2
state=touch
- name: task3
file: path=/testdir/t3
state=touch
ansible自带标签:
1.always关键字,所以即使task3对应的标签没有被调用,task3也会执行,这就是always的作用。
2.从字面上理解,never的作用应该与always正好相反
还有剩余的三个特殊标签分别为 tagged、untagged、all
这三个特殊标签并非像always一样,always作为标签值存在,而这三个特殊标签则是在调用标签时使用,示例如下
ansible-playbook --tags tagged testtag.yml
上述命令表示只执行有标签的任务,没有任何标签的任务不会被执行。
ansible-playbook --skip-tags tagged testtag.yml
上述命令表示跳过包含标签的任务,即使对应的任务包含always标签,也会被跳过。
ansible-playbook --tags untagged testtag.yml
上述命令表示只执行没有标签的任务,但是如果某些任务包含always标签,那么这些任务也会被执行。
ansible-playbook --skip-tags untagged testtag.yml
上述命令表示跳过没有标签的任务。
特殊标签all表示所有任务会被执行,不用指定,默认情况下就是使用这个标签。