What I learned from a year using Ansible extensively

By Martin Rusev read

In one way or another I have been part of the Devops movement since 2008. The moment I switched from uploading PHP sites with FTP to deploying Rails apps with Capistrano and later Fabric I knew there is no going back. Over the years I switched back and forth between lightweight deployments with Fabric/Capistrano and sophisticated provisioning with Chef and Puppet. For all that time there was always a gap - I wanted a tool that is powerful like Chef and Puppet, but simple just like bash scripts and fab files. This is where Ansible comes in.

I've been using Ansible extensively for over an year for provisioning remote hosts, deploying web apps and even installing applications on my dev machine. I can safely say that if you are looking for flexibility, power and simplicity in your automation tools, there is a big chance you will like what Ansible could do.

Ansible - The Good Parts

Directly compared to Chef, Puppet and Saltstack, Ansible is much easier to grasp. You can pick it up and be productive after a couple of hours experimenting and reading through the documentation. This makes Ansible and easy recommendation for small/medium companies without dedicated sys admins or devops. In my own experience Ansible could be adopted with no resistance in teams with basic Linux familiarity and already used to automatic deployments with Fabric and Capistrano.

Ansible has a great documentation and that, at least in my opinion is a great plus in the open source world where docs are usually an afterthought and can't catch up with the development speed. The Ansible docs are always up to date, including new features. When it comes to detail - the docs are similar to the Python docs(they are awesome) and one thing I particulary like is New in version X.X, which makes it easy to follow the progress of a specific module over time.

The Not So Good Parts

Puppet has manifests, Chef has cookbooks and Ansible follows these concepts and has Playbooks. One of the things that which really bothers me with Ansible is that it doesn't have a good, default out of the box way to test your playbooks in a safe, protected environment without altering your servers state. I eventually found a workaround using Docker(you can read about it here), but it is far from perfect and I would love to see a native way to do this.

My next big issue with Ansible was finding well written and cohesive playbooks. Ansible has the Ansible Galaxy which compared to the awesome Puppet Forge or the Docker Hub looks messy and kind of outsourced. At this point it is very difficult to find good playbooks(or share your own) on Ansible Galaxy.

I personally believe that improving it could lead to a greater Ansible adoption in the future. Especially if the people behind Ansible put some work in the UI and promote high quality playbooks. Even better will be if they provide official playbooks for the most popular libraries like Apache, MySQL, PostgreSQL and show them on the first page, so anyone new to Ansible could safely do ansible-galaxy install mysql

Interesting Use Cases

At its core Ansible is a provisioning automation tool. In most cases you are going to use it to provision your remote servers. Here I want to highlight a couple of edge use cases that I personally think could be interesting and improve your Ansible experience and devops productivity.

Use Case #1: Using Ansible to install the latest and greatest

As a developer I always want to work with the latest stable versions. That could be Nginx 1.8 or PostgreSQL 9.4 or just a package not present in the distro repositories like Fish shell or RethinkDB. Right now the the common solution for this problem is the following:

  1. Want to install Nginx 1.8, Fish shell 2.1, PostgreSQL 9.4 and RethinkDB
  2. Go to nginx.org, click Download, click pre-built packages, find and follow instructions (time 5-10 minutes)
  3. Repeat the same for Fish shell, PostgreSQL and RethinkDB (total time wasted 30 minutes)
  4. Several days later, build a new VM, repeat steps 1-3

With Ansible:

Ansible is just perfect for automating this process, which as developers we do really often. The official repositories rarely change(if ever), so the next time you find yourself doing something like this on more than one ocassion, you can repeat the steps in a playbook instead of the terminal and then just:

ansible-playbook -i 'localhost,' --connection=local nginx/fish/postgres/rethink.yml

If this sounds like an interesting use case, you can take a look at a small package I build, called Edgium. Edgium is a collection of Playbooks for the most popular packages I use like Docker, Ruby(2.X), MariaDB, Git(2.X), PostgreSQL(9.3+) and all it does behind the scenes is:

edgium install git

# This is just a shortcut for 
sh -c "(cd playbooks && git pull origin master)"
ansible-playbook -i 'localhost,' --connection=local git.yml

Use Case #2: Single file playbooks

Most the playbooks you are going to encounter when browsing Ansible Galaxy and Github are separated in multiple folders and files - tasks, roles, vars, handlers and so on. This is great if you have to support multilpe distros, have a lot of variables and templates and so on. By default Ansible forces you to structure your playbooks like this and it took me a couple of months until I found that out that you can actually create a single file playbooks. Single file playbooks are great for simple tasks, like installing a library for example:

- hosts: localhost
  connection: local
  sudo: True
  tasks:
  - 
  name: Add Mongo repository key.
  command: apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
  - 
  name: Add repository - Debian.
  lineinfile: "dest=/etc/apt/sources.list.d/mongodb-org.list create=yes line='deb http://downloads-distro.mongodb.org/repo/debian-sysvinit dist 10gen'"
  when: ansible_distribution == 'Debian'
  - 
  name: Add repository - Ubuntu.
  lineinfile: "dest=/etc/apt/sources.list.d/mongodb-org.list create=yes line='deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen'"
  when: ansible_distribution == 'Ubuntu'
  - 
  name: Update.
  command: apt-get update
  - 
  name: Install MongoDB.
  command: apt-get install -y mongodb-org
  -
  name: Start MongoDB.
  command: service mongod start
ansible-playbook -i 'localhost,' --connection=local mongo.yml

Use Case #3: Testing Ansible Playbooks with Docker

Ansible and Docker are two projects that complement each other very well. I use Ansible to built my Docker containers and I use Docker to test my Ansible Playbooks.

Writing playbooks require a lot of iterations and Docker is the perfect environment where you can test them fast and safe. It gets even more useful if you are using one distro(e.g. Ubuntu 14.04) and want to write playbooks for another(e.g. Debian 7, Ubuntu 12.04, CentOS).

FROM ubuntu:trusty

RUN apt-get update

RUN apt-get install -y software-properties-common
RUN apt-add-repository ppa:ansible/ansible
RUN apt-get update
RUN apt-get install -y git curl ansible

ADD mysql.yml /playbooks/mysql.yml
RUN ansible-playbook /playbooks/mysql.yml -i 'localhost' --conection=local

CMD ["/bin/bash"]

Conclusion

Hopefully this article gave you a general idea how Ansible could be useful as a more general automation tool. Both on your servers and on your desktop distro.

The latest version of Ansible comes with Windows support and I am really hopeful I could automate my Windows experience as well in the very near future.