In this tutorial, I’m going to walk through how to build Docker containers using Ansible. Ansible is a great tool to use with Docker. We could use it to build our containers, convert long shell scripts into readable roles that can be easily understood. I have been using it for around 2 years and when it comes to provisioning anything it’s my first tool to use! In this article, I will show you how to use it to build containers. With Ansible, you don’t have to write long shell scripts as the roles will be executed on the container. If you’re struggling to find out the use cases, please find below two of mine:
- Containerizing your legacy applications. (suppose your application is not on a container, instead of spending time writing long shell scripts you could utilize your roles on a container)
- Replacing Shell scripts. Long shell scripts can be difficult to fix or work on. Ansible roles can be easily read and modified and this is excellent for today’s DevOps.
So What do I need to start the tutorial?
- Linux OS
- Docker Engine
- Ansible
How does Ansible build Docker containers?
Ansible uses “Ansible Container” a package which handles the process of running roles on a Docker container. In short, it launches a container called “Conductor“. This container has all the required packages. The conductor then connects to the target container and runs the roles. SO EASY AND YET.
Once, an image is created you can upload it to the Docker hub or any other registry.
So Let’s begin:
Step 1: Installing Ansible-Container
To build Docker images, we need to install “Ansible-Container”. By default, it does not ship with Ansible.
- Install Ansible-Container
pip install ansible-container[docker]
2. Verify that it has successfully installed by running the command below.
ansible-container
It should work without any errors.
Now that we have installed the Ansible Container, we would need to create our project directory.
mkdir ansible-container-demo cd ansible-container-demo ansible-container init
Ansible container will create the files below:
ansible.cfg # override Ansible configs here ansible-requirements.txt #list python requirements here container.yml # this is the file which we will use! meta.yml requirements.yml .dockerignore # you know this -.-
Our Role
We then need to create a directory to hold our roles.
mkdir roles cd roles ansible-galaxy init optimize-nginx
Note: I won’t be adding the tasks in the role but let’s assume that we have a role that optimizes Nginx.
container.yml
This is the file which we will be working on it. Consider “container.yml” as the Docker Compose file. Here, we list out the roles we would like to run on our Container.
version: "2" settings: conductor: base: alpine:3.5 project_name: infinitypp services: web: from: nginx:alpine container_name: nginx-custom ports: - "80:80" roles: - optimize-nginx command: ["/usr/sbin/nginx", "-g", "daemon off;"] dev_overrides: environment: - "DEBUG=1" registries: docker: url: https://index.docker.io/v1/ namespace: infinitypp repository_prefix: ''
The syntax is similar to Docker-compose file. At the top, we declare our conductor image alpine:3.5. Then under services, we declare the services. You could also have multiple services. The most important part is the roles section. Here, we are asking Ansible to run the role “optimize-nginx” on the web service.
Imagine, the optimization Nginx script is a shell script of more 200 lines. Now, it is in an Ansible role. You could also use roles from the galaxy.
Now, that we have declared our container.yml settings lets build it.
Building the Container
To build the container, you would use:
ansible-container build
It will launch the “conductor” container. Once it is ready then it will run the Ansible roles to the target container.
[email protected]:/var/www/html/ansible-docker-demo# ansible-container build Building Docker Engine context... Starting Docker build of Ansible Container Conductor image (please be patient)... Parsing conductor CLI args. Docker™ daemon integration engine loaded. Build starting. project=infinitypp Building service... project=infinitypp service=web PLAY [web] ********************************************************************* TASK [Gathering Facts] ********************************************************* ok: [web] TASK [optimize-nginx : install Apache server] ********************************** changed: [web] PLAY RECAP ********************************************************************* web : ok=2 changed=1 unreachable=0 failed=0 Applied role to service role=optimize-nginx service=web Committed layer as image image=sha256:493d481f64c1bae879f4d2aa83a1dd5b09fa8dcd74945e0bafd1905ae9c9d41a service=web Build complete. service=web All images successfully built. Conductor terminated. Cleaning up. command_rc=0 conductor_id=4243634ff5bfe08393bbfad1927174493c818757b37381ac7dd4ef0621f00d1a save_container=False
Now, that we have our roles executed on the target conductor it’s time to run it.
How to run the service?
ansible-container run
preview:
[email protected]:/var/www/html/ansible-docker-demo# ansible-container run Parsing conductor CLI args. Engine integration loaded. Preparing run. engine=Docker™ daemon Verifying service image service=web PLAY [Deploy infinitypp] ******************************************************* TASK [docker_service] ********************************************************** ok: [localhost] PLAY RECAP ********************************************************************* localhost : ok=1 changed=0 unreachable=0 failed=0 All services running. playbook_rc=0 Conductor terminated. Cleaning up. command_rc=0 conductor_id=055afdd703de3579e0d8294916a88eb04a03b034a4444af7ca6772ec5d4cd42e save_container=False [email protected]:/var/www/html/ansible-docker-demo# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 769ee69402ec infinitypp-web:20181014201817 "/usr/sbin/nginx -g …" About an hour ago Up About an hour 0.0.0.0:80->80/tcp infinitypp_web_1
That’s all. In our example, we had only one role, but imagine you have a few roles and you want to migrate your application to a container.
Screencast

I’m a passionate engineer based in London.
Currently, I’m working as a Cloud Consultant at Contino.
Aside my full time job, I either work on my own startup projects or you will see me in a HIIT class 🙂