In my previous article about ansible, I gave a very basic introduction to it. After reading it, you were able to create your own playbooks and apply them to a single (group of) server(s). However, ansible is much more powerful than that. It can do a lot of things with groups and individual hosts, making it easier to apply your script to a whole bunch of servers at the same time. Even if them have slightly (or completely) different configurations.
I’m going to show you more about two things this time: the hosts file and the group_vars and host_vars. All these things contribute to having more and better control when applying your playbook(s) to several (or a lot of) servers.
The hosts file
The hosts file is the file where you define the hosts and groups of host you are going to target. In the previous guide, we created a hosts file that looked like this:
[example]
192.0.2.101
192.0.2.102
But that was just one group. You can easily define more groups the same way you defined the first:
[group1]
192.0.2.101
192.0.2.102[group2]
192.0.2.201
192.0.2.202
This allows for you to group similar servers together, like your nameservers, your web servers and your database servers. But what if you want to address all of these at the same time? Well, there’s the ‘all’ handler for that. When you want to address all servers in your playbook, you should make your playbook like this:
– hosts: all
user: mpkossen
sudo: yes
But what if you just want to address certain groups? That’s where groups with groups come in. In ansible, you can create groups of groups by using the ‘:children’ handler. When you create a group with that handler, all groups you list under it are included in the group:
[production:children]
webservers
databaseservers[webservers]
192.0.2.101
192.0.2.102[databaseservers]
192.0.2.201
192.0.2.202
As you see, you can just refer to groups by their group name. This way you can compose different groups for different playbooks. In the playbook you call the group you defined with the ‘:children’ handler but without the actual handler:
– hosts: production
user: mpkossen
sudo: yes
This will run on both the webservers and databaseservers groups. But instead of creating a group with children, you can also apply a playbook to multiple groups at once:
– hosts: webservers:databaseservers
user: mpkossen
sudo: yes
If you want to define a lot of servers easily, there’s a shortcut to help you. Say you name all your nameservers like nsX (ns1, ns2, ns3, and so on). It’s a pain if you have to add them all. So here’s how you solve that in the hosts file:
[nameservers]
ns[1:7].example.net
This will include ns1.example.net, ns2.example.net, ns3.example.net and to on up to ns7.example.net.
In the playbook, simply use:
– hosts: nameservers
user: mpkossen
sudo: yes
As you can see, you can really make this as bold as you want, there’s no limit on the number of servers. Do note that the more servers you add, the more servers your playbook is going to get applied to and the longer it will generally take for the ansible run to complete. There are some tricks there as well, but that’s for another guide.
Finally, let’s look at the following situation: you have 100 servers doing the same thing and you want to edit their configuration. But you don’t want to apply the changes to all at once, as you’d like to see how it runs on several servers in production first. Ansible has a trick for that:
ansible-playbook playbook.yml –limit webservers[0-9]
What we’ve done here is basically tell ansible to limit the number of hosts to apply the changes to. It just grabs the first 10 hosts and applies the playbook to those. Then it stops. The ‘–limit’ parameters accepts all kinds of patterns and even regular expressions. Next run, you can go on where you left off:
ansible-playbook playbook.yml –limit webservers[10-99]
That’s how easy it is!
I’ve now shown you a couple of examples of how to use the hosts file optimally for multiple situations. Combine all these together and you can really build a powerful hosts file which is suitable for many situations and playbooks.
Using host_vars and group_vars
As soon as you have defined a host, special functionality becomes available in ansible. You can define custom variables for each group and host that you define. These variables are known as group_vars for groups and host_vars for hosts. Any variables that you define for a host or a group can be used in both playbooks and templates.
Both group_vars and host_vars are defined in their own folders, ‘groups_vars’ and ‘host_vars’, respectively. For group_vars, the file must be named exactly the same as the group. For host_vars, the file has to be named exactly the same as the host. I’m going to show you an example of both.
Say you have a group ‘nameservers’ and you want to define a variable for all of them. Create an empty file first in the group_vars folder in the root of your ansible directory (where you put you playbooks):
group_vars/nameservers
Then add a variable to the file:
master_server=ns1
This makes the variable ‘master_server’ available to all playbooks that run on this group. I’ll show you how to use these in your playbook in a bit.
host_vars are similar to this. Create a file first:
host_vars/ns1.example.net
And add a variable:
master_server=yes
A nice feature of these group_vars and host_vars is the usage of them in ‘only_if’. ‘only_if’ can be used in playbooks to indicate an action must only be executed if a certain condition is met. A condition is usually dependent upon a variable. That variable can be a group_var or a host_var. Let’s give you an example.
Say you have one nameserver which is your master server and you want to execute a certain action in a playbook just on that server. The following is an action you could use, using a group_var definition:
– name: restart bind
action: service name=bind9 state=restarted
only_if: “$ansible_hostname == $master_server”
What we did here, is use two variables. The first one is a variable that ansible collects itself, in this case the hostname (not the FQDN hostname) variable ‘ansible_hostname’. The hostname is always available as a variable. We compare it with the group_var ‘master_server’ and if they match, the action gets executed.
You can also do this with host_vars:
– name: restart bind
action: service name=bind9 state=restarted
only_if: “$master_server == ‘yes'”
This action only gets executed if the ‘master_server’ variable is set to ‘yes’, which is only the case for ns1.example.net.
This is just one use for variables. They are a really powerful instrument. You can also use these (and other) variables in templates, but that’s something I’ll cover in another tutorial!
Final note
I’ve shown you a little bit more about ansible in this tutorial. There will definitely be more in the future! Again, ansible has really good documentation available: http://docs.ansible.com/
For now, enjoy ansible just a bit more!
Related Posts:
- How to Rapidly Install Java, OpenJDK & Oracle JDK on your VPS - December 14, 2015
- It’s been a great ride - December 14, 2015
- Cheap Windows VPS – $21/quarter 1GB KVM-based Windows VPS in 11 worldwide locations - November 30, 2015
I use fabric.
Maarten, thank your for your tutorials, they are quite useful.
Would you mind adding a tag to them, so it would be easier to read the whole series at once?
Thanks!
We’ll look into grouping them some way or the other. I’ve asked Liam how he likes to group them.
Under simple tag “Tutorials” maybe? :)
That would be really great!
Thanks for the nice article; gives a good introduction to the usages of ansible host_vars and group_vars
I think you are using Ansible 1.1 template format not latest jinja 1.2. for example your group variables should read:
master_server: ns1
not
master_server=ns1
Thanks for this great tutorial, going to try to apply to my 3 servers.
Thanks, nice post