异步操作和轮询

默认情况下,Ansible 同步运行任务,在操作完成之前保持与远程节点的连接打开。这意味着在剧本中,每个任务默认情况下都会阻塞下一个任务,并且后续任务在当前任务完成之前不会运行。这种行为可能会带来挑战。例如,某个任务可能需要比 SSH 会话允许的时间更长才能完成,从而导致超时。或者你可能希望一个长时间运行的进程在后台执行,同时并发执行其他任务。异步模式允许你控制长时间运行的任务的执行方式。

异步临时任务

你可以使用 临时任务 在后台执行长时间运行的操作。例如,要异步地在后台执行 long_running_operation,并设置 3600 秒的超时时间 (-B),并且不进行轮询 (-P)

$ ansible all -B 3600 -P 0 -a "/usr/bin/long_running_operation --do-stuff"

要稍后检查作业状态,请使用 async_status 模块,并传递在后台运行原始作业时返回的作业 ID

$ ansible web1.example.com -m async_status -a "jid=488359678239.2844"

Ansible 还可以通过轮询自动检查长时间运行作业的状态。在大多数情况下,Ansible 会在轮询之间保持与远程节点的连接打开。要运行 30 分钟并每 60 秒轮询一次状态

$ ansible all -B 1800 -P 60 -a "/usr/bin/long_running_operation --do-stuff"

轮询模式很智能,因此所有作业都将在任何机器上开始轮询之前启动。如果要快速启动所有作业,请确保使用足够高的 --forks 值。在时间限制 (以秒为单位) 超时 (-B) 后,远程节点上的进程将被终止。

异步模式最适合长时间运行的 shell 命令或软件升级。例如,异步运行 copy 模块不会进行后台文件传输。

异步剧本任务

剧本 也支持异步模式和轮询,并提供简化的语法。你可以在剧本中使用异步模式来避免连接超时或避免阻塞后续任务。剧本中异步模式的行为取决于 poll 的值。

避免连接超时:poll > 0

如果你想为剧本中的某个特定任务设置更长的超时时间限制,请使用 async,并将 poll 设置为正值。Ansible 仍然会阻塞剧本中的下一个任务,等待直到异步任务完成、失败或超时。但是,任务只有在超过使用 async 参数设置的超时时间限制时才会超时。

要避免任务超时,请指定其最大运行时间以及你希望多久轮询一次状态

---

- hosts: all
  remote_user: root

  tasks:

  - name: Simulate long running op (15 sec), wait for up to 45 sec, poll every 5 sec
    ansible.builtin.command: /bin/sleep 15
    async: 45
    poll: 5

注意

默认轮询值由 DEFAULT_POLL_INTERVAL 设置确定。异步时间限制没有默认值。如果你省略了“async”关键字,则任务会同步运行,这是 Ansible 的默认行为。

注意

从 Ansible 2.3 开始,async 不支持检查模式,并且在检查模式下运行时会使任务失败。请参阅 验证任务:检查模式和差异模式,了解如何在检查模式下跳过任务。

注意

当异步任务在启用轮询的情况下完成时,临时异步作业缓存文件 (默认位于 ~/.ansible_async/ 中) 会自动删除。

并发运行任务:poll = 0

如果你想在剧本中并发运行多个任务,请使用 async,并将 poll 设置为 0。当你设置 poll: 0 时,Ansible 会启动任务,然后立即继续执行下一个任务,而不会等待结果。每个异步任务都会一直运行,直到它完成、失败或超时 (运行时间超过其 async 值)。剧本运行会结束,而不会检查异步任务。

要异步运行剧本任务

---

- hosts: all
  remote_user: root

  tasks:

  - name: Simulate long running op, allow to run for 45 sec, fire and forget
    ansible.builtin.command: /bin/sleep 15
    async: 45
    poll: 0

注意

如果你期望在剧本中稍后对这些相同的资源运行其他命令,请不要对需要独占锁的操作 (例如 yum 事务) 指定 0 的轮询值。

注意

使用更高的 --forks 值会导致更快地启动异步任务。这也提高了轮询效率。

注意

在使用 poll: 0 运行时,Ansible 不会自动清理异步作业缓存文件。你需要使用 async_status 模块 (带有 mode: cleanup) 手动清理它。

如果你需要与异步任务进行同步点,可以将其注册以获取其作业 ID,并使用 async_status 模块在后面的任务中观察它。例如

- name: Run an async task
  ansible.builtin.yum:
    name: docker-io
    state: present
  async: 1000
  poll: 0
  register: yum_sleeper

- name: Check on an async task
  async_status:
    jid: "{{ yum_sleeper.ansible_job_id }}"
  register: job_result
  until: job_result.finished
  retries: 100
  delay: 10

注意

如果 async: 的值不够高,这会导致“稍后检查它”的任务失败,因为 async_status: 正在寻找的临时状态文件将不会被写入或不再存在

注意

异步剧本任务始终返回已更改。如果任务使用的是需要用户使用 changed_whencreates 等来注释更改的模块,那么应将其添加到以下 async_status 任务中。

要运行多个异步任务,同时限制并发运行的任务数量

#####################
# main.yml
#####################
- name: Run items asynchronously in batch of two items
  vars:
    sleep_durations:
      - 1
      - 2
      - 3
      - 4
      - 5
    durations: "{{ item }}"
  include_tasks: execute_batch.yml
  loop: "{{ sleep_durations | batch(2) | list }}"

#####################
# execute_batch.yml
#####################
- name: Async sleeping for batched_items
  ansible.builtin.command: sleep {{ async_item }}
  async: 45
  poll: 0
  loop: "{{ durations }}"
  loop_control:
    loop_var: "async_item"
  register: async_results

- name: Check sync status
  async_status:
    jid: "{{ async_result_item.ansible_job_id }}"
  loop: "{{ async_results.results }}"
  loop_control:
    loop_var: "async_result_item"
  register: async_poll_results
  until: async_poll_results.finished
  retries: 30

另请参阅

控制剧本执行:策略等

控制剧本执行的选项

Ansible 剧本

剧本简介

通信

有问题吗?需要帮助吗?想分享你的想法吗?请访问 Ansible 通信指南