Ansible 的 Handlers 用法

许多的 Linux 服务在修改配置文件后都是需要重启服务的,以便能够重新读取配置文件,使新的配置能够生效。那怎么用 Playbook 实现这个简单的功能呢?下面我们来编写一个修改 Nginx 端口的 Playbook,并且在修改完之后重启 Nginx

---
- hosts: all
  remote_user: root
  tasks:
  - name: modify config
    replace:
      path: /etc/nginx/nginx.conf
      regexp: "listen(.*)80;"    # 替换nginx端口为8080
      replace: "listen 8080;"
      backup: yes
  - name: restart nginx        # 重启nginx服务
    service:
      name: nginx
      state: restarted

注意思考:这个 Playbook 虽然可以帮助我们成功修改 Nginx 端口并重启 Nginx 服务,但是大家请注意如果我再次执行这个 Playbook 的话,Nginx 端口已经是8080了,由于 Ansible 幂等性的缘故,所以 modify config 这个 task 没有发生状态的改变,所以这一步返回了绿色的信息,但是 Nginx 的服务还是被重启了,其实我们并没有真正去改变 Nginx 的配置文件,但是却还是重启了 Nginx 服务,这是因为重启服务这个任务是写死了的。这种多余的重启是不需要的。那么在 Playbook 中就是使用 Handlers 来解决这种问题的,下面我们就继续以 Nginx 服务这个小例子来学习 Playbook 的 Handlers 用法

---
- hosts: all
  remote_user: root
  tasks:
  - name: modify config
    replace:
      path: /etc/nginx/nginx.conf
      regexp: "listen(.*)80;"
      replace: "listen 8080;"
      backup: yes
    notify:        # 在modify config这个任务调用handlers任务列表的restart nginx任务(认真理解这句话)
      restart nginx

  handlers:        # 定义一个handlers任务列表
  - name: restart nginx
    service:
      name: nginx
      state: restarted

上面示例我们使用 Handlers 用法,如果 modify config 这个 task 的状态被真正修改过了,notify 就会调用 Handlers 任务列表的 restart Nginx 任务,就会执行重启 Nginx 服务,这样就能达到只有 Nginx 配置文件被真正修改了,才会去重启 Nginx 服务

Handlers 是一种任务列表

在 Playbook 中 Handlers 和 tasks 是同级别的,这是因为 Handlers 也是任务列表的一种。只不过Handlers 中的任务是被用于 tasks 任务列表的 notify 调用而已

---
- hosts: all
  remote_user: root
  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/testfile2
      state: touch

上面 Playbook 的执行过程如下:

PLAY [all] ***********************************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************
ok: [webserver]
ok: [dbserver]

TASK [make testfile1] ************************************************************************************************************
changed: [webserver]
changed: [dbserver]

TASK [make testfile2] ************************************************************************************************************
changed: [webserver]
changed: [dbserver]

RUNNING HANDLER [ht1] ************************************************************************************************************
changed: [webserver]
changed: [dbserver]

RUNNING HANDLER [ht2] ************************************************************************************************************
changed: [dbserver]
changed: [webserver]

PLAY RECAP ***********************************************************************************************************************
dbserver                   : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
webserver                  : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

默认情况下,所有 task 执行完毕后,才会执行各个 Handler,而且 Handler 的执行顺序与 Handler 在 Playbook 中的定义顺序是相同的,与 Handler 被 notify 调用的顺序无关,这一点大家要注意。如果你想要在执行完某些 task 以后立即执行对应的 Handler,则需要使用 meta 模块

---
- hosts: all
  remote_user: root
  tasks:
  - name: make testfile1
    file:
      path: /testdir/testfile1
      state: directory
    notify: ht2

  - meta: flush_handlers    # 定义一个meta任务,表示立即执行之前task任务对应的handlers

  - name: make testfile3
    file:
      path: /testdir/testfile3
      state: directory
    notify: ht1

  handlers:
  - name: ht1
    file:
      path: /testdir/testfile4
      state: touch
  - name: ht2
    file:
      path: /testdir/testfile2
      state: touch

Handlers 分组

我们可以将 Handlers 任务列表分组,将多个 Handlers 任务组成一个组,然后在 task 任务列表 notify 一个 Handlers 组,这时候 task 任务执行完之后就会一次性执行多个 Handlers 任务

---
- hosts: all
  remote_user: root
  tasks:
  - name: make testfile1
    file:
      path: /testdir/testfile1
      state: directory
    notify: handlers group1

  - meta: flush_handlers

  handlers:
  - name: ht1
    listen: handlers group1
    file:
      path: /testdir/testfile4
      state: touch
  - name: ht2
    listen: handlers group1
    file:
      path: /testdir/testfile2
      state: touch

将 ht1 和 ht2 这两个 Handlers 任务都监听 Handlers group1 这一个组,这时候在 task 任务列表 notify “handlers group1” 这个组名时,就执行这个组的所有 Handlers 任务