Running Ansible Inside Docker

At Healthcare Blocks, we use Ansible and Docker to build, deploy, and run customer application environments on top of Amazon Web Services. By convention, Ansible is normally installed right on top of the operating system layer. This post discusses an alternate strategy of deploying Ansible inside a standalone Docker image, which can then be used to control other machines and containers. This approach brings numerous benefits to operational, testing, and continuous integration processes.

Ansible — in contrast to other configuration management frameworks — does not require the installation of any agents within managed environments. Instead, commands are pushed over SSH and interpreted by a Python runtime. This simple, yet efficient model enables new environments to be spun up faster and greatly reduces the amount of dependencies required on the target machine in order for it to be configured by Ansible.

The environment where Ansible is installed — the “control machine” — is not as simple, however, to setup. It requires specific Python libraries, their system dependencies, and the tuning of configuration settings. As new Ansible modules are added or improved, other dependencies might be required.

Ansible is also a fast paced open source project with over a thousand contributors. Evaluating a recent release or working from the development branch can be quite the adventure and often involves toggling between specific Git repo tags or commits, which might, in turn, require rolling back a dependency too.

In a development environment, Ansible “playbooks” can be tested within a virtual machine using tools such as Vagrant and VirtualBox. Ideally, we want to start with a fresh environment state when running a new playbook. But the process of (re)creating a pristine testing sandbox is tedious with these tools.

So to sum up so far, we’ve got issues with dependency management, challenges when evaluating newer versions of Ansible, and an inefficient testing flow. Docker, anyone?

By creating an Ansible Docker image, like this one, we isolate all of the required dependencies, instead of thrashing around the host machine’s operating system and potentially breaking things in other areas. The Dockerfile declares a specific entrypoint, enabling a running container to function as a self-contained executable. In other words, it is a proxy to the ansible-playbook command.

docker run --rm -it -v $(pwd):/ansible/playbooks \
philm/ansible_playbook site.yml

Evaluating different versions of Ansible and tweaking its configuration changes is much simpler with Docker. Modifying the Dockerfile and rebuilding the image is significantly faster than other alternatives. Using Docker tags, I can easily reference older variants. And by setting defaults for several environment variables recognized by Ansible, I can override them at runtime, if necessary.

docker run --rm -it -v $(pwd):/ansible/playbooks \
-e ANSIBLE_RETRY_FILES_ENABLED=true \
philm/ansible_playbook site.yml

Testing Ansible playbooks in a pristine sandbox that can be recreated quickly during each test run can be handled by Docker, as an alternative to Vagrant. It requires building a VM-like Docker image that exposes an SSH service, such as this one and running the container:

docker run -d -p 2222:22 --name ansible_test \
-v ~/.ssh/id_rsa.pub:/home/ubuntu/.ssh/authorized_keys \
philm/ansible_target:ubuntu1404

An example playbook command might look like this:

docker run --rm -it \ 
--link ansible_target \
-v ~/.ssh/id_rsa:/root/.ssh/id_rsa \
-v ~/.ssh/id_rsa.pub:/root/.ssh/id_rsa.pub \
-v $(pwd):/ansible/playbooks \
philm/ansible_playbook tests.yml -i inventory

Notice the linking of the two containers. Docker will automatically update /etc/hosts in the ansible_playbook container with the ansible_test container’s IP address. This allows your Ansible inventory file to be as simple as:

[test]
ansible_target
“I hate typing long commands…”

You can easily wrap the above commands into a bash script or makefile, but Docker Compose is also a great option. A sample docker-compose.yml is included in the repo. An abbreviated command is now possible:

docker-compose run --rm test tests.yml -i inventory

And anytime you want to reset the test environment, remove the ansible_target container before running the Docker Compose command.

docker rm -v -f ansible_target

Docker Images:

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.