Ansible - How to sequentially execute playbook for each host - ansible

I am using ansible to script a deployment for an API. I would like this to work sequentially through each host in my inventory file so that I can fully deploy to one machine at a time.
With the out box behaviour, each task in my playbook is executed for each host in the inventory file before moving on to the next task.
How can I change this behaviour to execute all tasks for a host before starting on the next host? Ideally I would like to only have one playbook.
Thanks

Have a closer look at Rolling Updates:
What you are searching for is
- hosts: webservers
serial: 1
tasks:
- name: ...

Using the --forks=1 specify number of parallel processes to use (default=5)

Strategy enable to parallel tasks in a per host basis. See https://docs.ansible.com/ansible/latest/user_guide/playbooks_strategies.html
There are 3 strategies: linear (the default), serial and free (quickest)
- hosts: all
strategy: free
tasks:
...

Related

Is there an ansible strategy that takes host groups into account?

We use an inventory file and ansible. We have many micro services and sometimes we have different instances for internal and external use. AFAIK, the builtin ansible strategies (free, host_pinned, serial) ignore the groups. Is there a strategy that works like host_pinned but on a per group basis?
Example inventory:
[my-service-external]
my-external-hosts[01:57].example.com
[my-service-internal]
my-internal-hosts[1:3].example.com
[my-service:children]
my-service-external
my-service-internal
Example playbook.yml:
---
- name: ping
hosts: my-service
strategy: host_pinned
serial:
- 1
- "30%"
tasks:
- name: ping
ansible.builtin.ping:
If this playbook would do something that would actually impact the service, like restarting some application server, all internal hosts would go down at pretty much the same time and possibly cause an outage.

How to configure apache vhosts in parallel with Ansible

Problem
I'm configuring apache vhosts with ansible. It takes a lot of time to create the configs.
Version info:
ansible-playbook 2.7.10
Apache/2.4.29
All vhosts are on the same server.
I'm using the file system structure that Apache suggests:
One file per site (vhost and used port) which will be saved to sites-available/443_example.com.conf. Then I generate a symlink to sites-enabled.
Sequentially running the tasks for the configuration takes about 5 minutes for 34 vhosts if there are no changes.
I created a role for the apache configuration. I identified 13 tasks that have to be run for every vhost:
openssl_privatekey
openssl_csr
openssl_certificate
6 tasks: place the files on the correct places in the file system but only if there is no Let's Encrypt configuration present
create template
enable template
2 tasks: delete old config
For example writing the config template, generate a self signed certificate.
It would be nice if I can parallelize these vhosts. I grouped the tasks I want to run in parallel in the file parallel.yml. The content of this file has to be processed in the correct order.
Solutions
These are the solutions I tried but none of them worked:
1) use async on every task:
I'm using a template in parallel.yml and this cannot be asynced.
2) use one async while including tasks:
- name: 'configure vhost'
include_tasks: parallel.yml
loop: "{{ vhosts }}"
async: 60
There is a know issue that makes Ansible ignore the the async. The loop items are processed serial https://github.com/ansible/ansible/issues/22716
3) use "delegate to" to create more forks of Ansible:
- name: 'configure vhost'
include_tasks: parallel.yml
loop: "{{ vhosts }}"
delegate_to: ansible#example.com
The loop items are processed serial https://github.com/ansible/ansible/issues/37995
4) use strategy: free
"A second strategy ships with Ansible - free - which allows each host to run until the end of the play as fast as it can." https://docs.ansible.com/ansible/latest/user_guide/playbooks_strategies.html
strategy: free allows multiple hosts to be run in parallel. That doesn't work on one host only.
5) increase forks
"Since Ansible 1.3, the fork number is automatically limited to the number of possible hosts at runtime," https://docs.ansible.com/ansible/2.4/intro_configuration.html#forks
Same problem as before.
Summary
How can I run the tasks in parallel?
How can I improve the performance?

Ansible - run plays on continously instead of linear

I have a list of hosts and roles to apply on each. They are hosted on a hypvervisor (XenServer / ESX).
I'm trying to revert each machine back to snapshot before executing the ansible plays. My flow of actions looks like this:
Take VM name from a file (machines.txt)
Connect the Hypervisor and through CLI revert the machine snapshot
Once SSH is up for the machine, connect and execute rest of the plays
Execute one machine at a time.
The best I came so far is executing the revert task on the hypervisor in one play, and then a different play to apply the roles. So that's how my playbook looks like:
---
- name: Reverting
hosts: xenhost
tasks:
- include_tasks: tasks/vms/revert_snap.yml
with_lines: cat "machines.txt"
- name: Ensure NTP is installed
hosts: temp
roles:
- ntp
The solution I've found so far is,
Execute a task of reverting snapshot from CLI and execute it on the Hypervisor
Execute my whole maintenance play on the machines
2.1 Wait for SSH on each
2.2 Apply roles
Clean up and take new snapshots on the Hypervisor
The drawback I have in this scenario is, first it reverts ALL machines, then apply all the roles and then reverting all of them in bulk.
I can't combine the tasks into a single play because they are executed on different hosts.
Any better solutions? - which will revert one machine, apply all roles, take snapshot, then continue to the next machine.
Any better solutions? - which will revert one machine, apply all roles, take snapshot, then continue to the next machine.
you could group all those tasks in a role, and execute the role with serial: 1
- hosts: hostgroup
serial: 1
roles:
- {role: rolename }

