使用Ansible处理程序

时间:2020-01-09 10:34:03  来源:igfitidea点击:

有时我们希望任务仅在计算机上进行更改时运行。例如,如果某个任务更新了某个服务的配置,则可能需要重新启动该服务,但如果配置未更改,则不需要重新启动。Ansible使用处理程序来处理这个用例。处理程序是仅在收到通知时运行的任务。每个处理程序都应该有一个全局唯一的名称。

  • 处理程序总是按定义的顺序运行,而不是按notify语句中列出的顺序运行。对于使用listen的处理程序也是这样。

  • 处理程序名称和侦听主题位于全局命名空间中。

  • 处理程序名称是可模板化的,而侦听主题不是。

  • 使用唯一的处理程序名称。如果触发多个同名处理程序,则会覆盖第一个处理程序。只有最后定义的一个将运行。

  • 可以通知在静态include中定义的处理程序。

  • 不能通知动态包含中定义的处理程序。

处理程序示例

我们要写一个 ansible-handlers.yml安装"httpd"包,然后使用处理程序启动"httpd"服务。此处理程序仅在进行更改时执行,即依赖任务报告"changed=1"或者更高的值。

--
 - name: Handlers Example
   hosts: server1
   gather_facts: false
   tasks:
     - name: Install httpd latest version
       yum:
         name: httpd
         state: latest
       become: true
       notify: restart_httpd
   handlers:
     - name: restart_httpd
       become: true
       service:
         name: httpd
         state: started

说明:

我们必须注意"handlers"的缩进,因为它应该与"tasks"从同一行开始。

在这个剧本中,我创建了一个任务,使用sudo权限将httpd rpm安装到最新的可用版本。

接下来,我创建了一个处理程序,负责启动服务。处理程序将使用处理程序的"name"值执行,即使用"notify"关键字"restart_httpd"。

我使用了'notify'和'handler'的名称,因此这将检查任务的更改模式,并相应地决定执行'handler'。

让我们执行剧本:

[ansible@controller ~]$ ansible-playbook ansible-handlers.yml
PLAY [Handlers Example] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
TASK [Install httpd latest version] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
changed: [server1]
RUNNING HANDLER [restart_httpd] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
changed: [server1]
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
server1                    : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

从输出中我们知道我们的任务和处理程序都已成功执行。

如果我们重新运行相同的playbook来检查是否执行了处理程序:

[ansible@controller ~]$ ansible-playbook ansible-handlers.yml
PLAY [Handlers Example] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
TASK [Install httpd latest version] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
ok: [server1]
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
server1                    : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

这次没有执行任何更改,因为httpd已经处于安装状态。

在处理程序运行时刷新处理程序和控件

默认情况下,处理程序在特定游戏中的所有任务完成后运行。这种方法非常有效,因为不管有多少任务通知处理程序,处理程序只运行一次。例如,如果多个任务更新配置文件并通知处理程序重新启动Apache,Ansible只会反弹Apache一次,以避免不必要的重新启动。

在这个示例剧本中,我有两个任务,它们将使用调试模块在控制台上打印一些文本。我还添加了一个处理程序,它将提供执行的日期和时间。现在我从两个任务调用此处理程序:

--
 - name: Handlers Example
   hosts: server1
   gather_facts: false
   become: true
   tasks:
     - name: print message-1
       debug:
         msg: "First Message"
       changed_when: true
       notify: run_handler
     - name: print message-2
       debug:
         msg: "Second Message"
       changed_when: true
       notify: run_handler
   handlers:
     - name: run_handler
       debug:
         msg: "Today's date and time: {{ '%d-%m-%Y %H:%M:%S' | strftime }}"

让我们验证是否为这两个任务执行了处理程序:

