概述
handler(触发器):Ansible 中一类特殊任务,默认不会主动执行,只有被 notify 触发时才运行。
典型场景:配置文件修改后重启服务(nginx、mysql、redis 等),避免配置没变也无脑重启。
其关键特性如下:
- 只有对应 task 状态为 changed(文件 / 配置被修改)才会触发 handler;
- 所有普通 task 执行完毕后,统一批量执行被触发的 handler;
- 多个 task 同时 notify 同一个 handler,只会执行一次;
- handler 内部也可以 notify 其他 handler(链式触发)。
基础语法结构
---
- name: 部署nginx配置并自动重启
hosts: web
tasks:
# 任务1:推送nginx配置文件
- name: 分发nginx.conf
copy:
src: ./files/nginx.conf
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
# 配置文件发生变更时,通知名为 restart nginx 的handler
notify: restart nginx
# 任务2:推送站点配置
- name: 分发站点vhost配置
copy:
src: ./files/site.conf
dest: /etc/nginx/conf.d/site.conf
notify: restart nginx
# 触发器定义区域,和tasks同级
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
执行逻辑:
- 两个 copy 任务只要任意一个文件发生修改(changed);
- 所有 tasks 跑完后,仅执行一次 restart nginx。
handler示例:
多个task同时触发handler会发生什么?
-
编写playbook文件
root@master:/data00/ansible# cat handler1.yaml
- name: handler使用
hosts: es
tasks:-
name: copy test1文件
copy:
src: /data00/ansible/test1.txt
dest: /tmp/test1.txt
mode: 600
notify: notify handler -
name: copy test2文件
copy:
src: /data00/ansible/test2.txt
dest: /tmp/test2.txt
mode: 600
notify: notify handler
handlers: -
name: notify handler
debug:
msg: "handler触发了"
-
- name: handler使用
测试执行
发现远端机器被改变,handler被触发了,但是只执行一次
root@master:/data00/ansible# ansible-playbook handler1.yaml
PLAY [handler使用] ***********************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************
ok: [10.37.120.9]
TASK [copy test1文件] ********************************************************************************************************************************************************************************************
changed: [10.37.120.9]
TASK [copy test2文件] ********************************************************************************************************************************************************************************************
changed: [10.37.120.9]
# 发现这里handler被触发了
RUNNING HANDLER [notify handler] *********************************************************************************************************************************************************************************
ok: [10.37.120.9] => {
"msg": "handler触发了"
}
PLAY RECAP *******************************************************************************************************************************************************************************************************
10.37.120.9 : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
如果再执行一次会发生什么?
发现handler没有被触发,因为文件状态没有被改变
root@master:/data00/ansible# ansible-playbook handler1.yaml
PLAY [handler使用] ***********************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************
ok: [10.37.120.9]
TASK [copy test1文件] ********************************************************************************************************************************************************************************************
ok: [10.37.120.9]
TASK [copy test2文件] ********************************************************************************************************************************************************************************************
ok: [10.37.120.9]
PLAY RECAP *******************************************************************************************************************************************************************************************************
10.37.120.9 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
定义多个handler,没有被notify会怎么样
root@master:/data00/ansible# cat handler2.yaml
- name: handler使用
hosts: db
tasks:
- name: copy test1文件
copy:
src: /data00/ansible/test1.txt
dest: /tmp/test1.txt
mode: 600
notify: notify handler
# 定义handler
handlers:
- name: notify handler
debug:
msg: "handler触发了"
- name: handler2
debug:
msg: "handler2触发了"
- name: handler3
debug:
msg: "handler3触发了"
执行:
发现只有1个handler被触发了,其它的handler并没有被触发
root@master:/data00/ansible# ansible-playbook handler2.yaml
PLAY [handler使用] ***********************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************
ok: [10.37.99.63]
TASK [copy test1文件] ********************************************************************************************************************************************************************************************
changed: [10.37.99.63]
RUNNING HANDLER [notify handler] *********************************************************************************************************************************************************************************
ok: [10.37.99.63] => {
"msg": "handler触发了"
}
PLAY RECAP *******************************************************************************************************************************************************************************************************
10.37.99.63 : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
多个handler之间的执行顺序是怎么样的?
先给结论,多个handler之间的执行顺序依赖文件中定义的先后顺序,不依赖notify的顺序。可以看下面的案例
-
编写playbook文件
root@master:/data00/ansible# cat handler3.yaml
-
name: handler使用
hosts: db
tasks:-
name: copy test1文件
copy:
src: /data00/ansible/test1.txt
dest: /tmp/test1.txt
mode: 600
notify: handler3 -
name: copy test2文件
copy:
src: /data00/ansible/test2.txt
dest: /tmp/test2.txt
mode: 600
notify: handler2
handlers:
- name: notify handler
debug:
msg: "handler触发了" - name: handler2
debug:
msg: "handler2触发了" - name: handler3
debug:
msg: "handler3触发了"
-
-
-
执行:
发现上文先notify的handle3,然后notify的handler2。但是下面执行的handler2,然后执行的handler3
root@master:/data00/ansible# ansible-playbook handler3.yaml
PLAY [handler使用] ***********************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************
ok: [10.37.99.63]
TASK [copy test1文件] ********************************************************************************************************************************************************************************************
changed: [10.37.99.63]
TASK [copy test2文件] ********************************************************************************************************************************************************************************************
changed: [10.37.99.63]
# 先执行的handler2
RUNNING HANDLER [handler2] ***************************************************************************************************************************************************************************************
ok: [10.37.99.63] => {
"msg": "handler2触发了"
}
# 再执行handler3
RUNNING HANDLER [handler3] ***************************************************************************************************************************************************************************************
ok: [10.37.99.63] => {
"msg": "handler3触发了"
}
一次性notify多个handler
-
编写playbook文件
root@master:/data00/ansible# cat handler4.yaml
-
name: handler使用
hosts: db
tasks:- name: copy test1文件
copy:
src: /data00/ansible/test1.txt
dest: /tmp/test1.txt
mode: 600一次性notify多个handler
notify:- handler3
- handler2
- notify handler
handlers:
- name: notify handler
debug:
msg: "handler触发了" - name: handler2
debug:
msg: "handler2触发了" - name: handler3
debug:
msg: "handler3触发了"
- name: copy test1文件
-
-
执行
这里发现不依赖notify的定义顺序,只依赖handlers定义的先后顺序
root@master:/data00/ansible# ansible-playbook handler4.yaml
PLAY [handler使用] ***********************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************
ok: [10.37.99.63]
TASK [copy test1文件] ********************************************************************************************************************************************************************************************
changed: [10.37.99.63]
RUNNING HANDLER [notify handler] *********************************************************************************************************************************************************************************
ok: [10.37.99.63] => {
"msg": "handler触发了"
}
RUNNING HANDLER [handler2] ***************************************************************************************************************************************************************************************
ok: [10.37.99.63] => {
"msg": "handler2触发了"
}
RUNNING HANDLER [handler3] ***************************************************************************************************************************************************************************************
ok: [10.37.99.63] => {
"msg": "handler3触发了"
}
PLAY RECAP *******************************************************************************************************************************************************************************************************
10.37.99.63 : ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
handler 链式触发(handler 内部 notify)
-
编写playbook文件
root@master:/data00/ansible# cat handler5.yaml
-
name: handler使用
hosts: db
tasks:- name: copy test1文件
copy:
src: /data00/ansible/test1.txt
dest: /tmp/test1.txt
mode: 600
notify:- handler3
handlers:
- name: notify handler
debug:
msg: "handler触发了" - name: handler2
shell:
cmd: "echo handler2"
notify: notify handler - name: handler3
shell:
cmd: "echo handler3"
notify: handler2
- name: copy test1文件
-
-
执行
root@master:/data00/ansible# ansible-playbook handler5.yaml
PLAY [handler使用] ***********************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************
ok: [10.37.99.63]TASK [copy test1文件] ********************************************************************************************************************************************************************************************
changed: [10.37.99.63]RUNNING HANDLER [handler3] ***************************************************************************************************************************************************************************************
changed: [10.37.99.63]RUNNING HANDLER [handler2] ***************************************************************************************************************************************************************************************
changed: [10.37.99.63]PLAY RECAP *******************************************************************************************************************************************************************************************************
10.37.99.63 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
强制立即执行 handler
默认 handler 在所有 task 末尾执行,若需要中途立刻触发,使用 meta 模块:
-
编写playbook文件
root@master:/data00/ansible# cat handler6.yaml
-
name: handler使用
hosts: db
tasks:- name: copy test1文件
copy:
src: /data00/ansible/test1.txt
dest: /tmp/test1.txt
mode: 600
notify: handler3
立刻执行上面触发的handler
- meta: flush_handlers
- name: copy test2文件
copy:
src: /data00/ansible/test2.txt
dest: /tmp/test2.txt
mode: 600
notify: notify handler
handlers:
- name: notify handler
debug:
msg: "handler触发了" - name: handler2
debug:
msg: "handler2触发了" - name: handler3
debug:
msg: "handler3触发了"
- name: copy test1文件
-
-
执行:
root@master:/data00/ansible# ansible-playbook handler6.yaml
PLAY [handler使用] ***********************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************
ok: [10.37.99.63]TASK [copy test1文件] ********************************************************************************************************************************************************************************************
changed: [10.37.99.63]TASK [meta] ******************************************************************************************************************************************************************************************************
RUNNING HANDLER [handler3] ***************************************************************************************************************************************************************************************
ok: [10.37.99.63] => {
"msg": "handler3触发了"
}TASK [copy test2文件] ********************************************************************************************************************************************************************************************
changed: [10.37.99.63]RUNNING HANDLER [notify handler] *********************************************************************************************************************************************************************************
ok: [10.37.99.63] => {
"msg": "handler触发了"
}PLAY RECAP *******************************************************************************************************************************************************************************************************
10.37.99.63 : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
handler中添加when判断
handler中添加上when判断,当条件表达式不成立时,handler也不会被触发。
-
编写playbook文件
root@master:/data00/ansible# cat handler7.yaml
-
name: handler使用
hosts: db
vars:
env: prod
tasks:- name: copy test1文件
copy:
src: /data00/ansible/test1.txt
dest: /tmp/test1.txt
mode: 600
notify: handler3
handlers:
- name: notify handler
debug:
msg: "handler触发了"
when: env is defined and env == "dev" - name: handler2
debug:
msg: "handler2触发了"
when: env is defined and env == "test" - name: handler3
debug:
msg: "handler3触发了"
when: env is defined and env == "prod"
- name: copy test1文件
-
-
执行:
发现只有handler3被触发了
root@master:/data00/ansible# ansible-playbook handler7.yaml
PLAY [handler使用] ***********************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************
ok: [10.37.99.63]
TASK [copy test1文件] ********************************************************************************************************************************************************************************************
ok: [10.37.99.63]
PLAY RECAP *******************************************************************************************************************************************************************************************************
10.37.99.63 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
root@master:/data00/ansible# echo 222 > test1.txt
root@master:/data00/ansible# ansible-playbook handler7.yaml
PLAY [handler使用] ***********************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************
ok: [10.37.99.63]
TASK [copy test1文件] ********************************************************************************************************************************************************************************************
changed: [10.37.99.63]
RUNNING HANDLER [handler3] ***************************************************************************************************************************************************************************************
ok: [10.37.99.63] => {
"msg": "handler3触发了"
}
PLAY RECAP *******************************************************************************************************************************************************************************************************
10.37.99.63 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
总结,handler常见坑点
- handler name 必须完全匹配 notify 字符串(大小写、空格敏感)
- tasks任务被触发时,只有当状态为changed时handler才会被触发,为ok时handler不会被触发
- 多个任务 notify 同一个 handler,只会执行一次,无需担心重复重启服务。
- handler 不会自动执行,无 notify 永远不运行若想强制运行 handler,可用 meta: flush_handlers
- handler支持when判断条件,当when的条件表达式不成立时,handler不会被触发
- handler中的任务执行顺序只依赖定义的先后顺序,不依赖notify的先后顺序