Docker with Ansible Part II : using variables
Ansible supports variables which allows you to add a level of abstraction in an Ansible file and to manage it more efficiently in case of change: a change will be applied globally rather than having to change one by one the options.
This is the second 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
Understand variables
Here is an example :
---
- hosts: localhost
vars:
var_1: variable
tasks:
- debug:
msg: "This is a {{ var_1 }}."
Result:
PLAY [localhost] *************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] *****************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "This is a variable."
}
PLAY RECAP *******************************************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
File without variables
We will use the file created in the previous post as a reference:
---
- hosts: localhost
tasks:
- name: Create network
docker_network:
name: network_app
ipam_options:
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"
Play with variables
To add variables we will place a vars
block above the tasks
block.
---
- hosts: localhost
vars:
docker_network: network_app
docker_network_backend: backend
mysql_db_name: db
mysql_db_user: ghost
mysql_db_host: db
mysql_db_password: secret_user
mysql_db_root_password: secret_root
tasks:
[...]
And for include in the tasks
part :
- name: Create network
docker_network:
name: "{{ docker_network }}"
ipam_options:
subnet: '172.16.98.0/24'
File with variables
Here an example with few variables :
---
- hosts: localhost
vars:
docker_network: network_app
mysql_db_name: db
mysql_db_user: ghost
mysql_db_host: db
mysql_db_password: secret_user
mysql_db_root_password: secret_root
tasks:
- name: Create network
docker_network:
name: "{{ docker_network }}"
ipam_options:
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: "{{ docker_network }}"
- 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: "{{ docker_network }}"
- 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: "{{ mysql_db_root_password }}"
MYSQL_DATABASE: "{{ mysql_db_name }}"
MYSQL_USER: "{{ mysql_db_user }}"
MYSQL_PASSWORD: "{{ mysql_db_password }}"
networks:
- name: "{{ docker_network }}"
Multiples variables
Here are examples with multiples variables :
---
- hosts: localhost
vars:
var_1: "the first variable"
var_2: "the second variable"
tasks:
- debug:
msg: "This is {{ var_1 }} and this is {{ var_2 }}"
and the result that goes with it:
PLAY [localhost] *************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] *****************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "This is the first variable and this is the second variable"
}
PLAY RECAP *******************************************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Variables are very flexible :
---
- hosts: localhost
vars:
var_1: app_
var_2: network
tasks:
- debug:
msg: "{{ var_1 }}{{ var_2 }}"
and the result :
PLAY [localhost] *************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] *****************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "app_network"
}
PLAY RECAP *******************************************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Prefix in Docker Compose way
With variables you can prefix resources really easily like Docker Compose do on a deployment (for a stack named app
all of the resources will be prefixed with the stack name (example : app_$VOLUME
, app_$NETWORK
, app_$CONTAINER
. Simply add a variables and prefix the resources you want.
For example, you want to prefix with app_
:
---
- hosts: localhost
vars:
docker_network: network
stack_prefix_name: app_
tasks:
- name: Create network
docker_network:
name: "{{ stack_name}}{{ docker_network }}"
ipam_options:
subnet: '172.16.98.0/24'
- name: Run Nginx container
docker_container:
name: '{{ stack_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"
- "{{ stack_name }}_php-app:/var/www/app"
networks:
- name: "{{ stack_name}}{{ docker_network }}"
Docker Compose up and down
Now that we have a super functionnal Ansible may be we want to easily up and down the stack. It's possible with the state
option.
For docker_network
there's 2 state : absent
and present
. For docker_container
there's 4 states : absent
, present
, stopped
, started
.
We'll set the default state with 2 variables : network_state
(the force
disconnect all containers from network when the network is deleted) and container_state
and add the option to the different resources :
---
- hosts: localhost
vars:
container_state: started
network_state: present
tasks:
- name: Create network
docker_network:
name: "{{ docker_network }}"
ipam_options:
subnet: '172.16.98.0/24'
state: "{{ network_state }}"
force: yes
[...]
- 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: "{{ docker_network }}"
state: "{{ container_state }}"
[...]
By default the stack will be created. If we want to delete it, just change the state with the --extra-vars
or -e
option :
$ ansible-playbook playbook.yml --extra-vars "container_state=absent network_state=absent"
Thanks to these option you can do whatever you want, for example just create the containers :
$ ansible-playbook playbook.yml --extra-vars "container_state=present"
Stop the containers :
$ ansible-playbook playbook.yml --extra-vars "container_state=stopped"
Or delete the containers but keep the network :
$ ansible-playbook playbook.yml --extra-vars "container_state=absent"
Resources :
https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html
https://docs.ansible.com/ansible/latest/modules/docker_network_module.html
https://docs.ansible.com/ansible/2.3/docker_container_module.html
https://docs.ansible.com/ansible/2.4/ansible-playbook.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.