[ansible@controller ~]$ ansible-playbook ansible-handlers.yml
PLAY [Handlers Example] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
TASK [print message-1] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
changed: [server1] => {
    "msg": "First Message"
}
TASK [print message-2] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
changed: [server1] => {
    "msg": "Second Message"
}
RUNNING HANDLER [run_handler] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ****
ok: [server1] => {
    "msg": "Today's date and time: 25-09-2017 09:55:03"
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
server1                    : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

因此,不管两个任务都在调用处理程序,处理程序只被调用一次。

现在这是默认行为,但"如果需要在播放结束前运行处理程序",请添加一个"task"来使用"meta"模块刷新它们,该模块执行Ansible操作。我已经更新了我的playbook,在远程服务器上执行'sleep',然后观察处理程序的时间,以确保在每个任务之后都有处理程序的新实例被执行:

--
 - name: Handlers Example
   hosts: server1
   gather_facts: false
   become: true
   tasks:
     - name: Sleep for 2 seconds
       command: sleep 2
       notify: run_handler
     - name: Flush handlers
       meta: flush_handlers
     - name: Sleep for 5 seconds
       command: sleep 5
       notify: run_handler
     - name: Flush handlers
       meta: flush_handlers
     - name: Sleep for 7 seconds
       command: sleep 7
       notify: run_handler
   handlers:
     - name: run_handler
       debug:
         msg: "Today's date and time: {{ '%d-%m-%Y %H:%M:%S:%s' | strftime }}"
  • 其中:我的第一个任务将休眠2分钟,然后执行处理程序

  • 然后我们刷新处理程序并调用第二个任务,该任务将休眠5秒并调用处理程序

  • 然后,我们将再次刷新处理程序并在第三次调用处理程序之前再休眠7秒

  • 在处理程序中,我使用了Jinja语法中的"strftime"来获取处理程序的执行时间

让我们执行剧本:

[ansible@controller ~]$ ansible-playbook ansible-handlers.yml
PLAY [Handlers Example] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
TASK [Sleep for 2 seconds] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
changed: [server1]
RUNNING HANDLER [run_handler] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ****
ok: [server1] => {
    "msg": "Today's date and time: 25-09-2017 10:05:45:1601028345"
}
TASK [Sleep for 5 seconds] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
changed: [server1]
RUNNING HANDLER [run_handler] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ****
ok: [server1] => {
    "msg": "Today's date and time: 25-09-2017 10:05:51:1601028351"
}
TASK [Sleep for 7 seconds] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
changed: [server1]
RUNNING HANDLER [run_handler] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ****
ok: [server1] => {
    "msg": "Today's date and time: 25-09-2017 10:05:58:1601028358"
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
server1                    : ok=6    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

所以现在我们的处理程序已经执行了三次了,因为我们在每次任务执行之后都会刷新处理程序,而且处理程序执行的时间也不一样,所以生命是美好的。

在处理程序中使用变量

我们可能希望Ansible处理程序使用变量。例如,如果服务的名称因分布而略有不同,则希望输出显示每个目标计算机的重新启动服务的确切名称。避免在处理程序的名称中放置变量。由于处理程序名称是早期模板化的,Ansible可能没有如下处理程序名称可用的值:

handlers:
# This handler name Jan cause your play to fail!
- name: Restart "{{ web_service_name }}"

如果处理程序名称中使用的变量不可用,"整个播放失败"。更改该变量不会导致新创建的处理程序。

因此,在我在示例剧本中使用的场景中,我们应该始终使用默认变量:

--
 - name: Handlers Example
   hosts: server1
   gather_facts: false
   become: true
   vars:
     pkg: httpd
   tasks:
     - name: Installing vsftpd
       debug:
         msg: "restarting vsftp"
       changed_when: true
       notify: restart vsftpd
   handlers:
     - name: "restart {{ pkg | default('vsftpd')}}"
       debug:
         msg: "Restarting {{ pkg | default('vsftpd')}}"

现在我已经定义了一个'pkg'变量,但是如果由于某种原因处理程序无法获得这个值,那么它将考虑默认变量值,而不是整个游戏失败:

让我们来执行这个剧本:

[ansible@controller ~]$ ansible-playbook ansible-handlers.yml
PLAY [Handlers Example] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **
TASK [Installing vsftpd] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
changed: [server1] => {
    "msg": "restarting vsftp"
}
RUNNING HANDLER [restart vsftpd] ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** *****
ok: [server1] => {
    "msg": "Restarting vsftpd"
}
PLAY RECAP ** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** ***
server1                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

所以我们的剧本成功地执行了任务和处理程序。