Cet article n’a pas vocation à être un tutoriel complet, ni même une référence : il s’agit ici de retracer ma première expérience avec Ansible.
Après des années à configurer les serveurs à la main, en me connectant en SSH à chaque fois (y compris sur des clusters de plusieurs machines similaires), il était temps d’apprendre à automatiser et paralléliser tout ça !
Ayant travaillé récemment sur AWS pour mettre en place l’infrastructure soutenant une application médicale, j’ai été tenté d’utiliser un service cloud, pour m’éviter toute maintenance. Mais garder un peu la main sur l’infra me paraît important pour ne pas devenir dépendant d’un service : va pour un VPS chez Gandi (des français que je respecte beaucoup, qui hébergent d’ailleurs gracieusement ZdS) !
- Principes de base
- Mise en route
- Premiers essais
- Les plugins : ça passe ou ça casse
- Organiser ses fichiers
Principes de base
De ce que j’en ai compris, l’idée d’Ansible est de fournir une liste d’actions reproductibles pour configurer, installer ou mettre à jour une machine.
Chaque action est une tâche (ex. : installer le package nginx
ou copier un fichier de configuration).
Les tâches peuvent être regroupées en rôles pour configurer un même module (ex. : actions spécifiques à la mise en place de Nginx ou clonage du dépôt Git).
Les rôles peuvent contenir des gabarits (templates en anglais), des fichiers bruts et des gestionnaires (handlers en anglais).
Les tâches et les gabarits peuvent faire appel à des variables, les rendant ainsi adaptatifs et réutilisables.
Mise en route
Ansible fournit un guide d’installation assez fourni sur le sujet, mais comme la loi de Murphy l’a prédit : ce qui pouvait mal se passer s’est mal passé.
J’ai donc vite abandonné l’installation manuelle pour utiliser Homebrew : un rapide brew install ansible
et le tour est joué ! Je peux confirmer que ça fonctionne avec ansible all -m ping --ask-pass
pour être sûr… et c’est parti pour jouer avec ce nouvel outil.
Le principal avantage est qu’Ansible ne requiert pas de configuration spécifique sur le serveur, tant que Python y est installé (ce qui est le cas par défaut des principales distributions Linux).
Premiers essais
On commence par configurer nos hôtes. Par défaut Ansible va chercher le fichier /etc/ansible/hosts
:
[portfolio]
portfolio_www ansible_host=www.corentin-hatte.eu ansible_user=corentin
Pour faire simple j’ai commencé par des actions simples, sans passer par un rôle :
---
- name: Mon premier test Ansible
hosts: all
tasks:
- name: "Dis bonjour"
command: "echo 'Hello World' > test.txt"
- name: "Dis au revoir"
command: "echo 'Goodbye World' > test.txt"
Et j’exécute mon fichier : ansible-playbook test.yml
pour obtenir (si tout se passe bien) quelque chose comme ça :
PLAY [Mon premier test Ansible] ********************************************************************
TASK [Gathering Facts] *****************************************************************************
ok: [portfolio_www]
TASK [Dis bonjour] *********************************************************************************
changed: [portfolio_www]
TASK [Dis au revoir] *******************************************************************************
changed: [portfolio_www]
PLAY RECAP *****************************************************************************************
localhost : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Les plugins : ça passe ou ça casse
Pour répondre à tous types de besoin, il est possible de créer des plugins Ansible, que l’on peut trouver sur Ansible Galaxy qui recense des contributions officielles et non-officielles installables par lots ou à l’unité.
L’avantage d’utiliser des plugins plutôt que d’exécuter des commandes à tout va, c’est qu’un plugin va aller vérifier l’état actuel de votre machine cible pour ne rien changer si tout est déjà bon. Ça évite des traitements inutiles et des erreurs potentielles.
J’en utilise quelques-uns sans problème. D’autres n’ont tout simplement pas fonctionné (comme ceux pour certbot
que j’avais trouvés) pour mon besoin.
Le plugin MySQL… presque parfait ?
Le plugin MySQL officiel est très pratique pour les opérations de configuration de base de donnée, et fonctionne avec MariaDB également.
Je peux ainsi créer me base de données (avec son utilisateur dédié) facilement, sans risquer d’écraser quoi que ce soit :
- name: "Create MariaDB database"
community.mysql.mysql_db:
login_user: root
login_password: "{{ mysql_root_password }}"
name: "{{ db_name }}"
state: present
- name: "Create MariaDB user"
community.mysql.mysql_user:
login_user: root
login_password: "{{ mysql_root_password }}"
name: "{{ db_user }}"
password: "{{ db_password }}"
priv: "{{ db_name }}.*:ALL"
host: localhost
state: present
Je me connecte ici en tant que root
à MariaDB pour créer la base et l’utilisateur dont j’ai besoin, en utilisant les variables déclarées en amont.
Principale limitation : je n’ai pas encore trouvé de fonctionnalité pour générer une sauvegarde automatique. Il faudra sans doute passer par une commande plus manuelle…
J’ai également eu du mal à paramétrer le mot de passe root
par défaut via ce plugin, je suis donc passé par une command
pour ne pas perdre trop de temps.
Le plugin Composer… peut-être ?
En théorie ça a l’air très intéressant, mais du fait de ma configuration spécifique (avec un utilisateur www
qui n’a que peu de droits) Ansible refuse d’exécuter les commandes à distance. Peut-être avez-vous des idées pour y parvenir ?
En attendant j’utilise un simple command: "sudo -u www composer update --no-scripts --no-dev"
par exemple pour mettre à jour les dépendances…
Organiser ses fichiers
Dans mon dossier ansible
, qui contient donc tout ma config de déploiement, j’ai l’architecture suivante :
roles/
(dossier regroupant les différents rôles et leurs définitions spécifiques)group_vars/all
(fichier contenant toutes les variables utilisées par les scripts).vault-password
(fichier privé qui contient le mot de passe pour déchiffrer les mots de passes du coffre Ansible)host
(fichier de configuration Ansible d'inventaire des serveurs)install.yml
(fichier qui regroupe les rôles dédiés à la mise en place du serveur)update.yml
(fichier qui regroupe les rôles dédiés à la mise à jour du site)
Créer des rôles
Les rôles ont globalement la forme suivante (adaptée en fonction des besoins spécifiques :
tasks/main.yml
(fichier décrivant les tâches à réaliser pour ce rôle)files/fichier1.ext
(fichier à copier tel quel sur le serveur)templates/fichier1.ext
(fichier utilisé comme template, dans lequel je peux injecter des variables Ansible avant de le copier sur le serveur)handlers/main.yml
(fichier d’écouteurs permettant de lancer des actions — comme relancer un service — en cas de notification)
En nommant les fichiers ainsi je n’ai pas à fournir beaucoup de configuration à Ansible : il sait directement où aller chercher ce dont il a besoin.
Mon rôle nginx
est par exemple assez simple : on ajoute le dépôt officiel, on installe le paquet système et on copie la config.
---
- name: Add nginx stable repository from PPA and install its signing key on Ubuntu target
ansible.builtin.apt_repository:
repo: ppa:nginx/stable
- name: Install nginx
apt:
update_cache: yes
name:
- nginx
- name: Copy nginx configuration for Laravel
template:
src: portfolio.conf
dest: /etc/nginx/sites-enabled/portfolio.conf
notify: restart nginx
La dernière tâche notifie le gestionnaire qui se charge de s’assurer que le service Nginx est bien redémarré.
---
- name: restart nginx
service:
name: nginx
state: restarted
enabled: yes
Utiliser les rôles pour déployer tout ça
Avec le bon Makefile
à la racine du projet je peux donc faciliter les déploiements :
server-create: server-install server-deploy
server-install:
cd ansible && ansible-playbook -i hosts --vault-password-file .vault-password install.yml
server-deploy:
cd ansible && ansible-playbook -i hosts --vault-password-file .vault-password update.yml
Le fichier update.yml
qui décrit les rôles à utiliser pour mettre à jour le site ressemblant à ça :
---
- name: Deploy new website version
hosts: all
become: yes
roles:
- laravel
- deploy
J’ai plus qu’à lancer un make server-deploy
une fois le code poussé sur le dépôt Git et tout se fait automatiquement !
Maintenant que j’ai rédigé cet article j’ai plus qu’à tout reprendre pour améliorer… jamais satisfait.