Passer au contenu principal

Fondamentaux (Ansible)

Les handlers

Les handlers résolvent un problème classique : redémarrer un service uniquement si sa configuration a changé, et uniquement une fois même si plusieurs tâches ont déclenché la demande. Sans handlers, on se retrouve à relancer nginx trois fois au cours d'un play, ou à le redémarrer même quand rien n'a changé.

Principe de fonctionnement

Un handler est une tâche spéciale déclarée dans une section handlers. Il n'est exécuté que si :

  1. Au moins une tâche du play a émis un notify vers ce handler
  2. La tâche qui a notifié a le statut changed (pas ok ni skipped)

L'exécution se fait à la fin du play, une seule fois, quel que soit le nombre de tâches qui l'ont notifié.

Syntaxe de base

---
- name: Configurer nginx
  hosts: web
  become: true

  tasks:
    - name: Déployer la configuration nginx
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: Recharger nginx

    - name: Déployer le vhost
      ansible.builtin.template:
        src: vhost.conf.j2
        dest: /etc/nginx/sites-available/myapp
      notify: Recharger nginx     # même handler, déclenché qu'une seule fois

  handlers:
    - name: Recharger nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

Si les deux tâches de configuration ont le statut changed, le handler "Recharger nginx" ne sera quand même exécuté qu'une seule fois, à la fin du play.

Notifier plusieurs handlers

- name: Mettre à jour la configuration SSL
  ansible.builtin.copy:
    src: cert.pem
    dest: /etc/ssl/certs/myapp.pem
  notify:
    - Recharger nginx
    - Mettre à jour le monitoring

Chaîner les handlers

Un handler peut lui-même notifier un autre handler :

handlers:
  - name: Recompiler l'application
    ansible.builtin.command:
      cmd: make -C /srv/myapp
    notify: Redémarrer l'application

  - name: Redémarrer l'application
    ansible.builtin.systemd:
      name: myapp
      state: restarted

flush_handlers : forcer l'exécution immédiate

Parfois, on a besoin qu'un handler s'exécute en milieu de play, avant les tâches suivantes. Par exemple, redémarrer un service avant de tester qu'il répond correctement :

tasks:
  - name: Déployer la configuration
    ansible.builtin.template:
      src: app.conf.j2
      dest: /etc/myapp/app.conf
    notify: Redémarrer myapp

  - name: Forcer l'exécution des handlers en attente
    ansible.builtin.meta: flush_handlers

  - name: Vérifier que l'application répond
    ansible.builtin.uri:
      url: http://localhost:8080/health
      status_code: 200

Pièges classiques

Handler non déclenché si la tâche est skippée : si une tâche avec when: false est skippée, elle n'émet pas de notify même si le handler y est listé.

Handler avec statut ok : si une tâche n'a rien changé (statut ok), son notify n'est pas pris en compte. C'est le comportement voulu pour l'idempotence.

Handlers dans les rôles : les handlers définis dans un rôle ne sont visibles que depuis ce rôle. Pour notifier un handler d'un rôle depuis un autre rôle, passer par listen :

handlers:
  - name: Recharger nginx
    ansible.builtin.service:
      name: nginx
      state: reloaded
    listen: "nginx config changed"   # alias écoutable par n'importe quelle tâche