Ansible: From Introduction to Abandonment (Part 21)

Asynchronous Operations

Asynchronous operations and polling can be set using <span>async</span> and <span>poll</span>.

Ansible Ad-Hoc Asynchronous, Concurrent, and Querying

Starting Asynchronous Tasks

[root@awx-1 ansible]# ansible localhost -B 3600 -P 5 -a "sleep 30"
localhost | CHANGED =&gt; {
    "ansible_job_id": "j445703768934.50473",
    "changed": true,
    "cmd": [
        "sleep",
        "30"
    ],
    "delta": "0:00:30.003839",
    "end": "2025-04-21 23:31:02.617454",
    "finished": 1,
    "msg": "",
    "rc": 0,
    "results_file": "/root/.ansible_async/j445703768934.50473",
    "start": "2025-04-21 23:30:32.613615",
    "started": 1,
    "stderr": "",
    "stderr_lines": [],
    "stdout": "",
    "stdout_lines": []
}
  • <span>-B</span>: Sets the timeout duration
  • <span>-P</span>: Polling interval for background task execution
    • • When the value is not 0, the task runs in the background, but the foreground does not exit, periodically checking the results
    • • When the value is 0, the foreground will also exit, and the subsequent status can be queried using <span>ansible.builtin.async_status</span> module

Ansible executes synchronously by default. The following table compares synchronous and asynchronous execution:

Feature Default (Foreground Connection) Async Mode (Background Running)
Is SSH connection maintained ✅ Always maintained ❌ Disconnects immediately after executing the command
Does the control node block ✅ Yes ❌ Does not block, returns immediately
Does it occupy connection resources ✅ Yes ❌ No
Is it suitable for long tasks ❌ Prone to timeout and failure ✅ Very suitable
Can status be queried midway ❌ No ✅ Can query using <span>async_status</span>

When using asynchronous task execution, you can increase the number of concurrently executed hosts by using <span>--forks</span>.

Starting Concurrent Tasks

<span>-p</span> set to 0 makes the task a concurrent task.

[root@awx-1 ansible]# ansible localhost -B 3600 -P 0 -a "sleep 30"
localhost | CHANGED =&gt; {
    "ansible_job_id": "j100035338039.50544",
    "changed": true,
    "finished": 0,
    "results_file": "/root/.ansible_async/j100035338039.50544",
    "started": 1
}

Querying Asynchronous Task Information

When the <span>-P</span> parameter is 0, the task runs completely in the background, and you need to query using the <span>ansible.builtin.async_status</span> module:

<span>ansible.builtin.async_status</span> is introduced later.

# Start an asynchronous task
[root@awx-1 ansible]# ansible localhost -B 3600 -P 0 -a "sleep 30"
localhost | CHANGED =&gt; {
    "ansible_job_id": "j594674720907.50900",
    "changed": true,
    "finished": 0,
    "results_file": "/root/.ansible_async/j594674720907.50900",
    "started": 1
}

# Query results
[root@awx-1 ansible]# ansible localhost -m ansible.builtin.async_status -a "jid=j594674720907.50900"
localhost | CHANGED =&gt; {
    "ansible_job_id": "j594674720907.50900",
    "changed": true,
    "cmd": [
        "sleep",
        "30"
    ],
    "delta": "0:00:30.004651",
    "end": "2025-04-21 23:54:24.838790",
    "finished": 1,
    "msg": "",
    "rc": 0,
    "results_file": "/root/.ansible_async/j594674720907.50900",
    "start": "2025-04-21 23:53:54.834139",
    "started": 1,
    "stderr": "",
    "stderr_lines": [],
    "stdout": "",
    "stdout_lines": []
}

# Clean up cache files
[root@awx-1 ansible]# ansible localhost -m ansible.builtin.async_status -a "jid=j594674720907.50900 mode=cleanup"
localhost | SUCCESS =&gt; {
    "ansible_job_id": "j594674720907.50900",
    "changed": false,
    "erased": "/root/.ansible_async/j594674720907.50900",
    "finished": 0,
    "started": 0,
    "stderr": "",
    "stderr_lines": [],
    "stdout": "",
    "stdout_lines": []
}

Asynchronous, Concurrent, and Polling in Playbooks

Asynchronous Playbook Tasks

To set tasks for asynchronous execution, add <span>async</span> and <span>poll</span> in the corresponding Play (note the timeout duration)

- name: sleep 30
  ansible.builtin.command: sleep 30
  poll: 5
  async: 300

Partial execution results:

TASK [sleep 30] **********************************************************************
ASYNC POLL on localhost: jid=j503432663133.51123 started=1 finished=0
ASYNC POLL on localhost: jid=j503432663133.51123 started=1 finished=0
ASYNC POLL on localhost: jid=j503432663133.51123 started=1 finished=0
ASYNC POLL on localhost: jid=j503432663133.51123 started=1 finished=0
ASYNC POLL on localhost: jid=j503432663133.51123 started=1 finished=0
ASYNC OK on localhost: jid=j503432663133.51123
changed: [localhost]

<span>async</span> has no default value, while <span>poll</span> defaults to 15s.

Concurrent Playbook Tasks

When <span>poll</span> is set to 0, the task is a concurrent task:

- name: sleep 30
  ansible.builtin.command: sleep 30
  poll: 0
  async: 300

Here is an example:

root@awx-1 ansible]# ansible-playbook test.yml

PLAY [test] *******************************************************************************************************************

TASK [sleep 30] ***************************************************************************************************************
changed: [localhost]

PLAY RECAP ********************************************************************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@awx-1 ansible]# ps -ef | grep sleep
root       51246   51245  0 00:02 ?        00:00:00 sleep 30
root       51248   46905  0 00:02 pts/2    00:00:00 grep --color=auto sleep

You can see that the Playbook has already finished, but the <span>sleep 30</span> process is still running.

Polling for Concurrent Tasks

ansible.builtin.async_status

When discussing polling for concurrent tasks, we must mention the <span>ansible.builtin.async_status</span> module, which is used to query the status of tasks.

Parameter Name Type Default Value Description
<span>jid</span> <span>str</span> None Required Parameter. The job ID of the asynchronous task to query, obtained from the previous task result <span>.ansible_async</span>.
<span>mode</span> <span>str</span> <span>status</span> Optional values:<span>status</span> or <span>cleanup</span>.<span>status</span>: Default, queries task status;<span>cleanup</span>: Used to clean up task status files.
- name: Asynchronous yum task
  ansible.builtin.yum:
    name: docker-io
    state: present
  async: 1000
  poll: 0
  register: yum_sleeper

- name: Wait for asynchronous job to end
  ansible.builtin.async_status:
    jid: '{{ yum_sleeper.ansible_job_id }}'
  register: job_result
until: job_result.finished
  retries: 100
  delay: 10

- name: Clean up async file
  ansible.builtin.async_status:
    jid: '{{ yum_sleeper.ansible_job_id }}'
    mode: cleanup
  • <span>async</span>: Asynchronous timeout duration
  • <span>poll</span>: Interval for querying task results; when set to 0, it does not query task results, meaning the task runs in the background, and the foreground result ends directly
  • <span>register</span>: Registers the current Play result as a variable for use in subsequent Plays
  • <span>until</span>: The task ends when the condition is met
  • <span>delay</span>: Task retry interval
  • <span>retries</span>: Number of task retries

Let’s discuss the logic of using this module:

  1. 1. <span>Asynchronous yum task</span> Play is used to install the <span>docker-io</span> package, with a timeout of 1000s, executed concurrently. Finally, the task registers a variable named <span>yum_sleeper</span>, which has a sub-variable named <span>ansible_job_id</span>, which is the task ID;
  2. 2. <span>Wait for asynchronous job to end</span> Play uses the task ID obtained from the previously registered variable to query every 10s, with a maximum of 100 retries. It registers a variable named <span>job_result</span>, which has a sub-variable <span>finished</span>; when <span>finished</span> is 1, the task execution is complete;
  3. 3. <span>Clean up async file</span> Play is used to clean up cache files.

Note the value of <span>async</span>; if the value is not high enough, it may cause the task to timeout and exit.

If you want to set options like <span>changed_when</span> or <span>creates</span>, you need to set them for the <span>ansible.builtin.async_status</span>.

Leave a Comment