【问题标题】:Run an Ansible handler only once for the entire playbook只为整个剧本运行一次 Ansible 处理程序
【发布时间】:2017-01-09 20:41:05
【问题描述】:

我想在整个剧本中只运行一次处理程序。

我尝试在 playbook 文件的以下内容中使用 include 语句,但这导致处理程序运行多次,每次播放一次:

- name: Configure common config
  hosts: all
  become: true
  vars:
        OE: "{{ ansible_hostname[5] }}"
  roles:
    - { role: common }
  handlers:
    - include: handlers/main.yml

- name: Configure metadata config
  hosts: metadata
  become: true
  vars:
        OE: "{{ ansible_hostname[5] }}"
  roles:
    - { role: metadata }
  handlers:
    - include: handlers/main.yml

这里是handlers/main.yml的内容:

- name: restart autofs
  service:
    name: autofs.service
    state: restarted

这是通知处理程序的任务之一的示例:

- name: Configure automount - /opt/local/xxx in /etc/auto.direct
  lineinfile:
     dest: /etc/auto.direct
     regexp: "^/opt/local/xxx"
     line: "/opt/local/xxx   -acdirmin=0,acdirmax=0,rdirplus,rw,hard,intr,bg,retry=2  nfs_server:/vol/xxx"
  notify: restart autofs

如何让剧本只为整个剧本执行一次处理程序?

【问题讨论】:

  • 你的剧本看起来有点疯狂。这本剧本的总体思路是什么,为什么要在此处包含处理程序?处理程序应该由任务通知,因此它是角色的一部分。查看文档如何使用处理程序:docs.ansible.com/ansible/playbooks_intro.html。另一种只运行一次特定任务的方法如下:docs.ansible.com/ansible/playbooks_delegation.html#run-once
  • 不确定野生是什么意思。该剧本是一系列剧本,示例中有两个剧本。我想对多个播放使用单个处理程序,并让它为整个剧本执行一次。我希望这是有道理的。不需要只运行一次任务,只需要运行一次处理程序。
  • @techraf 游戏中有多个任务通知处理程序。我编辑了问题并添加了一项任务。
  • @user3155618 我想你可能想检查我的new suggested way 来实现这一点。它比以前更简单。

标签: ansible ansible-handlers


【解决方案1】:

答案

标题中问题的字面答案是:不。

剧本是剧本的列表。 Playbook 没有命名空间,没有变量,没有状态。所有的配置、逻辑和任务都在 play 中定义。

处理程序是具有不同调用计划的任务(不是顺序的,而是有条件的,在播放结束时一次,或由meta: flush_handlers 任务触发)。

一个处理程序属于一个剧本,而不是剧本,并且没有办法在剧本之外触发它(即在剧本的末尾)。


解决方案

问题的解决方案无需参考处理程序。

您可以使用group_by module根据每场比赛底部的任务结果创建一个临时小组。

然后您可以在 playbook 的末尾定义一个单独的播放,在属于上述 ad-hoc 组的目标上重新启动服务。

请参考下面的存根:

- hosts: all
  roles:
    # roles declaration
  tasks:
    - # an example task modifying Nginx configuration
      register: nginx_configuration

    # ... other tasks ...

    - name: the last task in the play
      group_by:
        key: hosts_to_restart_{{ 'nginx' if nginx_configuration is changed else '' }}

# ... other plays ...

- hosts: hosts_to_restart_nginx
  gather_facts: no
  tasks:
    - service:
        name: nginx
        state: restarted

【讨论】:

    【解决方案2】:

    可能的解决方案

    使用处理程序将主机添加到内存清单。然后添加play 为这些主机运行重启服务。 看这个例子:

    如果任务发生变化,它会通知mark to restart 设置事实,该主机需要重新启动服务。

    第二个处理程序add host 非常特别,因为add_host 任务即使在处理程序中也只在整个播放过程中运行一次,另见documentation。但是如果收到通知,它将在处理程序命令中隐含的标记完成后运行。 处理程序遍历运行任务的主机并检查主机服务是否需要重新启动,如果是,则添加到特殊的hosts_to_restart 组。

    由于事实在播放过程中持续存在,因此请通知第三个处理程序 clear mark 以获取受影响的主机。

    您通过移动处理程序隐藏了很多行以分隔文件并包含它们。

    库存文件

    10.1.1.[1:10]
    [primary]
    10.1.1.1
    10.1.1.5
    

    test.yml

    ---
    
    - hosts: all
      gather_facts: no
      tasks:
          - name: Random change to notify trigger
            debug: msg="test"
            changed_when: "1|random == 1"
            notify:
                - mark to restart
                - add host
                - clear mark
      handlers:
          - name: mark to restart
            set_fact: restart_service=true
          - name: add host
            add_host:
                name: "{{item}}"
                groups: "hosts_to_restart"
            when: hostvars[item].restart_service is defined and hostvars[item].restart_service
            with_items: "{{ansible_play_batch}}"
          - name: clear mark
            set_fact: restart_service=false
    
    - hosts: primary
      gather_facts: no
      tasks:
          - name: Change to notify trigger
            debug: msg="test"
            changed_when: true
            notify:
                - mark to restart
                - add host
                - clear mark
      handlers:
          - name: mark to restart
            set_fact: restart_service=true
          - name: add host
            add_host:
                name: "{{item}}"
                groups: "hosts_to_restart"
            when: hostvars[item].restart_service is defined and hostvars[item].restart_service
            with_items: "{{ansible_play_batch}}"
          - name: clear mark
            set_fact: restart_service=false
    
    
    - hosts: hosts_to_restart
      gather_facts: no
      tasks:
          - name: Restart service
            debug: msg="Service restarted"
            changed_when: true
    

    【讨论】:

    • 我想你可能想查看我的new suggested way,因为修复旧的会很麻烦。
    【解决方案3】:

    post_tasks 中触发的处理程序将在其他所有内容之后运行。并且处理程序可以设置为run_once: true

    【讨论】:

      【解决方案4】:

      我不清楚你的处理程序应该做什么。不管怎样,至于official documentation,handlers

      在播放中的每个任务块结束时触发, 并且即使由多个不同的通知只会触发一次 任务 [...] 从 Ansible 2.2 开始,处理程序还可以“监听”通用主题,任务可以按如下方式通知这些主题:

      因此,处理程序会为每个任务块通知/执行一次。 可能您的目标只是在“所有”目标主机之后保留处理程序,但这似乎不是对处理程序的干净使用。 .

      【讨论】:

      • 如果处理程序仅包含在“所有”目标主机的播放中,则在其他播放中通知时它将不会运行。
      • 你是对的。但是你的“handlers/main.yml”在做什么呢?
      • 修改了原问题,请看帖子中“handlers/main.yml”的内容。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-05
      • 1970-01-01
      • 1970-01-01
      • 2017-10-05
      • 2017-09-26
      • 1970-01-01
      相关资源
      最近更新 更多