Aller au contenu
Infrastructure as Code medium

Parallélisme et stratégies Ansible : forks, serial, throttle, strategy

15 min de lecture

Logo Ansible

Par défaut, Ansible exécute chaque tâche sur 5 hôtes en parallèle, simultanément, et attend que tous finissent avant de passer à la tâche suivante. Ce comportement marche bien pour 5-20 hôtes, mais devient problématique sur grande échelle (paralléliser plus pour aller vite) ou en production sensible (serializer les hôtes pour faire un rolling update). Cette page couvre les 5 leviers de parallélisme d'Ansible : forks, serial, throttle, strategy, max_fail_percentage, et explique quand utiliser lequel.

C'est un terrain où la performance et la fiabilité entrent souvent en conflit. Bien tuner ces paramètres permet de descendre un déploiement de 30 minutes à 5 minutes sur 200 hôtes, ou de garantir un rolling update sans coupure sur 50 webservers.

  • forks : combien de connexions SSH parallèles ouvre Ansible ;
  • serial : traiter les hôtes par lots séquentiels (rolling update) ;
  • throttle : limiter une tâche spécifique à N hôtes simultanés ;
  • strategy : comment Ansible synchronise entre hôtes : linear, free, host_pinned, debug ;
  • max_fail_percentage : circuit breaker sur un déploiement.

forks détermine combien de hôtes Ansible traite en parallèle pour chaque tâche. Configuration au choix :

# ansible.cfg
[defaults]
forks = 20
Fenêtre de terminal
# CLI
ansible-playbook site.yml -f 20
Fenêtre de terminal
# Variable d'environnement
ANSIBLE_FORKS=20 ansible-playbook site.yml

Valeur par défaut : 5. Pour aller vite sur grande fleet, on monte à 20-50 :

Taille de fleetforks recommandé
< 20 hôtes5 (défaut, OK)
20-100 hôtes20-30
100-500 hôtes50
> 500 hôtes100+ (avec mitogen pour aller plus loin)

serial impose à Ansible de traiter les hôtes par lots séquentiels. Indispensable pour un rolling update qui ne doit pas tout casser d'un coup :

- name: Déployer nginx en rolling
hosts: webservers
serial: 1 # un hôte à la fois
tasks:
- name: ...

Variantes :

serial: 1 # lot de 1 hôte
serial: "20%" # lot = 20% du groupe (arrondi)
serial: [1, 5, 20] # lot 1 : 1 hôte, lot 2 : 5, lots suivants : 20
serial:
- 1 # idem mais en YAML multiligne
- "10%"
- "100%"

La stratégie de canary classique : [1, 10, 100]. Le premier lot teste sur 1 hôte (canary), le deuxième sur 10 (validation), puis 100% si tout va bien.

serial: + pre_tasks/post_tasks = rolling zero-downtime

Section intitulée « serial: + pre_tasks/post_tasks = rolling zero-downtime »

Le pattern complet pour un rolling update sans coupure de service :

- name: Déployer en rolling
hosts: webservers
serial: 1
become: true
pre_tasks:
- name: Drainer du load-balancer
community.general.consul_kv:
key: "lb/weight/{{ inventory_hostname }}"
value: "0"
tasks:
- name: Déployer la nouvelle version
ansible.builtin.dnf:
name: nginx
state: latest
- name: Vérifier que nginx redémarre OK
ansible.builtin.wait_for:
port: 80
timeout: 30
post_tasks:
- name: Réintégrer dans le LB
community.general.consul_kv:
key: "lb/weight/{{ inventory_hostname }}"
value: "100"

Sur 5 webservers, le déploiement prend 5x plus de temps mais aucun client ne voit de coupure.

throttle: limite une tâche à N hôtes simultanés, sans toucher au reste du play :

tasks:
- name: Tâche normale (parallélisée selon forks)
ansible.builtin.dnf:
name: nginx
state: present
- name: Tâche qui martèle un service externe (limiter à 2 simultanés)
ansible.builtin.uri:
url: "https://api.externe.com/register/{{ inventory_hostname }}"
method: POST
throttle: 2
- name: Tâche normale (à nouveau parallélisée)
ansible.builtin.systemd:
name: nginx
state: started

Cas typique : une API externe avec rate limiting, une base de données avec écritures coûteuses, une registry Docker privée.

Quatre stratégies disponibles :

Tous les hôtes exécutent la même tâche en parallèle. Ansible attend que tous finissent avant de passer à la tâche suivante :

Tâche 1 : web1 → web2 → web3 (en parallèle, attend tous)
Tâche 2 : web1 → web2 → web3 (en parallèle, attend tous)
Tâche 3 : web1 → web2 → web3 (en parallèle, attend tous)

Comportement prévisible : à chaque instant, tous les hôtes sont sur la même tâche. C'est ce que vous voulez 99% du temps.

Chaque hôte avance à son rythme, sans synchro entre tâches :

