
Les handlers sont des actions réactives déclenchées uniquement quand une tâche du rôle a fait changed=true et notify: un handler. C'est le pattern standard pour redémarrer un service après modification de sa config sans le redémarrer inutilement à chaque run idempotent.
Cette page démontre 3 handlers dans le rôle webserver (Restart, Reload, Notify deployment), explique la temporalité d'exécution, et enrichit le meta/main.yml pour publication Galaxy.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Écrire des handlers dans
handlers/main.yml. - Connecter les handlers via
notify:dans les tâches. - Comprendre quand les handlers s'exécutent (à la fin du play, sauf
flush_handlers). - Distinguer
RestartvsReload(différent niveau de perturbation). - Enrichir
meta/main.ymlavec galaxy_info, platforms, dependencies, allow_duplicates.
Prérequis
Section intitulée « Prérequis »- Avoir suivi Variables defaults vs vars.
- Comprendre le concept de handlers Ansible.
Le cycle handler, temporalité
Section intitulée « Le cycle handler, temporalité »1. Une tâche fait changed=true2. notify: lui associe un handler3. Ansible enregistre le handler dans une queue4. À la FIN de la section (tasks → post_tasks → roles), tous les handlers notifiés s'exécutent dans l'ordre où ils ont été DÉFINIS (pas notifiés)5. Si plusieurs notifs sur le même handler → exécuté UNE FOISConséquence : modifier 5 fichiers nginx déclenche Reload nginx une seule fois, pas 5. C'est exactement le comportement attendu.
Étape 1, Définir les handlers
Section intitulée « Étape 1, Définir les handlers »# handlers/main.yml : 3 handlers du rôle webserver---- name: Restart nginx ansible.builtin.systemd_service: name: "{{ webserver_package }}" state: restarted
- name: Reload nginx ansible.builtin.systemd_service: name: "{{ webserver_package }}" state: reloaded
- name: Notify deployment ansible.builtin.copy: dest: /var/log/deploy-notification.log content: | Deployment completed: {{ ansible_date_time.iso8601 }} Host: {{ inventory_hostname }} Webserver port: {{ webserver_listen_port }} mode: "0644"3 handlers nommés explicitement (verbe d'action). Le nom est l'identifiant utilisé par notify:.
Étape 2, Restart vs Reload, le bon choix
Section intitulée « Étape 2, Restart vs Reload, le bon choix »| Action | Quand l'utiliser |
|---|---|
state: restarted | Modification du binaire (upgrade nginx) ou modules natifs. Downtime court mais réel. |
state: reloaded | Modification de la config uniquement (nginx.conf, fichiers conf.d/). Pas de downtime, SIGHUP au processus. |
Règle pratique : config change → reloaded. Binaire change → restarted.
Étape 3, Brancher notify: dans les tâches
Section intitulée « Étape 3, Brancher notify: dans les tâches »# tasks/main.yml : démontre l'usage de notify---- name: Supprimer la conf par défaut conflictuelle ansible.builtin.file: path: /etc/nginx/conf.d/default.conf state: absent notify: Restart nginx # ← changement structurel → restart
- name: Déployer nginx.conf depuis le template ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf mode: "0644" notify: Reload nginx # ← changement de config → reload
- name: Déployer la page d'accueil ansible.builtin.copy: dest: "{{ __webserver_html_dir }}/index.html" content: "{{ webserver_index_content }}\n" mode: "0644" # PAS de notify : un changement de page HTML ne nécessite pas de reload nginx
- name: Tracer le déploiement ansible.builtin.copy: dest: /var/log/webserver-deploy.log content: "Deployed at {{ ansible_date_time.iso8601 }}\n" mode: "0644" notify: Notify deployment # ← handler custom de notificationNotez : la tâche « Déployer la page d'accueil » n'a pas de notify:. Modifier une page HTML statique ne nécessite aucun redémarrage nginx, éviter les notifs inutiles.
Étape 4, meta/main.yml enrichi
Section intitulée « Étape 4, meta/main.yml enrichi »# meta/main.yml : carte d'identité Galaxy enrichie---galaxy_info: author: Stéphane Robert namespace: stephrobert role_name: webserver description: | Installer et configurer nginx avec configuration paramétrable. Supporte le déploiement sur RHEL 9/10 et AlmaLinux 9/10. company: stephane-robert.info license: MIT min_ansible_version: "2.16" issue_tracker_url: https://github.com/stephrobert/ansible-training/issues
platforms: - name: EL versions: - "9" - "10" - name: AlmaLinux versions: - "9" - "10"
galaxy_tags: - nginx - webserver - http - rhce
dependencies: []
allow_duplicates: falseChamps essentiels :
role_name+namespace: identifiant Galaxy (stephrobert.webserver).min_ansible_version: version mini requise par votre code (vérifiable paransible --version).platforms: OS + versions supportés. Galaxy filtre les recherches selon ces tags.galaxy_tags: mots-clés pour la recherche utilisateur.issue_tracker_url: où signaler les bugs.allow_duplicates: false: empêche d'inclure 2 fois le rôle dans le même play (warning sinon).
dependencies:, chaîner les rôles
Section intitulée « dependencies:, chaîner les rôles »dependencies: - role: stephrobert.firewalld version: ">=1.0.0" vars: firewalld_zones: - publicAvant que tasks/main.yml du rôle s'exécute, les dépendances sont jouées. Pratique pour : firewall avant service web, selinux avant httpd. Couvert en détail au lab roles/dependencies.
meta: flush_handlers, forcer l'exécution immédiate
Section intitulée « meta: flush_handlers, forcer l'exécution immédiate »Si vous ne pouvez pas attendre la fin du play pour qu'un handler tourne (ex : tester nginx après reload, dans le même play) :
- name: Modifier la conf ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf mode: "0644" notify: Reload nginx
- name: Forcer le reload IMMÉDIATEMENT ansible.builtin.meta: flush_handlers
- name: Tester que la nouvelle conf répond ansible.builtin.uri: url: http://localhost status_code: 200Sans flush_handlers, le test tournerait avant le reload, donc sur l'ancienne config.
Mettre en pratique
Section intitulée « Mettre en pratique »Un handler ne se vérifie pas dans le code du rôle, mais dans la trace qu'il laisse sur la machine. Le lab étend le rôle webserver avec trois handlers, dont un qui écrit un fichier de journal, puis vous fait déployer sur db1.lab en port 8080. Les tests relisent ces fichiers pour prouver quels handlers ont réellement été notifiés, et que la tâche laissée sans notify: n'en a déclenché aucun. Vous complétez ensuite meta/main.yml avec les champs galaxy_info attendus par Galaxy.
Pièges courants
Section intitulée « Pièges courants »| Symptôme | Cause | Fix |
|---|---|---|
| Handler ne se déclenche pas | Tâche en ok (pas changed) | Normal, un handler ne tourne que sur changed |
| Handler tourne 5× | Pas possible, Ansible déduplique automatiquement | Vérifier que les 5 tâches notifient le même nom de handler |
notify: ignoré silencieusement | Faute de frappe sur le nom | Restart nginx ≠ Restart Nginx (case-sensitive) |
| Service redémarre à chaque run | state: restarted au lieu de reloaded | Préférer reloaded pour changements de config |
| Test post-reload échoue | Reload pas encore fait | Ajouter meta: flush_handlers avant le test |
À retenir
Section intitulée « À retenir »- Handlers = actions réactives, déclenchées uniquement par
notify:+changed=true. RestartvsReload: restart = downtime, reload = pas de downtime (SIGHUP).- Handlers s'exécutent à la fin de leur section (tasks/post_tasks/etc.).
- Déduplication automatique : 5× notify → 1× exécution.
meta: flush_handlersforce l'exécution immédiate dans la même section.meta/main.yml= carte d'identité Galaxy : platforms, tags, dependencies, allow_duplicates.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Introduction au TDD avec Molecule : un handler qui ne se déclenche pas est un défaut que seul un test attrape.
- Consommer un rôle : ce qui change pour les handlers selon qu'on utilise
import_roleouinclude_role. - Dépendances entre rôles : la suite directe de
meta/main.yml, avec l'ordre de chargement etallow_duplicates.