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

Utiliser Docker et MariaDB avec Ansible

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.