- hosts: webservers
strategy: free
tasks:
- name: Compiler une grosse lib (long)
ansible.builtin.shell: make -j4

Si web1 finit en 2 min et web2 en 5 min, web1 passe à la tâche 2 sans attendre web2. Gain de temps important quand les tâches sont longues et indépendantes (build, compilation, tests).

Inconvénient : la sortie devient désordonnée (output de tâches différentes intercalées) et le diagnostic plus complexe.

Variante de free : chaque hôte est pinné à un worker dédié. Les hôtes avancent à leur rythme mais on garantit qu'un même hôte n'est pas pris par plusieurs workers concurrents. Cas marginal, à utiliser uniquement si vous avez un problème spécifique.

Mode interactif : Ansible exécute une tâche à la fois et attend votre Enter avant de passer à la suivante :

Fenêtre de terminal
ANSIBLE_STRATEGY=debug ansible-playbook site.yml

Quand une tâche échoue, vous tombez dans un mini-shell Ansible qui permet de :

  • Réexécuter la tâche (r) ;
  • Continuer au suivant (c) ;
  • Quitter (q) ;
  • Inspecter une variable (p var_name).

Indispensable pour développer un playbook complexe en mode pas-à-pas.

Sans max_fail_percentage:, le play continue tant qu'au moins un hôte réussit. Avec :

- name: Déployer
hosts: webservers
serial: 1
max_fail_percentage: 0 # arrêter dès la 1ère erreur
max_fail_percentage: 30 # arrêter si > 30% des hôtes ont échoué

Combiné avec serial:, c'est le circuit breaker d'un déploiement : vous n'écrivez pas une bug sur l'ensemble de la fleet. Si web1 plante, web2 ne reçoit pas le déploiement.

Un playbook prod typique sur 100 hôtes :

- name: Déploiement applicatif
hosts: webservers # 100 hôtes
serial: "10%" # lots de 10 hôtes
max_fail_percentage: 5 # arrêter si > 5 hôtes du lot ont échoué
strategy: linear # synchronisation par tâche
tasks:
- name: Drainer du LB
community.general.consul_kv: ...
- name: Déployer
ansible.builtin.dnf:
name: app
state: latest
- name: API call qui rate-limite
ansible.builtin.uri:
url: ...
throttle: 3 # max 3 calls API simultanés

Lancement avec forks adapté :

Fenêtre de terminal
ansible-playbook site.yml -f 20

10 hôtes en parallèle par lot, 10 lots, soit 100 hôtes traités. Avec max_fail_percentage: 5, si plus de 5 hôtes du lot courant échouent, le déploiement s'arrête net, on n'envoie pas la même erreur sur les 90 hôtes suivants.

SymptômeCauseFix
forks = 100 ralentit le posteSaturation mémoire / FD côté control nodeRéduire à 20-50 ou utiliser un Execution Environment dédié
serial: ne change rienVous avez un seul hôte cible (ou --limit web1)serial: ne s'applique qu'à un groupe avec plusieurs hôtes
strategy: free rend les logs illisiblesC'est par design (hôtes désynchronisés)Utiliser linear sauf gain de temps massif
throttle: ignoréthrottle: 0 ou non défini = pas de limiteVérifier la valeur, doit être ≥ 1
max_fail_percentage: 100 ne s'arrête jamais100% = jamais d'arrêtPour arrêter à la 1ère erreur : max_fail_percentage: 0
Un play qui suit ne tourne pas après échecC'est attendu : le play d'après ne démarre que si le précédent a au moins un hôte OKVérifier la convention multi-plays
  • forks (CLI -f ou ansible.cfg) contrôle le nombre de connexions SSH parallèles. Défaut 5, montez à 20-50 pour grande fleet.
  • serial: réalise un rolling update par lots (1 hôte, 20%, ou patterns [1, 10, 100]).
  • throttle: limite une tâche spécifique à N hôtes simultanés (API externe, DB, registry).
  • strategy: linear (défaut) synchronise par tâche ; free désynchronise pour aller plus vite ; debug active le pas-à-pas interactif.
  • max_fail_percentage: est le circuit breaker, combiné avec serial:, vous évitez de propager un bug à toute la fleet.

Cette page a un lab d'accompagnement : labs/ecrire-code/parallelisme-strategies/ dans stephrobert/ansible-training. Il contient un README.md guidé, un Makefile (make verify lance les tests), et un challenge final auto-évalué : rolling update avec serial:1 + max_fail_percentage:0 sur webservers (timestamps croissants).

Une fois le lab provisionné :

Fenêtre de terminal
cd ~/Projets/ansible-training/labs/ecrire-code/parallelisme-strategies/
cat README.md # tuto pas à pas
cat challenge/README.md # consigne du challenge final
pytest -v challenge/tests/ # lancer les tests testinfra

Si les tests passent, vous maîtrisez les concepts couverts dans ce guide. En cas de blocage, docs/troubleshooting.md à la racine du repo couvre les pièges fréquents (rate-limit SSH, clé absente, collection manquante).

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn