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.
Related
In our deployment strategy, our playbooks take on the following structure:
workflow.yml
- hosts: host1
tasks:
- name: setup | create virtual machines on host1
include_tasks: setup.yml
- name: run | import a playbook that will target new virtual machines
include: "virtual_machine_playbook/main.yml"
- hosts: host1
tasks:
- name: cleanup | destroy virtual machines on host1
include_tasks: destroy.yml
virtual_machine_playbook/main.yml
- hosts: newCreatedVMs
roles:
- install
- run
Which works great most of the time. However if for some reason, the virtual_machine_playbook/main.yml errors out, the last hosts block does not run and we are required to manually destroy our VMs. I wanted to know if there was a way to mandate that each hosts block run, regardless of what happens before it.
Other Notes:
The reason that we structure our playbooks this way is because we would like everything to be as contained as possible. Variables are created in each hosts block that are rather important to the ones that follow. Splitting them out into separate files and invocations is something we have not had much success with
We have tried the standard ansible approach for error handling as found here, but most of the options only apply at the task level (blocks, ignore_errors, etc.)
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.
Let's say we have 3 services and 5 servers and
Service 1 should run on all servers.
Service 2 should run on server 1, 2 and 3.
Service 3 should run on server 4 and 5.
I could think of a few dirty ways to accomplish that, but I'd like to know what the best-practice for that scenario would be.
I was thinking that it might be a good idea to first define a group for each service that specifies the hosts that it should be deployed on in the inventory file:
[service-1]
server-[01:05]
[service-2]
server-[01:03]
[service-3]
server-[04:05]
To get the services on the target machines I would use copy or template.
And for actually starting services I found this.
But how would I make the services start on only those machines that they are supposed to run on?
I guess I could create multiple playbooks to achieve that, but I would really prefer to put everything into one playbook.
A playbook can have multiple plays. Therefore you indeed can use those groups to archive your goal and still only have one playbook:
- name: Setup service 1
hosts: service-1
tasks/roles: ...
- name: Setup service 2
hosts: service-2
tasks/roles: ...
- name: Setup service 3
hosts: service-3
tasks/roles: ...
Alternatively you can filter you roles/tasks with conditions like so:
- name: Setup all service
hosts: all
roles:
- role: service1
when: "'service-1' in group_names"
- role: service2
when: "'service-2' in group_names"
- role: service3
when: "'service-3' in group_names"
group_names is a magic variable and holds all the groups the current host belongs to.
I have a script that setups all the servers. Now trying to figure out a good way to configure them to talk to each other. e.g. Configure the application server to talk to a particular database server.
Test app 1
db01
app01
app02
mem01
Test app 2
db02
app03
mem02
The only thing I could come up with a role that takes the servers as params but I dislike that I have to also specify the hosts twice.
- name: Test app 1
hosts: [db01, app01, app02, mem01]
roles:
- {role: app, db: db01, ap: [app01, app02], mem: mem01}
How organized is your inventory file?
Looking at what you posted, this might be a good inventory file organization for you:
[testapp1-dbServers]
db01
[testapp1-appServers]
app01
app02
[testapp1-memServers]
mem01
[testapp2-dbServers]
db02
[testapp2-appServers]
app03
[testapp2-memServers]
mem02
[testapp1:children]
testapp1-dbServers
testapp1-appServers
testapp1-memServers
[testapp2:children]
testapp2-dbServers
testapp2-appServers
testapp2-memServers
[dbServers:children]
testapp1-dbServers
testapp2-dbServers
[appServers:children]
testapp1-appServers
testapp2-appServers
[memServers:children]
testapp1-memServers
testapp2-memServers
This might be overkill if you have no plans to increase the number of servers in any of the first 6 buckets, but it allows you to do things like group_vars files (individual ones for some or all groupings - testapp1, testapp2, dbServers, etc) and clean up your playbook file:
- name: Test app 1
hosts: testapp1 (all vars passed via group_vars file)
roles:
- generic_server_setup
- name: DB Server setup
hosts: dbServers (all vars passed via group_vars file)
roles:
- install_postgres
- other_db_things
The final thing that will help you the most can be found here.
Specifically, getting access to all the groups the current host is in and all the hosts in a group.
QUICK FIX: If you want to sacrifice some organization because you aren't worried about scaling and are not annoyed by the same information being in multiple locations, just add the relevant hosts as vars to testapp1 and testapp2 files under group_vars.
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:
...