Skip to content

Playbooks

Les playbooks sont les points d’entrée d’Ansible. Ce sont ces fichiers que l’on va invoquer avec Ansible pour exécuter des ensembles de tâches.

Ils ont un format spécifique qui va décrire :

  • sur quels hôtes de l’inventaire seront appliqué les tâches
  • les tâches à exécuter

La structure de base obligatoire d’un playbook Ansible est la suivante :

---
- name: 'Mon playbook'
hosts:
- machine1
- groupe1
- …

On retrouve les clefs suivantes :

  • name: Le nom du playbook
  • hosts: La liste des hôtes et/ou groupes de l’inventaire sur lequel va être joué le playbook

Ces informations vont permettre de définir le contexte d’exécution du playbook.

Il existe plusieurs mots clefs pour déclencher des exécutions.

On va en voir une partie, sans être exhaustif.

Clef Format Description
tasks Array Liste de tâches à exécuter
roles Array Liste de rôles à exécuter
handlers Array Liste de tâches à appeler dans des cas spécifiques
vars Forme libre Liste de variable qui seront spécifiques au playbook
pre_tasks Array Liste de tâches à exécuter avant toutes autre action (tasks, roles ou handlers)
post_tasks Array Liste de tâches à exécuter après toutes autre action (tasks, roles ou handlers)

Vous voyez ici deux notions qui n’ont pas été abordée par le moment :

  • roles: On y reviendra ultérieurement dans une autre page de documentation
  • handlers : Ce sont des tâches un peu spécifiques. Elles ont exactement le même format et utilisent les mêmes modules que ceux que vous pouvez utiliser dans vos tasks normales. La différence est qu’elles ne seront appelées qu’à deux conditions :
    • Uniquement en fin d’exécution du playbook ou sur appel volontaire de la meta flush_handlers
    • Uniquement suite à une tâche qui a impliqué une modification sur laquelle a été définie la notion de notify

Ne vous inquiétez pas, la notion de handlers peut rester flou jusque-là, on y revient dans très peu de temps.

Prenons le cas simple où l’on veut installer le paquet vim sur l’ensemble de nos machines. On va pour celà créer notre fichier d’inventaire qui va lister l’ensemble des machines, et le fichier de playbook qui va exécuter les tâches à notre place :

all:
hosts:
w1.example.org:
w2.example.org:
d1.example.org:
children:
webservers:
hosts:
w1.example.org:
w2.example.org:

Avec ces fichiers (en ayant adapté l’inventaire à votre environnement), vous pouvez exécuter le playbook pour installer vim sur les machines de votre inventaire :

Terminal window
ansible-playbook -i inventory.yaml basic-playbook.yaml

Vous devriez obtenir un résultat similaire à ça :

Terminal window
PLAY [Installation de vim sur l’ensemble des machines] *****************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [w2.example.org]
ok: [w1.example.org]
ok: [d1.example.org]
TASK [Installation de vim] *********************************************************************************************
ok: [d1.example.org]
changed: [w2.example.org]
changed: [w1.example.org]
PLAY RECAP *************************************************************************************************************
w1.example.org : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
d1.example.org : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
w2.example.org : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

La première tâche Gathering Facts assure la collecte des facts sur les équipements. Cette tâche est implicite ce n’est pas vous qui la déclarez, et elle sera toujours la première tâche à être effectuée lors de l’exécution d’un playbook.

Dans mon cas, la machine d1.example.org avait déjà le paquet vim d’installé, ce qui fait que la tâche d’installation n’a pas généré de changement et est en status ok. Pour les autres machines, la tâche d’installation a bien généré un changement (l’installation du paquet), et donc le status est passé en changed.

Je vous ai parlé dans les Mots clef des handlers. Les handlers sont des tâches un peu particulières parce qu’elles sont considéré comme réutilisables.

Ces tâches ne sont pas exécutées dans le déroulé classique de votre playbook, elle ne seront exécuté que sous deux conditions :

  • Si elles ont été notifiée par une autre tâche (j’y reviens juste après)
  • Soit en fin de playbook, soit sur appel d’une meta tâche flush_handlers

L’utilité des handlers se retrouve à plusieurs niveaux :

  • L’installation d’un nouveau service pour s’assurer d’un état en fin d’exécution
  • Suite à la modification de la configuration d’un service pour s’assurer de correctement relancer le service

Un même handler peut être notifié plusieurs fois par des tâches différentes, il ne sera joué qu’une seule fois.

Les notifications aux handlers s’effectuent au travers du mot clef notify qui sera à adosser à une tâche standard. L’effet qu’aura le notify sera d’enregistrer la nécessité d’exécuter un handler. Il ne sera activé que si une tâche est en état changed. Dans tous les autres cas, la notification n’aura pas lieu et le handler ne sera pas exécuté.

Prenons le cas où vous souhaitez déployer un serveur web.

En premier lieu, vous allez installer les paquets nécessaires. Vous pourrez à ce moment notifier un handler qui va s’assurer que le service apache2 est démarré, et activé au démarrage de la machine.

Le playbook ressemblerait à celà :

---
- name: 'Installe et configure apache2'
hosts:
- webservers
tasks:
- name: 'Installation du paquet apache2'
become: true
ansible.builtin.package:
name: apache2
state: present
- name: 'Redemarre et active apache2'
become: true
ansible.builtin.systemd_service:
name: apache2
state: restarted
daemon-reload: true
enabled: true

On veut ensuite modifier le fichier de configuration d’apache2 ce qui nécessitera un redémarrage.

Voici les playbooks avec et sans handlers :

---
- name: 'Installe et configure apache2'
hosts:
- webservers
tasks:
- name: 'Installation du paquet apache2'
become: true
ansible.builtin.package:
name: apache2
state: present
- name: 'Modification de la configuration de apache2'
become: true
ansible.builtin.template:
content: "{{ ansible_managed }}"
dest: '/etc/apache2/fake_config.conf'
- name: 'Redemarre et active apache2'
become: true
ansible.builtin.systemd_service:
name: apache2
state: restarted
daemon-reload: true
enabled: true

Dans le cas du playbook sans la gestion du handler, Ansible va systématiquement redémarrer apache2 à chaque rejeu. Dans le cas du playbook avec le handler, ce dernier ne sera exécuté qui si apache2 a été installé ou si la configuration a été modifiée pendant l’exécution du playbook.

Celà évite des actions inutiles pendant l’exécution, et surtout ça évite une rupture de service à l’exécution du playbook.

Si vous exécutez le playbook avec ou sans handler une première fois, vous devriez avoir les résultats suivants :

Terminal window
PLAY [Installe et configure apache2] ***********************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [w2.example.org]
ok: [w1.example.org]
TASK [Installation du paquet apache2] **********************************************************************************
changed: [w2.example.org]
changed: [w1.example.org]
TASK [Modification de la configuration de apache2] *********************************************************************
changed: [w2.example.org]
changed: [w1.example.org]
TASK [Redemarre et active apache2] *************************************************************************************
changed: [w2.example.org]
changed: [w1.example.org]
PLAY RECAP *************************************************************************************************************
w1.example.org : ok=1 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
w2.example.org : ok=1 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Sur la première exécution, les résultats sont les mêmes, c’est normal vu que les machines n’avaient ni les paquets, ni le fichier que l’on pousse avec ansible.

Si vous le relancez une deuxième fois sans modifications, vous aurez le résultat suivant :

Terminal window
PLAY [Installe et configure apache2] ***********************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [w2.example.org]
ok: [w1.example.org]
TASK [Installation du paquet apache2] **********************************************************************************
ok: [w2.example.org]
ok: [w1.example.org]
TASK [Modification de la configuration de apache2] *********************************************************************
ok: [w2.example.org]
ok: [w1.example.org]
TASK [Redemarre et active apache2] *************************************************************************************
changed: [w2.example.org]
changed: [w1.example.org]
PLAY RECAP *************************************************************************************************************
w1.example.org : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
w2.example.org : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Ce que l’on constate, c’est que dans le cas où il n’y a pas de handler, le service apache2 est quand même redémarré par Ansible.

Dans le cas où on a mis en place le handler, Ansible n’ayant pas détecté de modification sur la machine, il n’a pas de raison de redémarrer le service apache2, et de fait, le handler n’est pas exécuté.

Lorsque vous écrivez des playbooks long et/ou complexes, il peut s’avérer nécessaire d’exécuter les handlers avant la fin de l’exécution du playbook.

Ansible prévoit une méthode pour forcer l’exécution des handlers en cours d’exécution.

Attention: L’utilisation de cette méthode contraint l’exécution de l’ensemble des handlers qui ont été notifiés. Il est donc nécessaire d’avoir bien conscience de qu’est-ce qui va être joué lors de l’appel.

Pour forcer l’exécution des handlers, le module à appeler est meta :

…
- name: 'Exécution forcée des handlers'
ansible.builtin.meta: flush_handlers
…

Personnellement, j’ai tendance à mettre quasi systématiquement une tâche pour forcer l’exécution des handlers à la fin de mes playbooks.

La raison pour laquelle je fais celà, et qu’assez régulièrement, je chaine plusieurs playbooks à la suite dans un même fichier. Le fait d’ajouter une tâche terminale pour forcer l’exécution des handlers m’assure que l’ensemble de ce qui doit être joué dans un playbook est correctement effectué avant de passer au suivant.