Setting task execution delegation

In every play we have run so far, we have assumed that all the tasks are executed on each host in the inventory in turn. However, what if you need to run one or two tasks on a different host? For example, we have talked about the concept of automating upgrades on clusters. Logically, however, we would want to automate the entire process, including the removal of each host in turn from the load balancer and its return after the task is completed. 

Although we still want to run our play across our entire inventory, we certainly don't want to run the load balancer commands from those hosts. Let's once again explain this in more detail with a practical example. We'll reuse the two simple host inventories that we used earlier in this chapter:

[frontends]
frt01.example.com
frt02.example.com

Now, to work on this, let's create two simple shell scripts in the same directory as our playbook. These are only examples as setting up a load balancer is beyond the scope of this book. However, imagine that you have a shell script (or other executables) that you can call that can add and remove hosts to and from a load balancer:

  1. For our example, let's create a script called remove_from_loadbalancer.sh, which will contain the following:
#!/bin/sh
echo Removing $1 from load balancer...
  1. We will also create a script called add_to_loadbalancer.sh, which will contain the following:
#!/bin/sh
echo Adding $1 to load balancer...

Obviously, in a real-world example, there would be much more code in these scripts!

  1. Now, let's create a playbook that will perform the logic we outlined here. We'll first create a very simple play definition (you are free to experiment with the serial and max_fail_percentage directives as you wish) and an initial task:
---
- name: Play to demonstrate task delegation
hosts: frontends

tasks:
- name: Remove host from the load balancer
command: ./remove_from_loadbalancer.sh {{ inventory_hostname }}
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost

Notice the task structure—most of it will be familiar to you. We are using the command module to call the script we created earlier, passing the hostname from the inventory being removed from the load balancer to the script. We use the chdir argument with the playbook_dir magic variable to tell Ansible that the script is to be run from the same directory as the playbook.

The special part of this task is the delegate_to directive, which tells Ansible that even though we're iterating through an inventory that doesn't contain localhost, we should run this action on localhost (we aren't copying the script to our remote hosts, so it won't run if we attempt to run it from there).

  1. After this, we add a task where the upgrade work is carried out. This task has no delegate_to directive, and so it is actually run on the remote host from the inventory (as desired):
    - name: Deploy code to host
debug:
msg: Deployment code would go here....
  1. Finally, we add the host back to the load balancer using the second script we created earlier. This task is almost identical to the first:
    - name: Add host back to the load balancer
command: ./add_to_loadbalancer.sh {{ inventory_hostname }}
args:
chdir: "{{ playbook_dir }}"
delegate_to: localhost
  1. Let's see this playbook in action:
$ ansible-playbook -i hosts delegate.yml

PLAY [Play to demonstrate task delegation] *************************************

TASK [Gathering Facts] *********************************************************
ok: [frt01.example.com]
ok: [frt02.example.com]

TASK [Remove host from the load balancer] **************************************
changed: [frt02.example.com -> localhost]
changed: [frt01.example.com -> localhost]

TASK [Deploy code to host] *****************************************************
ok: [frt01.example.com] => {
"msg": "Deployment code would go here...."
}
ok: [frt02.example.com] => {
"msg": "Deployment code would go here...."
}

TASK [Add host back to the load balancer] **************************************
changed: [frt01.example.com -> localhost]
changed: [frt02.example.com -> localhost]

PLAY RECAP *********************************************************************
frt01.example.com : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
frt02.example.com : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Notice how even though Ansible is working through the inventory (which doesn't feature localhost), the load balancer-related scripts are actually run from localhost, while the upgrade task is performed directly on the remote host. This, of course, isn't the only thing you can do with task delegation, but it's a common example of a way that it can help you.

In truth, you can delegate any task to localhost, or even another non-inventory host. You could, for example, run an rsync command delegated to localhost to copy files to remote hosts using a similar task definition to the previous one. This is useful because although Ansible has a copy module, it can't perform the advanced recursive copy and update functions that rsync is capable of.

Also, note that you can choose to use a form of shorthand notation in your playbooks (and roles) for delegate_to, called local_action. This allows you to specify a task on a single line that would ordinarily be run with delegate_to: localhost added below it. Wrapping this all up into a second example, our playbook will look as follows:

---
- name: Second task delegation example
hosts: frontends

tasks:
- name: Perform an rsync from localhost to inventory hosts
local_action: command rsync -a /tmp/ {{ inventory_hostname }}:/tmp/target/

The preceding shorthand notation is equivalent to the following:

tasks:
- name: Perform an rsync from localhost to inventory hosts
command: rsync -a /tmp/ {{ inventory_hostname }}:/tmp/target/
delegate_to: localhost

If we run this playbook, we can see that local_action does indeed run rsync from localhost, enabling us to efficiently copy whole directory trees across to remote servers in the inventory:

$ ansible-playbook -i hosts delegate2.yml

PLAY [Second task delegation example] ******************************************

TASK [Gathering Facts] *********************************************************
ok: [frt02.example.com]
ok: [frt01.example.com]

TASK [Perform an rsync from localhost to inventory hosts] **********************
changed: [frt02.example.com -> localhost]
changed: [frt01.example.com -> localhost]

PLAY RECAP *********************************************************************
frt01.example.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
frt02.example.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

This concludes our look at task delegation, although as stated, these are just two common examples. I'm sure you can think up some more advanced use cases for this capability. Let's continue looking at controlling the flow of Ansible code by proceeding, in the next section, to look at the special run_once option.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.111.183