本来打算自己写一波 ansible 系列的,后来发现一老哥写的太好了,「点击此处直达」,我这里也就边看边对该系列文章做下笔记,方便以后查阅,ansible 入门的话墙裂建议阅读前方链接博文~~~
介绍
此处我们先大概的描述一下 handlers 的概念,你可以把 handlers 理解成另一种 tasks,handlers 是另一种任务列表,handlers 中的任务会被 tasks 中的任务进行调用,但是,被调用并不意味着一定会执行,只有当 tasks 中的任务真正执行以后(真正的进行实际操作,造成了实际的改变),handlers 中被调用的任务才会执行,如果 tasks 中的任务并没有做出任何实际的操作,那么 handlers 中的任务即使被调用,也并不会执行。这样说似乎不容易被理解,我们来写一个小示例,示例如下。
---
- hosts: all
tasks:
- name: Modify the configuration
lineinfile:
path=/etc/nginx/conf.d/test.zze.xyz.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
如上例所示,我们使用 handlers
关键字,指明哪些任务可以被调用,之前说过,handlers 是另一种任务列表,你可以把 handlers 理解成另外一种tasks,你可以理解成它们是平级的,所以,handlers 与 tasks 是对齐的(缩进相同)。
上例中的 handlers 中只有一个任务,这个任务的名称为 restart nginx
,之前也说明过,handlers 中的任务需要被 tasks 中的任务调用,那么上例中,restart nginx
被哪个任务调用了呢?很明显,restart nginx
被 Modify the configuration
调用了。没错,如你所见,我们使用 notify
关键字调用 handlers 中的任务,或者说,通过 notify
关键字通知 handlers 中的任务。
所以,综上所述,上例中的 play 表示,如果 Modify the configuration
真正的修改了配置文件(实际的操作),那么则执行 restart nginx
任务,如果 Modify the configuration
并没有进行任何实际的改动,则不执行 restart nginx
,通常来说,任务执行后如果做出了实际的操作,任务执行后的状态为 changed
,所以,任务执行后的状态为 changed
则会执行对应的 handlers,这就是 handlers 的作用。
执行顺序
handlers 是另一种任务列表,所以 handlers 中可以有多个任务,被 tasks 中不同的任务 notify,示例如下:
---
- hosts: all
tasks:
- name: make testfile1
file: path=/testdir/testfile1
state=directory
notify: ht2
- name: make testfile2
file: path=/testdir/testfile2
state=directory
notify: ht1
handlers:
- name: ht1
file: path=/testdir/ht1
state=touch
- name: ht2
file: path=/testdir/ht2
state=touch
如上例所示,tasks 与 handlers 都是任务列表,只是 handlers 中的任务被 tasks 中的任务 notify 罢了,那么我们来执行一下上述 playbook,如下:
$ ansible-playbook test
PLAY [all] ****************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************
ok: [B]
ok: [C]
TASK [make testfile1] *****************************************************************************************************************************
changed: [C]
changed: [B]
TASK [make testfile2] *****************************************************************************************************************************
changed: [B]
changed: [C]
RUNNING HANDLER [ht1] *****************************************************************************************************************************
changed: [C]
changed: [B]
RUNNING HANDLER [ht2] *****************************************************************************************************************************
changed: [C]
changed: [B]
PLAY RECAP ****************************************************************************************************************************************
B : ok=5 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
C : ok=5 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
可以看出,handler 执行的顺序与 handler 在 playbook 中定义的顺序是相同的,与 handler 被 notify 的顺序无关。
默认情况下,所有 task 执行完毕后,才会执行各个 handler,并不是执行完某个 task 后,立即执行对应的 handler。
立即执行
如果你想要在执行完某些 task 以后立即执行对应的 handler,则需要使用 meta 模块,示例如下:
---
- hosts: all
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
如上例所示,我在 task1 与 task2 之后写入了一个任务,我并没有为这个任务指定 name
属性,这个任务使用 meta 模块,meta 任务是一种特殊的任务,meta 任务可以影响 ansible 的内部运行方式。
上例中,meta 任务的参数值为 flush_handlers
,meta: flush_handlers
表示立即执行之前的 task 所对应 handler,什么意思呢?意思就是,在当前 meta 任务之前,一共有两个任务,task1
与 task2
,它们都有对应的 handler,当执行完 task1 与 task2 以后,立即执行对应的 handler,而不是像默认情况那样在所有任务都执行完毕以后才能执行各个 handler,那么我们来实际运行一下上述剧本,运行结果如下:
$ ansible-playbook test
PLAY [all] ****************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************
ok: [B]
ok: [C]
TASK [task1] **************************************************************************************************************************************
changed: [C]
changed: [B]
TASK [task2] **************************************************************************************************************************************
changed: [B]
changed: [C]
RUNNING HANDLER [handler1] ************************************************************************************************************************
changed: [C]
changed: [B]
RUNNING HANDLER [handler2] ************************************************************************************************************************
changed: [C]
changed: [B]
TASK [task3] **************************************************************************************************************************************
changed: [C]
changed: [B]
RUNNING HANDLER [handler3] ************************************************************************************************************************
changed: [B]
changed: [C]
PLAY RECAP ****************************************************************************************************************************************
B : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
C : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
如上所示,meta
任务之前的任务 task1
与 task2
在进行了实际操作以后,立即运行了对应的 handler1
与 handler2
,然后才运行了 task3
,在所有 task 都运行完毕后,又逐个将剩余的 handler 根据情况进行调用。
聪明如你一定想到了,如果想要每个 task 在实际操作后都立马执行对应 handlers,则可以在每个任务之后都添加一个 meta 任务,并将其值设置为 flush_handlers
所以,我们可以依靠 meta 任务,让 handler 的使用变得更加灵活。
批量调用
我们还可以在一个 task 中一次性 notify 多个 handler,需要借助另一个关键字,它就是 listen
,你可以把 listen 理解成组名,我们可以把多个 handler 分成组,当我们需要一次性 notify 多个 handler 时,只要将多个 handler 分为一组,使用相同的组名即可,当 notify 对应的值为组名时,组内的所有 handler 都会被 notify,这样说可能还是不容易理解,我们来看个小示例,示例如下:
---
- hosts: all
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
如上例所示,handler1
与 handler2
的 listen
的值都是 handler group1
,当 task1
中 notify
的值为handler group1
时,handler1
与 handler2
都会被 notify,还是很方便的。
评论区