So, for the past week I’ve been using Ansible in anger. Genuine, bare knuckled, actually trying to get shit done with it. Oh yes, I’ve tinkered over the years, nothing serious. You know, to kick the tires. But I never really saw the point. I was pretty happy with puppet. But recently the team I work in decided as a group to adopt Ansible for our provisioning and management tasks. I think that it’s a good choice – for a bunch of reasons – but I don’t really want to go into them here.
EDIT: This is no longer how I feel about Ansible, and probably a lot of the information here is out of date.
What I want to do, is document a little of my journey, and explain what I’ve learned after a week or so of trying to use this thing for reals.
The thing you have to understand about Ansible is that it is not a scripting language. It looks a lot like a scripting language, but it’s not. It can use scripts just fine – you can write your own custom modules in python, and the “tasks” model of Ansible looks a lot like scripting, but if you start doing anything remotely complex in Ansible, you’re going to have a bad time.
Ansible doesn’t replace the scripting tools you have today. It doesn’t stop you from needing to use shell scripts to do things. What it will do, is provide you with a way to orchestrate those tools that you have, so that they work together in a (hopefully) harmonious way.
First, a note on Nomenclature⌗
People get hung up on a lot of nomenclature used with Ansible. It really tripped me up, too.
- Task – A task is a single Idempotent step in a Role or a Playbook. By “Idempotent”, they mean, you should be able to run each task several times and you still get the same result. Previous executions should not effect latter executions.
- Role – In Ansible, a Role is a set of tasks that are applied when a role is included on a host, to perform some peice of work. That’s it. Don’t get hung up on the word. Just think of it as a library of tasks, which can also contains some custom python, all kinds of stuff.The idea is that you write a Role for different components of your application. For example, you might write a role for Sensu, Redis and RabbitMQ. Redis and RabbitMQ roles wouldn’t know anything about each other, and don’t need to. They also don’t know anything about Sensu. But Sensu would need to know where Redis and RabbitMQ are, and what authentication to use, etc. So you’d pass in some variables to the Sensu Role so that when it configures Sensu, it’s able to connect to the resources it needs.If you look at Ansible Galaxy you will find that roles are very strictly defined like this. They tell ansible how, but they don’t tell what.
- Playbook – A playbook is a collection of tasks that are applied against a host, or a group of hosts. Roles, can also be applied during a playbook at any time.
- Inventory – a file or a directory containing many files, that describe your environments. You can have many inventories and those inventories can also be dynamic (for example, with ec2.py) so that you don’t even have to manually maintain them.Inventories define groups of hosts, which are not hierarchical but are more like an overlapping venn diagram of hosts.
So, think of Playbooks as a way to structure your Roles such that the Tasks in the roles can be run over various hosts listed in your Inventory, which configuration taken from your Inventory group or host variables (more on that later).
Playbooks and Inventory tell Ansible what.
Roles tell ansible how.
Structuring your Ansible Project⌗
I recommend that when you start fiddling with Ansible that you make a project with a directory structure like the following. This differs a little form Ansible’s Best Practices document, but there’s a reason for it: I don’t think having all your playbooks scattered in the root is a great idea.
ansible.cfg should contain this:
This will give you slightly better output when there are errors, and it will also make it so that when you run ansible from the project root directory, you will add ./roles as a path to search for roles.
Now, when you develop playbooks, to start with, use the adhoc directory.
Roles obviously go in the roles directory, and inventory can be split up into several subdirectories for production, development, etc.
The Nitty Gritty⌗
So thats really the high level overview. I don’t want to get into the basics of ansible, because that’s really well covered elsewhere. What I want to talk about is the places where I screwed up and spent hours going down a rabbit hole, because I didn’t understand how Ansible works. The documentation isn’t much help either.
Oh, god. You are going to kind of loath these things once you work them out. Here’s the thing. Every “value” in a yaml file can be represented by an expression, like this:
You’ll see this scattered everywhere, and what you may not realise right away is that everything inside the quotes and curly braces is a separate little text templating language called Jinja2.
This means, if you want to do math, or you want to manipulate a string, or you want to add another key/value pair to an ansible map, you need to use a Jinja2 expression.
Often you will have something like this in a vars file or in a group_vars or something like that:
Now, lets say you want to add to that map in Ansible with a new key value pair, then you’d have to do something like this:
<h6> Fact Caching </h6> <p> OK, so I didn’t know about this. But apparently you can have <a href="http://docs.ansible.com/ansible/latest/playbooks_variables.html#fact-caching">ansible store facts in a fact cache</a>. Ok, that’s a little less crap. </p> <h6> Variable Precedence </h6> <p> So, just to make things even more annoying you need to <a href="http://docs.ansible.com/ansible/latest/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable">understand Variable Precedence</a>. </p> <p> The documentation is fine here. Just, you know, you may not notice it until hour 50. Like me. You idiot. </p>