Docker with Ansible Part I : from Docker Compose to Ansible
If you use Ansible to deploy your servers, you can go further by also integrating the deployment of your applications with Docker Compose.
This is the first post in a series of 3 articles about Docker and Ansible:
Part I: from Docker Compose to Ansible
Part II: using variables
Part III: using vault to encrypt sensitive information
Install Docker and Docker Compose
You'll find the instructions here.
Install Ansible
For installing the latest stable version of Ansible on CentOS :
$ yum install epel-release && yum install ansible
Personally I use the version under development to be able to take advantage of the latest features (for example, creating an internal network is not possible in the stable version 2.7) :
pip install git+https://github.com/ansible/ansible.git@devel
Create your playbook
We will deploy a stack with Nginx, PHP and mySQL.
Create your working directory :
$ mkdir ~/ansible-docker && cd ~/ansible-docker
Docker Compose Syntax
Here is a classic file with 3 services :
version: '3.6'
services:
nginx:
image: nginx:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./app.conf:/etc/nginx/conf.d/
- php-app:/var/www/app
restart: unless-stopped
php:
image: php:7-fpm
volumes:
- php-app:/var/www/app
restart: unless-stopped
db:
image: mysql:latest
volumes:
- mysql:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: secret_root
MYSQL_DATABASE: database_name
MYSQL_USER: sql_user
MYSQL_PASSWORD: secret_user
restart: unless-stopped
volumes:
mysql:
networks:
network_app:
driver: bridge
ipam:
config:
- subnet: 172.16.98.0/24
Ansible way
Create your Nginx : app.conf
:
$ vi app.conf
server {
listen 80;
server_name your_ip;
client_max_body_size 4M;
client_body_buffer_size 128k;
root /var/www/app;
index index.php;
location ~ \.php$ {
try_files $uri =404;
fastcgi_index index.php;
fastcgi_pass php:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
}
Create your Ansible playbook :
$ vi deploy.yml
And the same 3 services :
---
- hosts: localhost
tasks:
- name: Create network
docker_network:
name: network_app
ipam_config:
subnet: "172.16.98.0/24"
- name: Run Nginx container
docker_container:
name: 'nginx'
recreate: true
restart_policy: unless-stopped
image: 'nginx:latest'
published_ports:
- "80:80"
- "443:443"
volumes:
- "./app.conf:/etc/nginx/conf.d/app.conf"
- "php-app:/var/www/app"
networks:
- name: "network_app" -
- name: Run PHP container
docker_container:
name: 'php'
recreate: true
restart_policy: unless-stopped
image: 'php:7-fpm'
volumes:
- "php-app:/var/www/app"
networks:
- name: "network_app"
- name: Run Percona container
docker_container:
name: 'percona'
recreate: true
restart_policy: unless-stopped
image: 'percona:latest'
volumes:
- "percona:/var/lib/mysql"
env:
MYSQL_ROOT_PASSWORD: "secret_root"
MYSQL_DATABASE: "db"
MYSQL_USER: "db_user"
MYSQL_PASSWORD: "secret_user"
networks:
- name: "network_app"
Deploy
You've just to do :
$ ansible-playbook deploy.yml
And you'll see the output :
$ ansible-playbook deploy.yml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] *************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Run Nginx container] ***************************************************************************************************************************************************************************************************************
changed: [localhost]
TASK [Run PHP container] *****************************************************************************************************************************************************************************************************************
changed: [localhost]
TASK [Run Percona container] *************************************************************************************************************************************************************************************************************
changed: [localhost]
PLAY RECAP *******************************************************************************************************************************************************************************************************************************
localhost : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Check if everything is up :
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f7bed03b6674 percona:latest "/docker-entrypoint.…" 4 seconds ago Up 3 seconds 3306/tcp percona
5761d15e793e php:7-fpm "docker-php-entrypoi…" 8 seconds ago Up 7 seconds 9000/tcp php
517dbb31f0d6 nginx:latest "nginx -g 'daemon of…" 10 seconds ago Up 9 seconds 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp nginx
Test the deployment
Add a info.php
in the php-app
volume :
$ vi /var/lib/docker/volumes/php-app/_data/info.php
add :
<?php
// Show all information, defaults to INFO_ALL
phpinfo();
?>
Get the info.php
:
$ curl -I http://127.0.0.1/info.php
HTTP/1.1 200 OK
Server: nginx/1.15.9
Date: Mon, 25 Mar 2019 16:00:37 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/7.3.3
It's all good!
Destroy
It's very simple, for example :
- name: remove Nginx container
docker_container:
name: nginx
state: absent
- name: remove PHP container
docker_container:
name: php
state: absent
- name: remove Percona container
docker_container:
name: percona
state: absent
- name: Delete network, disconnecting all containers
docker_network:
name: network_app
state: absent
force: yes
But it's more simple to integrate directly in the first file with the state
option, we'll see this here.
Resources :
https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html
https://docs.ansible.com/ansible/2.7/plugins/connection/docker.html
https://docs.ansible.com/ansible/2.7/modules/docker_container_module.html
https://docs.ansible.com/ansible/2.7/modules/docker_network_module.html
https://docs.ansible.com/ansible/latest/modules/docker_network_module.html
Feel free to correct me if you see any typo or if something seems wrong to you.
You can send me an email or comment below.