Ecriture, Exécution et Debug de playbook Ansible
Création :
Dans la précédente partie, vous aviez découvert l’installation et l’exécution de modules Ansible. Aujourd’hui attardons sur l’écriture de playbook Ansible en utilisant toutes les fonctions mises à notre disposition.
Si vous utilisez un environnement virtuel python, avant tout exécution de vos playbooks Ansible ou installation de librairies n’oublier pas d’activer l’environnement virtuel python avec la commande suivante :
Construction d’un playbook Ansible
Nous avons vu qu’un playbook ansible est une séquence de tâches ou de rôles
décrits dans un fichier. Pour le moment, nous n’utiliserons que des tâches
(tasks). Un playbook ansible est un fichier au format yaml
. Le YAML
est
un langage de description comme l’est le HTML
. En YAML
Le plus important est
de respecter l’indentation sans quoi la structure n’est plus compréhensible.
Le débogage est de ce fait complexe quand le fichier devient volumineux, mais
des EDI, vscode par exemple, ont de très
nombreux plugins permettant de vous aider à écrire vos fichiers et à détecter
les erreurs.
Exemple de playbook ansible permettant d’ajouter le repo EPEL sur Centos :
Ce playbook fait appel au module package
que j’ai documenté dans ce
billet.
Pour lancer un playbook ansible, on utilise la commande ansible-playbook
.
Pour lancer un playbook sur un inventaire, on ajoute comme pour la commande
ansible
l’argument -i avec le chemin de celui-ci. On indique ensuite la
localisation du ou des playbooks Ansible. Il est possible d’enchaîner
plusieurs groupes d’exécution sur différentes cibles. Pour cela il suffit de
rajouter des blocs Ansible -hosts. Ce qui donne :
Pour vérifier la syntaxe d’un ansible playbook, on peut ajouter
--syntax-check
et le lancer en mode dry-run avec --check
Si vous voulez écrire du code de qualité je vous recommande vivement d’installer ansible-lint et/ou spotter
Les variables Ansible
Les facts d’Ansible
Les « gather facts », sont des variables qu’Ansible recueille sur les machines
cibles : le système d’exploitation, les adresses IP, la mémoire, le disque, etc.
Ensuite, il stocke ces informations dans des variables qu’on nomme facts. Le
moyen le plus simple est de lancer le module setup sur localhost : ansible -m setup localhost
Ces variables prédéfinies peuvent ensuite être utilisées dans les playbook ansible. Pour cela, il suffit de les mettre entre doubles accolades :
Ce qui donne :
Dans le cas où vous n’utilisez pas ces facts
vous pouvez désactiver sa collecte
en ajoutant dans l’entête de votre playbook Ansible [gather_facts: no]. Cela va accélérer
l’exécution de votre playbook ansible.
Vos variables
Le plus simple pour ajouter une variable à votre playbook ansible est de définir une
section vars. Ensuite, vous pouvez les récupérer comme pour les facts
avec des
doubles accolades.
Il est possible de définir des variables Ansible dans un fichier séparé en ajoutant la
section vars_files
.
Définir des variables Ansible par group et host
Il est possible de créer des variables dans des fichiers séparés qui seront
automatiquement chargés par Ansible. Il faut qu’il se nomme host_vars
et
group_vars
et doivent se trouver là où se trouve l’inventaire.
Pour définir une variable à un host
ou un groupe spécifique, il suffit de nommer
le fichier par le nom du host
ou du groupe.
Ensuite leur syntaxe est fonction de l’extension du fichier.
Sauvegarde du résultat d’une tache Ansible dans une variable
Pour enregistrer une variable, il suffit d’ajouter à votre tache le mot-clé
register
. Dans le cas de dictionnaire, il suffit d’ajouter un .
entre les champs
(test.ping)
Les boucles Ansible
Dans certains cas, nous avons besoin de réaliser une tâche sur plusieurs cibles, par exemple installer plusieurs paquets et tout cela en une seule opération. Ansible intègre les boucles de différentes manières :
Les boucles standards : with_items ou loop
Le premier type de boucle permet de répéter une action sur une liste de valeurs, par exemple une liste de packages :
Les boucles imbriquées : with_nested
Cet exemple permettra de comprendre le fonctionnement de ce type de boucle :
Ici Ansible va boucler sur chaque utilisateur et remplira leur fichier authorized_keys avec les 3 clés définies dans la liste.
Les boucles sur dictionnaire: with_dict
Les boucles sur dictionnaires : En terminologie Python, un dictionnaire est un ensemble défini de variables possédant plusieurs valeurs :
Do until
Do until
peut être utilisé pour attendre qu’une condition soit vraie où qu’elle
ait atteint le nombre maxi d’itérations pour sortir de la boucle.
Le playbook exemple ci-dessus exécute le module shell de manière répétée jusqu’à ce que le résultat du module retourne “OK” dans sa sortie standard ou que la tâche ait été itérée 5 fois avec un délai de 10 secondes. La variable result aura également une nouvelle clé «attemps» qui aura le nombre des tentatives effectuées par la boucle.
Et bien d’autres
Il existe toute une série de type de boucle répondant à des besoins spécifiques. Si vous avez des choses complexes à construire n’hésitez pas à jeter un œil à cette section de la documentation Ansible.
Les conditions d’Ansible: when
Parfois, vous voudrez qu’une tache particulière ne s’exécute ou pas dans certaines conditions. Par exemple ne pas installer un certain paquet si le système d’exploitation correspond à une version particulière, ou encore de procéder à certaines étapes de nettoyage si un système de fichiers est saturé.
La syntaxe des conditions (and or, in, not in, is … ) reprend celle de Jinja2 qui nous servira à créer par la suite des modèles.
Il est possible de mettre des conditions en fonction de l’exécution ou de
l’échec ou du bypass d’une tache précédente. Pour ignorer une erreur d’une
tache, il suffit d’ajouter la clé ignore_errors
à true
(Ne pas utiliser dans
tous les cas).
On peut regrouper des actions utilisant les mêmes conditions en recourant à des blocks Ansible.
Jinja
Jinja est à la base un module python écrit pour Django permettant de produire rapidement du texte dynamique. L’idée principale est de fournir à un module, un modèle et une liste de valeurs pour qu’il construise dynamiquement un résultat. Jinja fournit des possibilités plus avancées, comme appliquer des filtres sur une variable, appliquer des boucles sur des listes et bien d’autre encore. Et cela va nous servir dans les playbook Ansible.
Les filtres Ansible
Les filtres dans Ansible sont utilisés pour transformer des données. Sa
syntaxe est "{{ var | filter }}"
.
D’autres sont là pour contraindre leur existence, pour assigner une valeur par défaut ou encore leur omission :
Il existe toute une série de filtres, des filtres mathématiques, pour des opérations sur des listes, les dictionnaires, les adresses mac ou ip, des [expressions régulières](/docs/developper/expressions-regulieres/, … Leur documentation se trouve dans ses trois billets : 1, 2 et 3
Si vous ne trouvez pas votre bonheur, vous pouvez développer vos propres filtres.
Les tests Ansible
Les tests en Jinja
permettent d’évaluer les expressions de modèle et de renvoyer
True
ou False
. La principale différence entre les tests et les filtres réside
dans le fait que les tests Jinja
sont utilisés à des fins de comparaison, alors
que les filtres sont utilisés pour la manipulation de données.
Ils sont tous écrits de la forme valeur is type_de_test (paramètres)
Tests sur les strings
Ansible propose trois types de recherche sur les strings : is match
, is search
et is regex
match
retourne vrai s’il trouve le motif au début de la chaîne, contrairement à
search
qui lui réussit s’il trouve le motif n’importe où dans la chaîne. Ces
tests acceptent des arguments ignorecase
et multiline
.
Les tests sur les booléens
Les tests truthy
et falsy
acceptent un paramètre facultatif appelé
convert_bool
qui tentera de convertir les indicateurs en booléens.
Comparer des versions
Pour comparer un numéro de version, il existe un test version
, On peut ainsi
vérifier que la version trouvée ansible_facts['distribution_version']
version est
supérieure ou égale à celle attendue.
Ce test accepte trois paramètres : operator
version_type
strict
- operator l’opérateur de comparaison parmi :
<, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne
- version_type défini le type de versioning et accepte les valeurs suivantes:
loose
,strict
,semver
,semantic
- strict ne peut être utilisé avec version_type
Tester si une liste contient une valeur
contains
permet de vérifier l’existence d’une valeur dans une liste. Il
accepte les filtres select
, reject
, selectattr
et rejectattr
.
Tests sur les ensembles
Pour voir si une liste inclut ou est incluse dans une autre liste, vous pouvez
utiliser subset
et superset
:
Tests sur les fichiers
Ces tests permettent de tester le type de fichiers ou son état :
Tests sur les résultats de taches
On peut controler le statut d’une tache parmi : failed
, changed
, succeeded
et skipped
Les handlers Ansible
Les handlers
d’Ansible permettent de déclencher des événements après
qu’une tâche soit passé au status changed
. En outre, bien que plusieurs tâches
puissent nécessiter une même action, l’action en question ne sera lancée
qu’après l’exécution de tous les blocs tâches.
Pour cela, il suffit d’ajouter un notify
à votre tache Ansible avec le nom du handler
et
de définir le bloc handlers
avec le même nom à la fin du fichier.
Si vous souhaitez forcer le déclenchement d’un handler
après une tache ajouter
la ligne suivante :
Escalation de privilèges
Avant tout, il faut bien se rappeler qu’Ansible utilise principalement le
protocole ssh
pour se connecter à vos machines hôtes. La bonne pratique est de
créer sur les machines hôtes un user ansible ne possédant pas les droits root
,
mais lui donner les droits sudo
.
Dans ce cas, il faudra peut-être demander à Ansible de faire ce qu’on appelle de l’escalade de privilèges.
Pour cela, il est possible d’utiliser les paramètres Ansible.
become
: mis à true
pour activer l’escalade de privilèges
become_user
: défini l’utilisateur souhaité par défaut, il s’agit de root
become_method
: la méthode d’escalade de privilège. Par défaut sudo
, mais on
peut aussi utiliser su
ou doas
. On peut aussi développer ses propres plugins
de connexion.
Exemples
Démarrer le service apache avec sudo
Utiliser le compte apache
Il est possible de définir ces paramètres directement dans les variables de groupes. Vous pouvez aussi les définir au niveau le plus haut de votre playbook ou dans la commande de lancement de votre playbook (j’aime moins) :
ou dans la ligne de commande :
Plus d’infos sur become
Debug des playbooks Ansible
Vous avez plusieurs outils à votre disposition pour débuger vos playbooks.
Utilisons ce playbook comme exemple qui ne fonctionne pas bien sur :
Le mode verbose
Un premier moyen de voir ce qui se passe lorsqu’on lance un playbook ansible,
avec d’activer le mode verbose -v
. Chaque v
supplémentaire permet à
d’augmenter la verbosité de la sortie.
Avec ce niveau, vous obtenez déja bcp d’informations.
Check_mode
Le second moyen est de lancer votre playbook ansible en mode check
. En
fait, dans ce mode :
- Les modules qui effectuent une action de modification, d’ajout ou de suppression vont uniquement vérifier que la source et la destination sont présentes, l’action ne sera pas effectuée.
- Les modules qui effectuent peut-être des actions de modifications, mais sans
possibilité de contrôle, comme modules
command
etshell
, seront ignorés. - Ceux qui ne font que rendre une information comme les modules
find
etstat
seront tout de même exécutés.
Pour contrôler, vous pouvez activer le mode diff
et vous ne verrez aucune
modification (sauf pour les tasks utilisant shell
ou command
):
Si vous voulez désactiver le mode check sur une tache, il suffit de rajouter ceci à votre task :
Si certaines de vos taches retourne des erreurs, vous pouvez ajouter ceci à ces tasks pour poursuivre le playbook:
Le mode debugger
Vous pouvez activer le mode débogage pour chaque exécution de vos playbooks en
mettant la variable enable_task_debugger
à True
dans le fichier ansible.cfg
:
Un autre moyen est de le rajouter dans votre playbook :
Lorsqu’on lance ce playbook ansible, qui plante, car le package n’existe pas, on voit apparaître cette ligne :
A partir de là, nous pouvons utiliser les commandes suivantes pour debugger :
p : affiche des informations utilisées pendant l’exécution par votre tâche module task.args[key] = value: met à jour l’argument du module. task_vars[key] = value: met à jour les variables de votre playbook. update_task: si vous modifiez les task_vars alors utilisez cette commande pour recréer la tâche à partir de votre nouvelle structure de données. redo: exécutez à nouveau la tâche. continue: passe à la tâche suivante. quit: quitte le débogueur. L’exécution du playbook est abandonnée.
Ici modifions le user le nom du package avant de relancer :
Si vous avez plusieurs hosts, il faudra le répéter autant de fois.
Relancer depuis une étape particulière
Parfois si vous voulez relancer un playbook à partir d’une étape particulière et
non depuis le début, il suffit d’utiliser l’option --start-at-task
.
Jouer un playbook de manière interactive
Avec cette option, Ansible s’arrête à chaque tâche et demande s’il doit
l’exécuter ou pas. Par exemple, si vous avez une tâche appelée configure ssh
,
l’exécution du playbook s’arrêtera et demandera :
c
permet de quitter le mode interactif !
L’utilitaire ansible-console
Je l’ai documenté dans ce billet. Cet outil permet de lancer les modules ansible de manière interactifs.
Changer la sortie d’Ansible
Si comme moi vous trouvez que la sortie standard de la commande
ansible-playbook
pas très pratique, vous pouvez en changer.
On va prendre cet exemple pour montrer les différences.
donne :
Sortie au format YAML
Il suffit dans le fichier ansible.cfg
Sortie au format JSON
D’autres formats de sortie
La liste complète de la communauté des stdout_callback Ansible
D’autres qu’il faut placer dans le répertoire plugins/callback :
Utilisation de vagrant pour provisionner des machines Ansible de test
J’utilise beaucoup vagrant pour mettre au point mes playbooks Ansible. Même si le nombre de serveurs est restreint par les ressources de votre machine hôte, c’est un excellent moyen pour finaliser vos playbooks avant de les lancer sur des machines réelles. Je vois propose de lire mon billet sur vagrant