Dans cet atelier/tutoriel, nous allons déployer une base de données SQL (MariaDB) et une application web conteneurisée (via Docker) avec Ansible. Nous verrons également comment utiliser Molecule pour générer un rôle Ansible et le tester en local.
Prérequis
Afin de pouvoir réaliser ce tutoriel, vous devez avoir installé Docker, Python3 et Pip.
Le tutoriel a été réalisé sur Linux, je ne promet rien si vous avez un autre système d’exploitation 😉
Architecture du serveur
L’objectif de projet Ansible est d’installer Docker et MariaDB sur une machine distance. Le schéma ci-dessus représente l’architecture de test du projet.
Molecule, Docker et Ansible seront installés sur la machine locale (votre ordinateur). Nous allons provisionner un conteneur Docker dans lequel Ansible installera notre application de test (i.e MariaDB et notre application web). Le conteneur de test est celui à gauche du schéma.
Ce conteneur recevra un accès au socket de Docker, ce qui lui permettra de lancer le conteneur de l’application web. Attention, ce conteneur ne sera pas situé à l’intérieur du conteneur de test mais bien côte à côte de celui-ci.
Le conteneur isolant l’application web recevra un accès à MariaDB via un socket. Pour cela, on utilisera un volume partagé entre le conteneur de test et celui de l’application web.
Création d’un rôle Ansible avec Molecule
Un rôle Ansible regroupe un ensemble de tâches à accomplir pour atteindre un but précis. Développer un ensemble de rôles réutilisables et testés permet d’éviter des bugs et de factoriser la logique du déploiement.
Comme notre exemple est très simple, nous allons créer un seul rôle, nommé app. Si la complexité de l’application grandit, vous pourrez séparer les tâches de ce rôle dans plusieurs autres rôles et en créér de nouveaux pour les tâches additionnelles que votre client vous demande d’accomplir.
Par exemple, dans mon projet actuel, j’ai séparé mon déploiement en quatre rôles:
- Configuration de Docker, MariaDB et création des conteneurs applicatifs
- Installation de Certbot (certificats SSL) et de Nginx (proxy vers les conteneurs)
- Installation d’un serveur FTP
- Création d’un utilisateur MariaDB accessible à distance via une application Qt
Voici comment créer la structure du projet et installer Ansible et Molecule dans un virtualenv Python:
mkdir -p tutoriel-ansible/roles
cd tutoriel-ansible
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install ansible "molecule[docker,lint]"
Nous pouvons maintenant créer la structure du rôle app
avec
Molecule:
cd roles
molecule init role app --driver-name docker
Vous pouvez voir la structure du rôle créé avec la commande tree app
. Voici sa sortie que nous allons examiner ensemble:
app
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── molecule
│ └── default
│ ├── converge.yml
│ ├── molecule.yml
│ └── verify.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
Contenu des dossiers du rôle Ansible créé
Dossier de tâches
Le dossier tasks
contient les tâches à accomplir lors de l’exécution
de ce rôle.
Dossier de variables
Les dossiers vars
et defaults
contiennent des jeux de
variables. On stockera les variables par défaut dans defaults
. Des
variables correspondant à des scénarios particuliers peuvent être
placées dans vars
.
Les variables importées à partir de defaults
seront écrasées par
celles importées depuis vars
. Vous pouvez lire la documentation
concernant les priorités des
variables
pour plus de détails sur ce mécanisme.
Dossier Molecule
Le dossier molecule/default
du rôle app
contient deux
playbooks
Ansible:
converge.yml
verify.yml
Le premier playbook converge.yml
est lancé avec la commande molecule converge
qui lance les tâches du rôle courant avec comme inventaire
(i.e ensemble des machines sur lesquelles installer des applications)
le conteneur Docker de test défini dans molecule.yml
.
Le second playbook, verify.yml
est exécuté avec molecule verify
. Le contenu de ce playbook vise à tester l’état du conteneur
de test pour voir si l’installation s’est bien passée.
Dossier meta
app/meta
contient un fichier main.yml
qui décrit le rôle app
.
Vous devriez remplacer les paramètres author
et description
par
une description qui fais sens et l’auteur par la personne ayant créé
le rôle.
Les paramètres role_name
et namespace
devront également être
ajoutés.
galaxy_info:
role_name: app
namespace: tutorial
author: Jules Sagot--Gentil
description: |
Install MariaDB, Docker and deploy a demo app and a database.
Instance de test de nos rôles
Nous allons modifier l’instance de test provisionnée par Ansible afin
de rendre le démon Docker accessible. Pour cela, éditez le fichier
app/molecule/default/molecule.yml
.
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: tutorial-instance
image: debian:bullseye
volumes:
- /var/run/docker.sock:/var/run/docker.sock:rw
- /sys/fs/cgroup:/sys/fs/cgroup:ro
- instance-mariadb-socket:/var/run/mysqld
privileged: true
provisioner:
name: ansible
verifier:
name: ansible
Molecule va maintenant lancer notre rôle Ansible app
dans le
conteneur Docker nommé tutorial-instance
utilisant l’image Docker de
Debian Bullseye.
Le volume docker nommé instance-mariadb-socket
va permettre de
partager le socket MariaDB entre l’application web conteneurisée et le
conteneur tutorial-instance
.
On peut lancer le maintenant provisionnement du conteneur de test avec:
cd app
molecule create
Installation de Docker et de MariaDB
On va éditer le fichier tasks/main.yml
qui contient les tâches de
notre rôle qui seront exécutées par Ansible. Nous allons dans un
premier temps installer Docker et MariaDB.
---
- name: mise à jour des paquets
apt:
upgrade: yes
update_cache: yes
- name: installation des dépendances de docker
apt:
name:
- ca-certificates
- curl
- gnupg
- lsb-release
- name: installation de la clef GPG du projet Docker
apt_key:
data: "{{ lookup('file', 'docker_repo_gpg_key.asc') }}"
state: present
- name: ajout du dépôt Debian de Docker
apt_repository:
repo: deb [arch=amd64] https://download.docker.com/linux/debian bullseye stable
state: present
update_cache: yes
- name: installation de docker
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
update_cache: yes
- name: installation de python pip
apt:
name:
- python3-pip
- name: installation du sdk python pour Docker
pip:
name: docker
- name: installation de MariaDB
apt:
name: mariadb-server
- name: /var/run/mysql doit appartenir à mysql
file:
path: /var/run/mysqld
owner: mysql
group: mysql
- name: démarrage du service MariaDB
service:
name: mariadb
state: started
enabled: yes
Le dossier files
contient des fichiers statiques nécéssaires au
projet. Il vous faudra télécharger la clef GPG
de Docker pour installer ce logiciel en toute sécurité.
curl https://download.docker.com/linux/ubuntu/gpg > files/docker_repo_gpg_key.asc
Nous pouvons maintenant lancer molecule converge
afin de déployer
notre rôle sur l’instance Docker de test.
Pour vérifier que tout s’est passé comme prévu, on va vérifier qu’on
peut lancer le conteneur hello-world
depuis l’instance de test.
On ajoute cette tâche au playbook Ansible molecule/default/verify.yml
- name: Verify
hosts: all
gather_facts: false
tasks:
- name: run docker hello world
community.docker.docker_container:
name: test_hello_world
image: hello-world
detach: no
cleanup: yes
Pour vérifier que les tests passent, lancer molecule verify
.
Partager et utiliser des rôles Ansible classiques avec Ansible Galaxy
Le projet Ansible Galaxy permet aux utilisateurs d’Ansible de partager leurs rôles. Par exemple, dans notre rôle, l’installation de Docker prend 4 étapes. Il existe sûrement un rôle Ansible Galaxy permettant d’installer Docker.
Utiliser les rôles d’Ansible Galaxy permet de factoriser son application Ansible avec des rôles testés unitairement ainsi qu’en production par d’autres utilisateurs d’Ansible.
Une collection Ansible Galaxy est un ensemble de rôles partageant une même thématique. Nous allons utiliser les deux collections de rôles Ansible suivants:
- community.docker
- community.mysql
Le premier permet d’intéragir avec les conteneurs Docker et le second de requêter une base de données SQL.
Ces collections de rôles seront listées comme une dépendance du rôle
app
. Pour ce faire, nous allons définir un fichier de dépendances,
requirements.yml
collections:
- name: community.docker
- name: community.mysql
Pour installer ces collections de rôles (depuis le fichier
requirements.yml
), lancez la commande suivante:
ansible-galaxy collection install -vr requirements.yml
Administration de la base de données
Les rôles de la collection Ansible Galaxy community.mysql
que nous
avons précédemment installée va nous permettre de créer:
- Un utilisateur MariaDB
- Une base de données
Création d’un utilisateur MariaDB
On ajoute une template Jinja2 de fichier de configuration MariaDB
my.cnf.j2
dans templates
qui permettra de sauvegarder le mot de
passe de l’utilisateur MariaDB créé pour les prochaines connexions et
garantir
l’idempotence
du rôle Ansible:
[client]
user={{ root_user }}
password={{ root_password }}
Les variabes root_user
et root_password
auront comme valeurs par
défaut demo
. Nous stockons ces défauts dans defaults/main.yml
,
ainsi que le nom de la base de données à créer.
root_user: demo
root_password: demo
database: demo-db
Ajoutons la création d’un utilisateur MariaDB à tasks/main.yml
:
#...
- name: installation du SDK python pour MariaDB
pip:
name: PyMySQL
- name: création d'un utilisateur root avec mot de passe
community.mysql.mysql_user:
name: "{{ root_user }}"
password: "{{ root_password }}"
plugin: mysql_native_password
priv: '*.*:ALL,GRANT'
state: present
login_unix_socket: /var/run/mysqld/mysqld.sock
- name: sauvegarde du compte MariaDB
template:
src: my.cnf.j2
dest: /root/.my.cnf
owner: root
group: root
mode: '0600'
- name: suppression de l'utilisateur root MariaDB
community.mysql.mysql_user:
name: root
state: absent
Création d’une base de données
On crée une base de données avec le rôle mysql_db
contenu dans la
collection galaxy community.mysql
téléchargée précédemment:
#...
- name: creation de la base de données
community.mysql.mysql_db:
name: "{{ database }}"
state: present
Instantiation d’une application web
Pour cet atelier, nous allons simplement déployer PHPMyAdmin qui est un logiciel libre permettant de visualiser/modifier une base de données.
Nous allons cette fois utiliser la collection de rôle Ansible Galaxy
community.docker
et plus précisément le rôle container
.
La nouvelle tâche suivante est à insérer dans tasks/main.yml
.
#...
- name: instantiation de l'application web
community.docker.docker_container:
name: webapp
image: phpmyadmin:5-apache
env:
PMA_SOCKET: "/var/run/mysqld/mysqld.sock"
PMA_HOST: localhost
PMA_USER: "{{ root_user }}"
PMA_PASSWORD: "{{ root_password }}"
volumes: "instance-mariadb-socket:/var/run/mysqld"
ports:
- "3000:80"
Nous ajoutons également un test vérifiant si PHPMyAdmin répond aux
reqûetes sur le port 3000. Le code suivant est à ajouter aux tâches du
playbook verify.yml
qui sera lancé lors du test de notre rôle
Ansible avec molecule verify
.
- name: l'application web doit répondre aux requêtes
command: "curl -s http://localhost:3000/"
delegate_to: localhost
Code source de l’atelier
Le code source que nous avons écrit ensemble dans le cadre de ce tutoriel est disponible sur Gitlab.
Conclusion
Ce tutoriel vous a permis d’avoir un aperçu des possibilités qu’offrent les rôles d’Ansible en terme de factorisation/qualité de code. Molecule et Docker apportent de leur côté un déploiement standard des applications (en les conteneurisant) et la possibilité de tester le bon déroulement d’un rôle Ansible.
L’automatisation des tests de déploiement avec Ansible, Molecule et Docker permet ainsi d’être confiant lors des releases en production et d’éviter les erreurs humaines, ainsi que d’assurer une traçabilité de l’infrastructure.