notify:关键字,紧跟模块之后,用于通知handler(基于name进行通知),若写了notify,那么模块执行的结果为changed 状态将会触发所指定的handler.
handlers:关键字,和tasks是一个层级,里面可以包含有很多的task, 每个task在这里叫做handler,每个handler也是支持并且仅仅支持一个模块. 但是一个handlers 可以包含有很多个handler.
notify配合handler的使用方法以及小小的坑:
notify 和handler是配合在一起使用的,但是: handler 并不会在notify 通知后立即执行,而是在运行到handlers的时候,最终执行一次,所以handlers 通常是写在play的最后,handlers里面的每个handler都需要一个name 属性. handlers中的handler 只能被notify 触发,如果没有notify触发,handlers 不会被执行.
这时候就导致了一个问题:
如果play中的某个task带有notify属性,在当前的运行过程中,这个task的运行结果状态是changed, 这样就触发了notify, 但是在执行到相应的handler之前遇到了错误导致play 失败,此时我们重新运行play, 前面带有notify属性的task 返回的结果状态是“OK“, 不是changed, 所以不会触发 notify. 此时后面执行到 相应handler的时候 并不会执行对应的task. 这与我们所期望的结果是不一致的. 下面做一个简单的验证:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #首先编写一个playbook,内容如下: ocalhost ~]# cat my.yml --- - name: local test only. hosts: localhost tasks: - copy: "src=/etc/passwd dest=./passwd.bak" notify: start restart sshd.service - shell: "lsblkid" #该task会报错而导致退出. 然后handler 无法成功执行 handlers: - name: start restart sshd.service shell: "systemctl restart sshd.service" ... [root@localhost ~]# ansible-playbook ./my.yml [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' PLAY [local test only.] ********************************************************************************************************************************************************************* TASK [Gathering Facts] ********************************************************************************************************************************************************************** ok: [localhost] TASK [copy] ********************************************************************************************************************************************************************************* changed: [localhost] TASK [shell] ******************************************************************************************************************************************************************************** fatal: [localhost]: FAILED! => {"changed": true, "cmd": "lsblkid", "delta": "0:00:00.005293", "end": "2020-11-03 14:23:54.802252", "msg": "non-zero return code", "rc": 127, "start": "2020-11-03 14:23:54.796959", "stderr": "/bin/sh: lsblkid: command not found", "stderr_lines": ["/bin/sh: lsblkid: command not found"], "stdout": "", "stdout_lines": []} RUNNING HANDLER [start restart sshd.service] ************************************************************************************************************************************************ PLAY RECAP ********************************************************************************************************************************************************************************** localhost : ok=2 changed=1 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0 |
上面的结果中,有两个状态是ok, 一个是failed, 还有一个状态是changed。 其中 :
gating facts 也是一个task, 结果是ok
copy task返回的状态是changed, changed状态必然也是 ok 状态,但是返回ok状态一定不会是changed 状态.
shell task对应的是failed的结果
在上面的结果中,其实是 触发了handler的,所以可以看到有一条: RUNNING HANDLER [start restart sshd.service] 的提示,但是对应的handler 却没有得到执行,这是因为默认情况下,一旦task失败,那么后续的task 都会停止运行, 而task 本质上都是module的执行,所以 handler也就因为前面task的失败而无法获得运行, 要避免这个失败导致的task 退出,可以在特定的task 中添加 ignore_errors: True 关键字.
下面修改my.yml 文件,使其可以正常的运行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | [root@localhost ~]# cat my.yml --- - name: local test only. hosts: localhost tasks: - copy: "src=/etc/passwd dest=./passwd.bak" notify: start restart sshd.service - shell: "lsblk" handlers: - name: start restart sshd.service shell: "systemctl restart sshd.service" ... [root@localhost ~]# ansible-playbook ./my.yml [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' PLAY [local test only.] ********************************************************************************************************************************************************************* TASK [Gathering Facts] ********************************************************************************************************************************************************************** ok: [localhost] TASK [copy] ********************************************************************************************************************************************************************************* ok: [localhost] TASK [shell] ******************************************************************************************************************************************************************************** changed: [localhost] PLAY RECAP ********************************************************************************************************************************************************************************** localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 |
这一次,playbook成功执行完成,但是并没有执行handler,这是因为 copy task返回的结果状态为 ok, 所以不会触发 notify. 其返回的结果解释如下:
3个状态是ok的task分别是:gathering facts, copy, shell.
1个状态是changed的task 是: shell
handler 没有获得运行,因为根本就没有获得触发条件
总结一下就是:
A.
触发handler 和 handler获得执行是两回事,成功触发后,会在playbook的运行结果中看到 RUNNING HANDLER 字样的提示,但是不代表就执行了.
B.
handler要获得执行,除了满足触发条件外,需要前面的task 无误的执行到 handler ,如果前面有错误导致有task 失败,那么默认情况下, playbook会终止在目标机器上执行,从而导致handler 即使被触发,那么也无法获得执行.
C.
对于上述的问题,一个解决办法是 : 在前面可能失败的task里面,增加 ‘ignore_errors: True’ 属性. 这样playbook就可以无视该task的执行结果而继续向下执行,从而避免成功触发却无法执行的情形.