When one uses Docker for a while, after the excitement of how amazing is Docker is gone, he starts realizing that building and pulling images, and creating containers by hand, may not be the best way for managing Docker applications and services.
Luckily, Ansible, one of the most powerful IT Automation tool, comes to the rescue here. Ansible offers modules for managing our Docker images and containers, in an amazingly easy way (as Ansible uses to with everything). Using Ansible for managing Docker containers, in combination with a version control system for keeping a track of the playbooks is, without any doubt, the best way to manage Docker.
For this tutorial, Linux Mint 18, Ansible 220.127.116.11 and Docker 1.12.6.
You may skip Ansible, Docker and Docker modules for Ansible, and jump directly to the beginning of the example below.
We will start installing Ansible:
sudo apt-get update sudo apt-get install software-properties-common sudo apt-add-repository ppa:ansible/ansible sudo apt-get update sudo apt-get install ansible
We will also need pip for installing the Docker module for Python:
sudo apt-get install python-pip python-dev build-essential sudo pip install --upgrade pip
Now, we can install the Docker module:
sudo pip install docker-py
Obviously, in the host we are going to run Docker (localhost for this example), we will need to have it installed:
sudo apt-get install docker.io
2. Using Docker with Ansible
Ansible offers two modules for dealing with Docker:
docker_container. Let’s see how to use them for different scenarios.
2.1. Pulling images
For pulling images from the Docker Hub, the module to use is, naturally, the docker_image. For example, for pulling an Ubuntu image, our playbook would be:
--- - hosts: localhost tasks: - name: Pull Ubuntu image docker_image: name: ubuntu
That’s it! Executing this playbook:
Would result in the download of the Ubuntu image in the host (localhost, in this case). We can check that the image was effectively pulled executing:
We may also want to specify the tag of the image:
--- - hosts: localhost tasks: - name: Pull Ubuntu image docker_image: name: ubuntu tag: 17.04
2.2. Building images
Let’s consider a extremely simple Dockerfile, for installing Nginx in an Ubuntu image:
FROM ubuntu:17.04 MAINTAINER Julen Pardo <email@example.com> ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update RUN apt-get install -y nginx
For building it with Ansible, we also have to use the docker_image module, in almost an identical way as for pulling:
--- - hosts: localhost tasks: - name: Build Nginx image docker_image: path: ./docker_nginx name: my-nginx tag: 0.1
As we can see, the only difference is that we have to specify the path to the Dockerfile. The name and the tags are specified in the same way.
We can also push it to a Docker Hub repository very easily:
--- - hosts: localhost tasks: - name: Build Nginx image docker_image: path: ./docker_nginx name: registry.ansible.com/julenpardo/my-nginx tag: 0.1 push: yes
2.3. Creating containers
After seeing the ways to set our images, it’s time to create the containers!
For this, we will take the example of the creation of the Nginx container, for starting to have more thorough playbooks:
--- - hosts: localhost tasks: - name: Build Nginx image docker_image: path: ./docker_nginx name: my-nginx tag: 0.1 - name: Create Nginx container docker_container: name: my-nginx1 image: my-nginx:0.2
The second task is enough to create our Nginx container. But it will be exited immediately. And we didn’t even bind the ports with the host. To do so, we just have to do the following:
- name: Create another Nginx container docker_container: name: my-nginx2 image: my-nginx:0.2 ports: - "80:80" env: KEY: value command: sleep infinity
That’s it. Note that we also defined some environmental variable as an example.
2.4. Other operations with containers
Of course, creating them is not the only operation we may need to do with Docker. For removing, stopping, etc. we have to use the
state parameter, which has the following values available:
started(default): create the container. The default option; if we don’t specify any, a container will be created.
present: checks that a container, for the given name and configuration, exists. If it doesn’t match, the container will be created.
started: same as
present, but we can use the
restartparameter for restarting the container if exists and it’s stopped.
absent: for stopping and removing the container of the given name.
stopped: checks that the container it’s first
present, and then, sets it as stopped. For removing instead of stopping, we have to use
Let’s play with this options:
--- - hosts: localhost tasks: - name: Build Nginx image docker_image: path: ./docker_nginx name: my-nginx tag: 0.3 - name: Create another Nginx container docker_container: name: my-nginx3 image: my-nginx:0.3 command: sleep infinity - name: Stop Nginx container docker_container: name: my-nginx3 state: stopped - name: Check that Nginx container exists (it does) docker_container: name: my-nginx3 image: my-nginx:0.3 state: present - name: Check that Nginx container exists, and restart it if it does (it does) docker_container: name: my-nginx3 image: my-nginx:0.3 state: started restart: true - name: Remove Nginx container docker_container: name: my-nginx3 state: absent
In this example we have seen how to manage our Docker images and containers with Ansible, in an extremely easy and comfortable way. Using Ansible in combination with Docker will allow us to manage our containers within our servers (or locally, as done in the example) more easily, and exactly knowing which configuration is set for each one.