Baking Your Docker Images With Ansible

In Cesar Tonnoir’s serie of posts, you have been presented the value of containerization with Docker and and how easy it can be to set up your own PaaS using Chef, Machine Swarm and Compose.

In this article, I will present you how to create a Docker image using Ansible to install a standard LAMP stack (Linux Apache MySQL PHP) and then reuse it from another Docker image. The ultimate goal is to run Vtiger CRM – a PHP application – on it minimizing the effort and time required to create this image and launch a container to run it.

Getting started with Ansible

If you are not familiar with Ansible, I strongly advise you to take a couple of hours to play around with it. It is a powerful tool that can automate any task run over SSH. If you frequently execute the same set of commands over SSH on remote servers, you will realize that automating this manual job is rewarding and fun!

Set up your LAMP stack

There is a myriad of LAMP stack roles available on Ansible Galaxy so you do not have to reinvent the wheel. Get inspired by your fellow open-source community contributors and craft your perfect stack!

To install Apache and MySQL on a Debian Linux, we split the tasks into two playbooks

Apache Playbook

Playbooks are written as YAML files and define a sequence of tasks to be run on the remote hosts.

This playbook makes usage of the Ansible core modules apt and apache2_module to install the required packages and configure the list of Apache modules needed to run our application.
We also use the template module to configure the php.ini file to set memory options, error logging and so on. preview of a typical php.ini file
The default Apache virtual host displaying the Apache server welcome page is removed so we ensure having a virgin Apache installation.

MySQL Playbook


After installing the required MySQL packages and configuring the template my.cnf, we ask Ansible to grant our MySQL root user access to the database and delete the default test database.

Configuration settings

The templates of the previous playbooks reference Ansible variables that we haven’t defined yet. The default values are set in the file ansible/var/settings.yml and can be overwritten later when the playbook is executed.

Alright! Now we have configured Ansible to be capable of installing our LAMP stack on any SSH accessible hosts. So how do we run that on a Docker image?

Dockerize my stack!

Directory structure

We organize our directory structure as follows
Directory Structure
The playbooks, templates and settings are located under the ansible directory so they are visible to the Dockerfile.

Starting from the latest Debian image, we use the ADD commands to copy the content of the ansible directory on the Docker image.

You will observe that we only have one command RUN which takes care of running the yak shaving tasks: installing Ansible, running the playbooks and cleaning up temporary files. The goal of combining these commands is to reduce the size of the Docker image created.
Another thing to note is the parameter -c local passed to Ansible. This tells Ansible to run on the local machine, so we do not connect to the remote node over SSH and do not need to configure SSH keys.

The last thing to note is the creation of the script file /root/ which starts our services mysql and apache2 and then launches bash. We set this file as the image ENTRYPOINT so when a container is launched based on this image, the services are automatically started and a bash terminal available if we run the container in interactive mode.

Run it!

Creating the Docker image is as simple as running the command sudo docker build -t “maestrano:lamp” .
After a few minutes, your LAMP Docker image is created and added to your local image repository.

The output of the docker build command can be found here

Brilliant! now let’s install our PHP application on top

Install the PHP application

At Maestrano, we use open source applications to put SMBs on steroids, providing them with an ecosystem of integrated application out of the box. In this example we are going to install our customized version of Vtiger CRM including Single Sign-on and Data-sharing capabilities.

Ansible to the rescue

Once again, Ansible is our new best friend when it comes to automating tasks.

It is interesting to note the usage of the ansible modules mysql_db and mysql_user to delegate the creation of our application database and user. You do no longer need to run the typical command CREATE USER … GRANT … anymore!

The way we install our application is by fetching our Git repository and specifying the version to install with the git module. This is really powerful when setting up a build pipeline, you can specify the application tag/version/commit to fetch and then build different versions of your application reusing the same playbooks.

After downloading the application, we use a predefined apache virtual host template to expose the web application. This template is copied under /etc/apache2/sites-available/vtigercrm.conf. We then tell Ansible to create a symbolic link to /etc/apache2/sites-enabled/vtigercrm.conf

Dockerize my application!

Leveraging from our previously built LAMP image, we define a lightweight Dockerfile running the Vtiger installation playbook on top

And running this docker build command `sudo docker build -t “maestrano:vtiger-6.2.0” .` takes only a few seconds thanks to the pre-built LAMP image.

Run it!

Now comes our final step, launching a container. This is as simple as

We instruct Docker to start the container in interactive mode. On start, the container executes the script /root/ to start the services and expose the bash shell for our convenience.

To reach the Apache server inside the container we use command the docker inspect to retrieve its IP Address

And when browsing to the URL, Bingo! you have a fresh install of Vtiger running in a Docker container!

The Maestrano Docker images are publicly available on Docker Hub:
Lamp Stack:

Bruno Chauvet