In my attempt to promote the usage of Ansible, I have often tried to succinctly explain some of its core concepts. In particular, I’ve tried to communicate a few simple features that I personally have gotten a lot of leverage out of:

  1. Roles
  2. Tags
  3. Host limits

Master these simple concepts and you can do powerful things with Ansible.

Roles can create some confusion when learning Ansible. I’ve always talked about them as an abstraction for task reuse. But that’s not very clear. It’s easier to think of them as reusable tasks executing against hosts in a context. That context can provide files, templates, variables, handlers, that the role’s tasks can refer to and make use of during a play. But remember that, in the end, applying a role to a host is still all about the tasks.

That brings us to two main ways of slicing and dicing the execution of tasks against hosts during a play. This is in particularly useful when you have large inventories, and master playbooks for entire deployments (playbooks that in turn include playbooks for specific services/applications).1 Let’s say you have a master playbook site.yml and can do:

$ ansible-playbook -i prod site.yml 

That’s great because you can quickly deploy your entire system with a single command. It’s also easy to remember. But you rarely need to deploy your entire system.

By mastering the usage of tags and host limits, you can precisely target the execution of specific tasks across your entire deployment. I use that all the time. Visually, this is how I think about it:

All hosts in the play (across the bottom in the graphics above) can have tasks executed against them. By default, all applicable tasks gets executed against all hosts in the play. However, within a given play, exactly which tasks gets executed against which hosts can be controlled using tags and host limits.

If all tasks in your roles have tags with a sensible naming convention, it’s easy to only run tasks in a specific role.2

$ ansible-playbook -i prod site.yml -t "mysql-server"

This play might only be applicable to specific servers anyway. You can easily limit the hosts to be considered with limits, like so:

$ ansible-playbook -i prod site.yml -t "mysql-server" -l "mysql-servers"

Alternatively, consider this example, re-deploying all Tomcat applications:

$ ansible-playbook -i prod site.yml -t "tomcat-war" -l "tomcat-servers"

Now you have limited the play to a specific subset of hosts, and a subset of tasks. This gives you a lot of freedom and control. Below are some more examples that I use all the time.

Pull all Docker images across the deployment infrastructure:

$ ansible-playbook -i prod site.yml -t "docker-images-pull" -l "docker-hosts"

Deploy all Docker containers:

$ ansible-playbook -i prod site.yml -t "docker-containers" -l "docker-hosts"

Re-deploy a specific application on Tomcat:

$ ansible-playbook -i prod site.yml -t "tomcat-war" -l "front-end-applications"

Re-configure your front-end application load balancer:

$ ansible-playbook -i prod site.yml -t "haproxy" -l "front-end-applications"

Spend a few moments learning these concepts. I can guarantee you will find them useful.

  1. As recommended in Ansible’s best practices documentation.

  2. I like to tag all tasks in a role with the name of the role, and then provide additional tags with the role name as a prefix. For example, I might give the following tags to a task in the role mysql-server that updates the configuration: mysql-server, mysql-server-config.