Docker with Ansible Part I : from Docker Compose to Ansible
![Docker with Ansible Part I : from Docker Compose to Ansible](/content/images/size/w960/2019/03/Ansible_logo.svg.png)
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.