- Practical Ansible 2
- Daniel Oh James Freeman Fabio Alessandro Locati
- 1612字
- 2021-06-24 16:06:51
Adding host and group variables to your inventory
We have already touched upon host variables—we saw them earlier in this chapter when we used them to override connection details such as the user account to connect with, the address to connect to, and the port to use. However, there is so much more you can do with Ansible and inventory variables, and it is important to note that they can be defined not only at the host level but also at the group level, which again provides you with some incredibly powerful ways in which to efficiently manage your infrastructure from one central inventory.
Let's build on our previous three-tier example and suppose that we need to set two variables for each of our two frontend servers. These are not special Ansible variables, but instead are variables entirely of our own choosing, which we will use later on in the playbooks that run against this server. Suppose that these variables are as follows:
- https_port, which defines the port that the frontend proxy should listen on
- lb_vip, which defines the FQDN of the load-balancer in front of the frontend servers
Let's see how this is done:
- We could simply add these to each of the hosts in the frontends part of our inventory file, just as we did before with the Ansible connection variables. In this case, a portion of our INI-formatted inventory might look like this:
[frontends]
frt01.example.com https_port=8443 lb_vip=lb.example.com
frt02.example.com https_port=8443 lb_vip=lb.example.com
If we run an ad hoc command against this inventory, we can see the contents of both of these variables:
$ ansible -i hostvars1-hostgroups-ini frontends -m debug -a "msg=\"Connecting to {{ lb_vip }}, listening on {{ https_port }}\""
frt01.example.com | SUCCESS => {
"msg": "Connecting to lb.example.com, listening on 8443"
}
frt02.example.com | SUCCESS => {
"msg": "Connecting to lb.example.com, listening on 8443"
}
This has worked just as we desired, but the approach is inefficient as you have to add the same variables to every single host.
- Luckily, you can assign variables to a host group as well as to hosts individually. If we edited the preceding inventory to achieve this, the frontends section would now look like this:
[frontends]
frt01.example.com
frt02.example.com
[frontends:vars]
https_port=8443
lb_vip=lb.example.com
Notice how much more readable that is? Yet, if we run the same command as before against our newly organized inventory, we see that the result is the same:
$ ansible -i groupvars1-hostgroups-ini frontends -m debug -a "msg=\"Connecting to {{ lb_vip }}, listening on {{ https_port }}\""
frt01.example.com | SUCCESS => {
"msg": "Connecting to lb.example.com, listening on 8443"
}
frt02.example.com | SUCCESS => {
"msg": "Connecting to lb.example.com, listening on 8443"
}
- There will be times when you want to work with host variables for individual hosts, and times when group variables are more relevant. It is up to you to determine which is better for your scenario; however, remember that host variables can be used in combination. It is also worth noting that host variables override group variables, so if we need to change the connection port to 8444 on the frt01.example.com one, we could do this as follows:
[frontends]
frt01.example.com https_port=8444
frt02.example.com
[frontends:vars]
https_port=8443
lb_vip=lb.example.com
Now if we run our ad hoc command again with the new inventory, we can see that we have overridden the variable on one host:
$ ansible -i hostvars2-hostgroups-ini frontends -m debug -a "msg=\"Connecting to {{ lb_vip }}, listening on {{ https_port }}\""
frt01.example.com | SUCCESS => {
"msg": "Connecting to lb.example.com, listening on 8444"
}
frt02.example.com | SUCCESS => {
"msg": "Connecting to lb.example.com, listening on 8443"
}
Of course, doing this for one host alone when there are only two might seem a little pointless, but when you have an inventory with hundreds of hosts in it, this method of overriding one host will suddenly become very valuable.
- Just for completeness, if we were to add the host variables we defined previously to our YAML version of the inventory, the frontends section would appear as follows (the rest of the inventory has been removed to save space):
frontends:
hosts:
frt01.example.com:
https_port: 8444
frt02.example.com:
vars:
https_port: 8443
lb_vip: lb.example.com
Running the same ad hoc command as before, you can see that the result is the same as for our INI-formatted inventory:
$ ansible -i hostvars2-hostgroups-yml frontends -m debug -a "msg=\"Connecting to {{ lb_vip }}, listening on {{ https_port }}\""
frt01.example.com | SUCCESS => {
"msg": "Connecting to lb.example.com, listening on 8444"
}
frt02.example.com | SUCCESS => {
"msg": "Connecting to lb.example.com, listening on 8443"
}
- So far, we have covered several ways of providing host variables and group variables to your inventory; however, there is another way that deserves special mention and will become valuable to you as your inventory becomes larger and more complex.
Right now, our examples are small and compact and only contain a handful of groups and variables; however, when you scale this up to a full infrastructure of servers, using a single flat inventory file could, once again, become unmanageable. Luckily, Ansible also provides a solution to this. Two specially-named directories, host_vars and group_vars, are automatically searched for appropriate variable content if they exist within the playbook directory. We can test this out by recreating the preceding frontend variables example using this special directory structure, rather than putting the variables into the inventory file.
Let's start by creating a new directory structure for this purpose:
$ mkdir vartree
$ cd vartree
- Now, under this directory, we'll create two more directories for the variables:
$ mkdir host_vars group_vars
- Now, under the host_vars directory, we'll create a file with the name of our host that needs the proxy setting, with .yml appended to it (that is, frt01.example.com.yml). This file should contain the following:
---
https_port: 8444
- Similarly, under the group_vars directory, create a YAML file named after the group to which we want to assign variables (that is, frontends.yml) with the following contents:
---
https_port: 8443
lb_vip: lb.example.com
- Finally, we will create our inventory file as before, except that it contains no variables:
loadbalancer.example.com
[frontends]
frt01.example.com
frt02.example.com
[apps]
app01.example.com
app02.example.com
[databases]
dbms01.example.com
dbms02.example.com
Just for clarity, your final directory structure should look like this:
$ tree
.
├── group_vars
│ └── frontends.yml
├── host_vars
│ └── frt01.example.com.yml
└── inventory
2 directories, 3 files
- Now, let's try running our familiar ad hoc command and see what happens:
$ ansible -i inventory frontends -m debug -a "msg=\"Connecting to {{ lb_vip }}, listening on {{ https_port }}\""
frt02.example.com | SUCCESS => {
"msg": "Connecting to lb.example.com, listening on 8443"
}
frt01.example.com | SUCCESS => {
"msg": "Connecting to lb.example.com, listening on 8444"
}
As you can see, this works exactly as before, and without further instruction, Ansible has traversed the directory structure and ingested all of the variable files.
- If you have many hundreds of variables (or need an even finer-grained approach), you can replace the YAML files with directories named after the hosts and groups. Let's recreate the directory structure but now with directories instead:
$ tree
.
├── group_vars
│ └── frontends
│ ├── https_port.yml
│ └── lb_vip.yml
├── host_vars
│ └── frt01.example.com
│ └── main.yml
└── inventory
Notice how we now have directories named after the frontends group and the frt01.example.com host? Inside the frontends directory, we have split the variables into two files, and this can be incredibly useful for logically organizing variables in groups, especially as your playbooks get bigger and more complex.
The files themselves are simply an adaptation of our previous ones:
$ cat host_vars/frt01.example.com/main.yml
---
https_port: 8444
$ cat group_vars/frontends/https_port.yml
---
https_port: 8443
$ cat group_vars/frontends/lb_vip.yml
---
lb_vip: lb.example.com
Even with this more finely divided directory structure, the result of running the ad hoc command is still the same:
$ ansible -i inventory frontends -m debug -a "msg=\"Connecting to {{ lb_vip }}, listening on {{ https_port }}\""
frt01.example.com | SUCCESS => {
"msg": "Connecting to lb.example.com, listening on 8444"
}
frt02.example.com | SUCCESS => {
"msg": "Connecting to lb.example.com, listening on 8443"
}
- One final thing of note before we conclude this chapter is if you define the same variable at both a group level and a child group level, the variable at the child group level takes precedence. This is not as obvious to figure out as it first sounds. Consider our earlier inventory where we used child groups to differentiate between CentOS and Ubuntu hosts—if we add a variable with the same name to both the ubuntu child group and the frontends group (which is a child of the ubuntu group) as follows, what will the outcome be? The inventory would look like this:
loadbalancer.example.com
[frontends]
frt01.example.com
frt02.example.com
[frontends:vars]
testvar=childgroup
[apps]
app01.example.com
app02.example.com
[databases]
dbms01.example.com
dbms02.example.com
[centos:children]
apps
databases
[ubuntu:children]
frontends
[ubuntu:vars]
testvar=group
Now, let's run an ad hoc command to see what value of testvar is actually set:
$ ansible -i hostgroups-children-vars-ini ubuntu -m debug -a "var=testvar"
frt01.example.com | SUCCESS => {
"testvar": "childgroup"
}
frt02.example.com | SUCCESS => {
"testvar": "childgroup"
}
It's important to note that the frontends group is a child of the ubuntu group in this inventory (hence, the group definition is [ubuntu:children]), and so the variable value we set at the frontends group level wins as this is the child group in this scenario.
By now, you should have a pretty good idea of how to work with static inventory files. No look at Ansible's inventory capabilities is complete, however, without a look at dynamic inventories, and we shall do exactly this in the next section.