Lessons from working and playing with things like AWS, Ansible, and others
Ansible layered configuration for AWS
17 Mar 2014
Ansible’s system for managing configuration of hosts is
inventory. The documentation on inventory - both static and dynamic - is a
good reference but I’d like to provide further context through example.
First, some background. Configuring systems and applications can be very simple. Particularly
if you only have one or two. Then you can check the configuration into version control on a per
host basis, and just manage changes as they come. However, when you have applications in the
100s, and hosts in the 1000s, this approach simply does not scale.
So the goal of effective and efficient configuration management becomes similar to writing
good code - Don’t Repeat Yourself, break things down into independent modular units and
manage versions well.
I like to think of configuration as a layered set of properties that are applied to a host
in turn to combine as the eventual result. A production web server will share a number of
properties with its preproduction equivalent (location of files to serve, port to listen on,
etc.) but will also have properties in common with other production application servers
(databases to talk to, subnets to belong to).
In Ansible inventory, such layers can be modelled as groups - that is a production web
server might belong to a production group, a web group and possibly a production-web group
for those properties that are particularly specific (e.g. what virtual hosts to serve)
While this might start to seem like an inheritance tree, it’s actually a Directed Acyclic
Graph (as a group will likely have multiple children - groups or hosts) and can also have
multiple parents - so the production-web group could belong to both production and web
groups.
An example inventory graph for the instance we’ll build below is
Creating an instance in AWS EC2
This is relatively straightforward, and the ec2 module documentation
shows how to do it, but for completeness we’ll describe it here.
This assumes you have set up your AWS connection ready for use
with boto
either in the boto configuration file or environment variables.
A minimal playbook looks like:
With this in mind then, and bringing this around to AWS, we’ll take the example of
an organisation that uses a single AWS region, separate VPCs for preproduction and
production.
Note that while I use dummy image, subnet and security group ids in this documentation,
I haven't obscured the results of the commands run or the contents within the source
repository - except for ssh key.
Also, you'll need to have a copy of YOURKEYNAME.pem - you can use
ec2_key to generate it
or create it in the EC2 console.
inventory/group_vars/all.yml
all is a special group that all hosts in inventory, other than localhost, belong to.
So this is a great place for site defaults.
inventory/group_vars/production.yml
inventory/group_vars/production-web.yml
(This would be more reasonable instance size in real production but this is an example on the cheap!)
inventory/group_vars/production-az-a.yml
inventory/hosts
The production-web-a group shows a useful way of being able to create instances
in a specific availability zone without needing to know how many of them or their
exact names in advance (I know the Cattle Theory suggests that hostnames are a
thing of the past, but they often convey a lot of inventory information!)
I’ve commented out groups that would lead to other paths along the graph for
simplicity’s sake.
create-ec2-instance.yml
And then this gets run with:
Managing the resulting instance
You can simplify your connections to EC2 instances with something like the following in ~/.ssh/config
Managing the resulting instance can then be through Ansible’s
EC2 dynamic inventory
For example, using the ec2_facts
module to get the EC2 facts about an instance can be as simple as using
with the playbook
The source code for the inventory and playbooks is also on Github