Bookmark this page

Chapter 8.  Coordinating Rolling Updates

Abstract

Goal

Use advanced features of Ansible to manage rolling updates in order to minimize downtime, and to ensure the maintainability and simplicity of Ansible Playbooks.

Objectives
  • Run a task for a managed host on a different host, and control whether facts gathered by that task are delegated to the managed host or to the other host.

  • Tune the number of simultaneous connections that Ansible opens to managed hosts, and how Ansible processes groups of managed hosts through the play's tasks.

  • Tune the behavior of the serial directive when batching hosts for execution, abort the play if it fails for too many hosts, and create tasks that run only once for each batch or for all hosts in the inventory.

Sections
  • Delegating Tasks and Facts  (and Guided Exercise)

  • Configuring Parallelism  (and Guided Exercise)

  • Managing Rolling Updates  (and Guided Exercise)

Lab
  • Coordinating Rolling Updates

Delegating Tasks and Facts

Objectives

  • Run a task for a managed host on a different host, and control whether facts gathered by that task are delegated to the managed host or to the other host.

Delegating Tasks

Sometimes, when Ansible is running a play to ensure the correct configuration of a system, it might need to perform one or more tasks on another host on behalf of the managed host. For example, you might need to log in to a network device to change a DHCP configuration, make sure that certain groups exist in an Active Directory domain, or communicate with the API of a service by using tools that are not available on the managed host.

In a play, you can delegate a task to run on another host instead of the current managed host.

A task delegates the action to a host by using the delegate_to directive. This directive points Ansible to the host that executes the task in place of the corresponding target. The host that you delegate the task to does not need to be listed in the Ansible inventory.

The following example runs the uname -a command on each host in the play, and then runs the uname -a command on host.lab.example.com on behalf of each host in the play.

---
- name: Delegation Example
  hosts: demo.lab.example.com
  become: false

  tasks:
    - name: Get system information
      ansible.builtin.command: uname -a
      register: managed_host

    - name: Display demo system information
      ansible.builtin.debug:
        var: managed_host

    - name: Get system information
      ansible.builtin.command: uname -a
      delegate_to: host.lab.example.com
      register: delegated

    - name: Display localhost system information
      ansible.builtin.debug:
        var: delegated

In the following example, the first task is delegated to each of the HAProxy load balancers in the lbservers Ansible group in turn, removing the managed host from all the load balancers. The second task, which is not delegated, then stops the web server on the managed host. Both tasks run for each host in the play.

    - name: Remove the server from HAProxy
      community.general.haproxy:
        state: disabled
        host: "{{ ansible_facts['fqdn'] }}"
        socket: /var/lib/haproxy/stats
      delegate_to: "{{ item }}"
      loop: "{{ groups['lbservers'] }}"

    - name: Make sure Apache HTTPD is stopped
      ansible.builtin.service:
        name: httpd
        state: stopped

You might also delegate tasks to another host when you want to verify access to a service on the managed host currently being processed by Ansible. For example, you might want to ensure that communication from a test client can pass through a host-based firewall running on that host.

    - name: Access the web service
      uri:
        url: http://{{ ansible_facts['fqdn'] }}
        timeout: 5
      delegate_to: test-client.example.com

Delegating to the Execution Environment

One of the most common places you might delegate a task is to the execution environment, or localhost environment. For example, you need to communicate with the API for a controller that you cannot reach from the managed host, but that you can reach from the execution environment, so you delegate the task.

    - name: Get information about controller instance
      ansible.builtin.uri:
        url: https://{{ ansible_facts['fqdn'] }}/api/v2/ping/
        method: GET
        validate_certs: no
        return_content: yes
      delegate_to: localhost
      register: controller_ping

Important

The introduction of automation execution environments in Red Hat Ansible Automation Platform 2 changes how delegation to localhost works. With the ansible-navigator command, using delegate_to: localhost causes the module to run within the automation execution environment container. In earlier versions that used the ansible-playbook command, the module ran directly on the control node, with various consequences.

  • Privilege escalation might fail if the sudo command is not present. The supported automation execution environments provided by Red Hat require the sudo command.

  • If you are reusing a playbook that assumes it can access resources or commands on the control node, those resources might not be available inside the automation execution environment container.

Delegating Facts

In an earlier example, the ansible_facts['fqdn'] fact was used in a task that was delegated to each of the load balancers. In that task, the fully qualified domain name (FQDN) for the managed host is used, not that of the current load balancer.

When you delegate a task, Ansible uses the host variables and facts for the managed host (the current inventory_hostname) for which the task is running. For example, if a task is running for demo, but has been delegated to localhost, then the variables and facts for demo are used. In most cases, it is correct to use the variables and facts for the managed host, even when the task is delegated to another host.

However, sometimes you might want to assign the facts collected by a delegated task to the host to which the task was delegated. To change this setting, set the delegate_facts directive to true.

- name: Delegate Fact Example
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Set a fact in delegated task on demo
      ansible.builtin.set_fact:
        myfact: Where am I set?
      delegate_to: demo.lab.example.com
      delegate_facts: true

    - name: Display the facts from demo.lab.example.com
      ansible.builtin.debug:
        msg: "{{ hostvars['demo.lab.example.com']['myfact'] }}"

The preceding play runs for localhost, but the first task is delegated to demo.lab.example.com. Using the delegate_facts directive on that task instructs Ansible to gather facts into the hostvars['demo.lab.example.com'] namespace for the delegated host, instead of the default hostvars['localhost'] namespace for the current managed host.

Revision: do374-2.2-82dc0d7