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 tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn