You are currently viewing Ansible – How To Run Tasks Asynchronously Using Async And Poll With Examples

Ansible – How To Run Tasks Asynchronously Using Async And Poll With Examples

In this article, you will learn the difference between running ansible tasks synchronously and asynchronously with examples. You will also learn how to monitor the background process manually as well as within the playbook.

If you wish to practice along you can replicate my lab setup using the following article.

[irp posts=”333″]

Ansible Synchronous Tasks

Ansible by default executes the tasks synchronously. What is synchronous execution? Ansible will execute the task one by one against the target host. The task should be completed in all the hosts for ansible to pick up and run the next tasks. Also, ansible holds the connection between all the managed nodes where the task is running and releases the connection only after certain events occur. Events can be anything like task failure, completion, timeout, etc.

I have a sample playbook that has two tasks. Both tasks do nothing but get the distribution from ansible facts and print it to stdout. In the below image, you can see the first task is executed against all the target hosts post which the second task ran against all target hosts. 

ansible default task execution

Ansible Async & Poll Default Behavior

In asynchronous mode, you can set the tasks to execute in parallel. Asynchronous tasks are very useful when we have a task that is long-running. Let me demonstrate this with an example.

The following playbook has one task that will download the alpine iso image and verify its checksum using the get_url module.

---
- name: Download Alpine iso image
  hosts: anscontroller
  gather_facts: false

  vars:
    src_url: https://dl-cdn.alpinelinux.org/alpine/v3.17/releases/x86/alpine-standard-3.17.1-x86.iso
    csum: https://dl-cdn.alpinelinux.org/alpine/v3.17/releases/x86/alpine-standard-3.17.1-x86.iso.sha256
    dest_path: /home/ansuser/alpine.iso

  tasks:
    - name: Download the iso image
      ansible.builtin.get_url:
        url: "{{ src_url }}"
        dest: "{{ dest_path }}"
        checksum: "sha256:{{ csum }}"

Task like this does not log any output in the stdout. Sometimes it feels like the playbook itself got hung. But these tasks are running in the target host and will only return the status after the task is completed.

Ansible background task

I am executing the same task again but this time with async and poll directives.

tasks:
  - name: Download the iso image
    ansible.builtin.get_url:
      url: "{{ src_url }}"
      dest: "{{ dest_path }}"
      checksum: "sha256:{{ csum }}"
    async: 45
    poll: 15

The async and poll directives accept time in seconds. The async is set to 45 seconds and the poll is set to 15 seconds. Let’s understand the default behavior of async and poll.

  1. Setting the async is a way of telling ansible to fail the task if not completed within the given seconds. The task will fail if it runs past the async time.
  2. When the poll is set, for every N seconds it will see if the task is completed or not. By default, the poll is 10 seconds.

From the below image, you can see the task failed as it runs past the async time of 45 seconds. Also for every 15 seconds poll was checking for the process status and logs the output to stdout.

Ansible Async & Poll

You may now think about how this is different from synchronous execution. It is not. The tasks are still executed synchronously waiting for the current task to complete to run the next set of tasks. All we are doing here is setting a limit for the task to complete or get killed.

Ansible Asynchronous Task Execution

To execute tasks synchronously set the poll directive to zero. Take a look at the below snippet. The first task is the same but with the poll set to zero and the second task will download the checksum file.

tasks:
  - name: Download the iso image
    ansible.builtin.get_url:
      url: "{{ src_url }}"
      dest: "{{ dest_path }}"
    async: 50
    poll: 0

  - name: Downloading the checksum file
    ansible.builtin.get_url:
      url: "{{ csum }}"
      dest: "{{ dest_path }}"

Now the first task will be submitted and the connection will be closed. Ansible will execute the second task. The first task will still be running in the background and will timeout based on the async time. Either you have to manually verify the process completion or you can use the job ID to monitor the task status.

In the below output, you can see the playbook completed its execution but the first task is still running in the background.

ansible async task at background

Ansible Monitor Asynchronous Task Execution Manually

A job ID will be generated for the async tasks and its output will be recorded in the ~/.ansible_async directory with the job ID as the file name.

$ ls -l ~/.ansible_async/total 8
-rw-r--r-- 1 ansuser ansuser   61 Feb  5 10:54 736589023851.824
-rw-r--r-- 1 ansuser ansuser 1314 Feb  5 10:58 994554694852.898

When you submit the task with async and poll the file will be automatically removed on successful completion of the task. But if the task is not successful then you have to manually clean up the file. 

When you run the task in asynchronous mode, you can monitor the task status using this file. Again you have clean the file manually irrespective of its state in asynchronous mode.

You will see the below message in the file when the task gets failed due to a time-out error.

{"msg": "Timeout exceeded", "failed": true, "child_pid": 828}

When the task runs fine then you will see an output similar to the below image. To format the output pipe it to the jq utility. 

$ cat <job-id> | jq .
ansible async file output

Ansible Monitor Asynchronous Task Execution From Playbook

Using the built-in async_status module you can monitor the task execution status. 

  1. The first task output is registered to the variable named alpine_iso. This will store the job id for the task.
  2. In the third task, the async_status module is used, and the output of the job ID is again registered to the variable named result.
  3. Using until directive the status of the task is monitored and it will be retired until the given condition is satisfied. The retries directive is set to 3 and the delay directive is set to 40. This task will be retired three times with an interval of 40 seconds.
[irp posts=”366″]
tasks:       
   - name: Download the iso image
      ansible.builtin.get_url:
        url: "{{ src_url }}"
        dest: "{{ dest_path }}"
      async: 50
      poll: 0
      register: alpine_iso

    - name: Downloading the checksum file
      ansible.builtin.get_url:
        url: "{{ csum }}"
        dest: "{{ dest_path }}"

    - name: Monitor the iso task
      ansible.builtin.async_status:
        jid: "{{ alpine_iso.ansible_job_id }}"
      register: result
      until: result.finished
      retries: 3
      delay: 100
Ansible Monitor async_status

Wrap Up

In this article we have seen what is the difference between synchronous and asynchronous task execution. We have also seen how to run the task asynchronously and monitor it using the log file and from within the playbook.

Leave a Reply

4 × two =