
La délégation Ansible permet d'exécuter une tâche sur un autre hôte que celui ciblé par le play, ou de ne l'exécuter qu'une seule fois quel que soit le nombre d'hôtes. Cas typique : pendant un déploiement sur 50 webservers, vous devez drainer chaque hôte du load-balancer (commande à lancer sur le LB, pas sur le webserver). C'est l'usage exact de delegate_to. De même, une migration de base de données ne doit tourner qu'une fois même si 5 webservers la déclenchent, c'est run_once.
Cette page couvre les trois mécanismes de délégation : delegate_to (rediriger vers un autre hôte), run_once (exécuter une seule fois), local_action (raccourci pour cibler le control node), avec les cas concrets de production où vous en aurez besoin.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »delegate_to: exécuter une tâche sur un hôte différent duhosts:du play ;run_once: forcer l'exécution sur un seul hôte quel que soit le nombre cible ;local_action: raccourci pourdelegate_to: localhost;delegate_facts: true: faire en sorte que les facts récupérés s'appliquent à l'hôte délégué ;- Les patterns de production : drain LB, migration DB one-shot, écriture inventaire dynamique.
delegate_to, exécuter ailleurs
Section intitulée « delegate_to, exécuter ailleurs »Une tâche dans un play hosts: webservers s'exécute sur chaque webserver. Avec delegate_to, vous redirigez l'exécution :
- name: Drainer du load-balancer hosts: webservers serial: 1 tasks: - name: Marquer comme drain dans HAProxy l'hôte {{ inventory_hostname }} ansible.builtin.shell: | set -o pipefail echo "disable server backend/{{ inventory_hostname }}" \ | socat stdio /var/run/haproxy.sock args: executable: /bin/bash delegate_to: lb1.lab # ← exécution SUR lb1, mais en boucle pour chaque webserver changed_when: trueLe play boucle sur chaque webserver mais chaque itération exécute la commande sur lb1.lab, en injectant {{ inventory_hostname }} (le webserver courant) dans la commande. Résultat : le LB drainera web1, puis web2, puis web3 successivement.
delegate_to accepte un hôte de l'inventaire (lb1.lab), un groupe (avec groups['lbs'][0]), ou une adresse IP littérale.
run_once, une seule exécution
Section intitulée « run_once, une seule exécution »Une tâche en run_once: true ne s'exécute que sur le premier hôte du play, peu importe combien d'hôtes sont ciblés :
- name: Migration de base hosts: webservers tasks: - name: Lancer la migration SQL (UNE SEULE FOIS) ansible.builtin.command: /usr/local/bin/migrate.sh run_once: true delegate_to: db1.lab # ← combiné : 1 fois, sur db1 changed_when: truerun_once seul exécute la tâche sur un webserver au hasard (le premier de la liste). Combiné à delegate_to: db1.lab, la tâche tourne une seule fois sur db1. C'est le pattern migration one-shot.
local_action, raccourci pour le control node
Section intitulée « local_action, raccourci pour le control node »local_action est un alias pour delegate_to: localhost :
- name: Récupérer un certificat depuis l'API locale ansible.builtin.uri: url: https://ca.local/issue delegate_to: localhost # la forme à utiliserLa forme raccourcie existe et s'écrit local_action: ansible.builtin.uri url=https://ca.local/issue, sur une seule ligne. Elle fonctionne toujours, vérifié sur ansible-core 2.20 : le moteur l'exécute sans émettre le moindre avertissement de dépréciation.
Elle est pourtant à éviter, pour deux raisons. Elle mélange deux syntaxes, les paramètres passant en clé=valeur au lieu du YAML habituel, ce qui casse la lecture d'un playbook. Et ansible-lint la refuse en profil production, sous la règle deprecated-local-action : un playbook qui l'emploie ne passera pas votre intégration continue. Retenez delegate_to: localhost, qui dit exactement la même chose en restant homogène avec le reste de vos tâches.
Cas typique : interroger une API qui n'est joignable que depuis votre poste, générer un fichier qui sera ensuite distribué via copy:.
delegate_facts, où vont les facts
Section intitulée « delegate_facts, où vont les facts »Quand vous faites gather_facts: ou exécutez un module qui retourne des facts (setup, command avec register:), ces facts sont rattachés à l'hôte courant par défaut. Avec delegate_facts: true, ils sont rattachés à l'hôte délégué :
- name: Récupérer la config depuis lb1 ansible.builtin.command: cat /etc/haproxy/haproxy.cfg delegate_to: lb1.lab delegate_facts: true # le résultat va dans hostvars['lb1.lab'] register: lb_config changed_when: falseSans delegate_facts: true, le résultat va dans hostvars['<hôte courant>'], ce qui est rarement ce que vous voulez quand la donnée concerne lb1.
Patterns de production
Section intitulée « Patterns de production »Drain de load-balancer (rolling deploy)
Section intitulée « Drain de load-balancer (rolling deploy) »Le pattern complet pour un rolling deploy avec drain :
- name: Déployer en rolling avec drain HAProxy hosts: webservers serial: 1 become: true
pre_tasks: - name: Drainer de HAProxy l'hôte {{ inventory_hostname }} ansible.builtin.shell: | set -o pipefail echo "disable server backend/{{ inventory_hostname }}" \ | socat stdio /var/run/haproxy.sock args: executable: /bin/bash delegate_to: lb1.lab changed_when: true
- name: Attendre la fin des connexions actives ansible.builtin.wait_for: timeout: 30
tasks: - name: Déployer la version cible de l'application ansible.builtin.dnf: name: "app-{{ app_version }}" state: present
post_tasks: - name: Réintégrer dans HAProxy l'hôte {{ inventory_hostname }} ansible.builtin.shell: | set -o pipefail echo "enable server backend/{{ inventory_hostname }}" \ | socat stdio /var/run/haproxy.sock args: executable: /bin/bash delegate_to: lb1.lab changed_when: trueserial: 1 + delegate_to: lb1.lab = un webserver à la fois est drainé, déployé, réintégré.
Migration de schéma DB one-shot
Section intitulée « Migration de schéma DB one-shot »Plusieurs webservers démarrent l'application, la migration de schéma DB ne doit tourner qu'une fois :
- name: Déploiement applicatif hosts: webservers tasks: - name: Lancer la migration de schéma ansible.builtin.command: /opt/app/bin/migrate run_once: true delegate_to: db1.lab # une fois, sur la DB register: migration_result changed_when: true
- name: Démarrer l'application sur chaque webserver ansible.builtin.systemd: name: app state: started when: migration_result is definedInventaire dynamique mis à jour à la volée
Section intitulée « Inventaire dynamique mis à jour à la volée »Après création d'une VM, vous voulez l'ajouter à l'inventaire en mémoire pour le reste du playbook :
- name: Provisionner et configurer une nouvelle VM hosts: localhost tasks: - name: Créer la VM community.libvirt.virt: name: web3 state: running
- name: Ajouter web3 à l'inventaire en mémoire ansible.builtin.add_host: name: web3.lab groups: webservers ansible_host: 10.10.20.23
- name: Configurer la nouvelle VM hosts: web3.lab tasks: - name: Installer nginx ansible.builtin.dnf: name: nginxLe second play utilise l'hôte créé dynamiquement par le premier, sans avoir à réécrire l'inventaire fichier.
Pièges fréquents
Section intitulée « Pièges fréquents »| Symptôme | Cause | Fix |
|---|---|---|
delegate_to suivi mais facts ne s'appliquent pas | Manque delegate_facts: true | Ajouter delegate_facts: true si le but est de récupérer des facts pour l'hôte délégué |
run_once exécute sur le mauvais hôte | Sans delegate_to, run_once prend le premier de la liste (peut varier) | Toujours combiner run_once: true + delegate_to: <hôte> |
delegate_to: localhost mais SSH essaie de se connecter | connection: local n'est pas implicite | Ajouter connection: local ou utiliser local_action |
Variable inventory_hostname montre localhost au lieu du webserver | Vous lisez sur la tâche déléguée, inventory_hostname est l'hôte d'origine, pas la cible déléguée | Utiliser delegate_to_host (la cible) si besoin |
delegate_to: sur un hôte hors inventaire | Ansible ne sait pas comment se connecter | Ajouter via add_host: ou définir l'hôte dans l'inventaire |
À retenir
Section intitulée « À retenir »delegate_to: <hôte>redirige l'exécution d'une tâche vers un autre hôte, pattern drain LB.run_once: trueforce l'exécution sur un seul hôte (le premier), combiner avecdelegate_to:pour fixer la cible.local_action:est l'alias dedelegate_to: localhost(préférer la forme explicite).delegate_facts: truerattache les facts à l'hôte délégué (utile pour lesregister:sur un hôte tiers).add_host:ajoute un hôte à l'inventaire en mémoire pour le reste du playbook (utile après provisioning dynamique).
Mettre en pratique
Section intitulée « Mettre en pratique »La délégation se comprend en regardant où le fichier atterrit. Le lab vous fait rediriger une tâche vers un autre hôte avec delegate_to, la restreindre à une seule exécution dans un play multi-hôtes avec run_once, puis viser le control node avec local_action. La validation compte les fichiers posés sur chaque machine, ce qui rend une erreur de cible immédiatement visible.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Facts et magic vars :
hostvarsetinventory_hostnamepour lire les données de l'hôte d'origine depuis l'hôte délégué. - register et set_fact : comprendre où atterrit une variable capturée quand
delegate_facts:entre en jeu. - Block / rescue / always : remettre l'hôte dans le load balancer même quand la mise à jour échoue.