Ansible group variable evaluation with local actions

I have an an Ansible playbook that includes a role for creating some Azure cloud resources. Group variables are used to set parameters for the creation of those resources. An inventory file contains multiple groups which reference that play as a descendant node.
The problem is that since the target is localhost for running the cloud actions, all the group variables are picked up at once. Here is the inventory:
[cloud:children]
cloud_instance_a
cloud_instance_b
[cloud_instance_a:children]
azure_infrastructure
[cloud_instance_b:children]
azure_infrastructure
[azure_infrastructure]
127.0.0.1 ansible_connection=local ansible_python_interpreter=python
The playbook contains an azure_infrastructure play that references the actual role to be run.
What happens is that this role is run twice against localhost, but each time the group variables from cloud_instance_a and cloud_instance_b have both been loaded. I want it to run twice, but with cloud_instance_a variables loaded the first time, and cloud_instance_b variables loaded the second.
Is there anyway to do this? In essence, I'm looking for a pseudo-host for localhost that makes it think these are different targets. The only way I've been able to workaround this is to create two different inventories.
It's a bit hard to guess how you playbook look like, anyway...
Keep in mind that inventory host/group variables are host-bound, so any host always have only one set of inventory variables (variables defined in different groups overwrite each other).
If you want to execute some tasks or plays on your control machine, you can use connection: local for plays or local_action: for tasks.
For example, for this hosts file:
[group1]
server1
[group2]
server2
[group1:vars]
testvar=aaa
[group2:vars]
testvar=zzz
You can do this:
- hosts: group1:group2
connection: local
tasks:
- name: provision
azure: ...
- hosts: group1:group2
tasks:
- name: install things
apk: ...
Or this:
- hosts: group1:group2
gather_facts: no
tasks:
- name: provision
local_action: azure: ...
- name: gather facts
setup:
- name: install things
apk:
In this examples testvar=aaa for server1 and testvar=zzz for server2.
Still azure action is executed from control host.
In the second example you should turn off fact gathering and call setup manually to prevent Ansible from connecting to possibly unprovisioned servers.

Checking which hosts failed a playbook in ansible

I have 2 playbooks running on ansible, one after another. After playbook 1 finishes, I want to run the second one on only the hosts for which the first playbook fully succeeded. Looking through the ansible docs, I can't find any accessible info on which hosts failed a specific playbook. How could this be done?
FYI I need separate playbooks because the second one must be run with in serial, which is only available at the playbook level
Where all hosts - successful hosts = failed hosts, you can use the following task to get the difference between the two special variables for all hosts in the play (including failed hosts) and all hosts that have not yet failed. Use of serial will affect the result.
- name: Print play hosts that failed
debug:
msg: "The hosts that failed are {{ ansible_play_hosts_all| difference(ansible_play_batch) |join('\n') }}"
Source: https://docs.ansible.com/ansible/latest/reference_appendices/special_variables.html
Honestly, the best way is to have some queryable state on each host. A simple method is to check for a file's existence, which is created after your first playbook succeeds. You can then have a task which checks for that state and notifies a notify task that it has been "updated", which will get what you want.
In an aside, I stopped using ansible because it wasn't configurable enough; I also had issues getting the parallelism controls I wanted. You my try hitting up the Ansible Project Google Group to put in a feature suggestion or describe your use case.
There is a difference between a play and a playbook. The serial argument is available on the play level. A playbook may contain multiple plays.
---
- name: Play 1
hosts: some_hosts
tasks:
- debug:
- name: Play 2
hosts: some_hosts
serial: 1
tasks:
- debug:
...
Hosts which failed in play 1 will not be processed in play 2.
If your really want to have separate playbooks, I see two options:
Create a callback plugin. You can register a function which gets fired when a task or host fails. You can store this info then locally and use in the next playbook run.
Activate Ansible logging. It will log pretty much the same stuff you see as raw output when running ansible-playbook.
Second option is bit ugly, but easier than creating a callback plugin.
It both cases you then need to create a dynamic inventory script which checks previously saved data and returns valid hosts. Or return all hosts but set a property to mark those hosts. You then can use group_by to create an ad-hoc group.

Resources