Réglage des forks Ansible : équilibrer la concurrence et la consommation de ressources

Réglez les forks Ansible en toute sécurité en mesurant la concurrence, la charge du nœud de contrôle, la pression sur les nœuds cibles et le risque de déploiement.

Réglage des forks Ansible : équilibrer la concurrence et la consommation de ressources

La force d'Ansible réside dans sa nature sans agent et sa capacité à gérer de nombreux hôtes simultanément. Cette concurrence est principalement régie par le paramètre forks. Un réglage approprié du paramètre forks est essentiel pour obtenir un débit optimal dans vos tâches d'automatisation. Trop peu de forks, et vos playbooks s'exécutent lentement ; trop, et vous risquez de submerger votre nœud de contrôle ou les nœuds gérés eux-mêmes.

Cet article sert de guide pratique pour comprendre ce que sont les forks Ansible, comment ils impactent les performances et la méthodologie pour définir la valeur optimale pour votre environnement spécifique. Nous explorerons où définir ce paramètre et les compromis impliqués par une concurrence agressive.

Comprendre les forks Ansible

Dans la terminologie Ansible, un fork représente un processus Python séparé généré par le nœud de contrôle Ansible pour gérer une connexion à un seul hôte géré simultanément. Lorsque vous exécutez un playbook, Ansible lance jusqu'au nombre de processus défini par forks pour exécuter des tâches en parallèle sur votre inventaire.

Pourquoi les forks sont importants pour les performances

La concurrence est la clé de la vitesse d'Ansible. Si vous avez 100 serveurs à mettre à jour, définir forks = 100 signifie qu'Ansible tente de se connecter à tous en même temps (sous réserve des limites de connexion et des délais d'attente). Cependant, ce parallélisme a un coût :

  1. Consommation de ressources du nœud de contrôle : Chaque fork consomme du CPU et de la mémoire sur la machine exécutant Ansible (le nœud de contrôle). Un nombre élevé de forks peut priver le nœud de contrôle, entraînant des performances médiocres, une latence accrue et des plantages potentiels.
  2. Charge du nœud géré : Des connexions rapides peuvent submerger les commutateurs réseau ou les hôtes gérés eux-mêmes s'ils sont déjà sous forte charge ou disposent de ressources CPU limitées pour gérer les connexions SSH entrantes et l'exécution des tâches.

Où configurer le paramètre forks

La valeur forks peut être configurée à plusieurs endroits, remplaçant les paramètres précédents dans un ordre en cascade. Comprendre cette hiérarchie est essentiel pour un comportement cohérent entre différents projets et environnements.

1. Le fichier de configuration Ansible (ansible.cfg)

L'emplacement principal et persistant pour définir les valeurs par défaut à l'échelle du système est le fichier ansible.cfg. On le trouve généralement dans /etc/ansible/ansible.cfg (à l'échelle du système) ou dans le répertoire racine de votre projet (spécifique au projet).

Pour définir le niveau de concurrence par défaut, modifiez la section [defaults] :

# Extrait de ansible.cfg
[defaults]
# Définit le nombre par défaut de processus parallèles
forks = 50

2. Remplacement en ligne de commande (-f ou --forks)

Vous pouvez temporairement remplacer le paramètre du fichier de configuration directement lors de l'exécution de la commande ansible ou de l'exécution d'un playbook :

# Exécuter un playbook avec un nombre de forks spécifique
ansible-playbook site.yml --forks 25

# Exécuter une commande ad-hoc avec un nombre de forks spécifique
ansible all -m ping -f 100

3. Variable d'environnement

Pour l'exécution basée sur des scripts ou les pipelines CI/CD, la définition de la variable d'environnement ANSIBLE_FORKS offre un moyen flexible de contrôler la concurrence sans modifier les fichiers de configuration :

export ANSIBLE_FORKS=30
ansible-playbook site.yml

Priorité de configuration : Les arguments de ligne de commande remplacent les variables d'environnement, qui remplacent toutes deux les paramètres de ansible.cfg.

Comment déterminer la valeur optimale de forks

Trouver le nombre parfait de forks est un processus itératif basé sur des tests empiriques. Il n'existe pas de nombre magique unique ; cela dépend fortement de la latence de votre réseau, de la capacité de votre nœud de contrôle et des capacités de vos nœuds cibles.

Étape 1 : Évaluer la capacité du nœud de contrôle

Avant de régler, connaissez vos contraintes. Un nœud de contrôle dédié avec une capacité CPU, mémoire et réseau de rechange peut généralement gérer plus de forks qu'un ordinateur portable exécutant Ansible via un VPN. Le nombre exact dépend de la charge de travail, du plugin de connexion, des frais généraux de démarrage de Python sur les hôtes gérés et de la quantité de données renvoyées par chaque tâche.

Meilleure pratique : Surveillez l'utilisation du CPU et de la mémoire sur votre nœud de contrôle lors de l'exécution d'un playbook de taille moyenne. Si l'utilisation du CPU atteint systématiquement 100 % avant la fin de l'exécution de la tâche, votre nombre de forks est probablement trop élevé pour votre matériel.

Étape 2 : Évaluer la tolérance du nœud cible

Si vos nœuds gérés exécutent des services critiques ou sont déjà fortement utilisés, définir forks trop élevé peut entraîner une dégradation des performances sur ces serveurs (par exemple, réponse SSH lente, services interrompus).

Astuce : Si vous avez seulement besoin d'exécuter des tâches non invasives (comme la collecte de faits), vous pouvez vous permettre des forks plus élevés. Si vous déployez des mises à jour d'applications volumineuses, envisagez de réduire les forks pour minimiser la charge simultanée sur les systèmes de production.

Étape 3 : Tests de charge empiriques

Commencez par une valeur prudente (par exemple, 20 ou 50) et augmentez-la progressivement tout en mesurant le temps d'exécution total d'un playbook standard et représentatif.

Itération de test Paramètre Forks Temps d'exécution total
1 20 450 secondes
2 50 210 secondes
3 100 185 secondes
4 150 190 secondes (Légère augmentation)

Dans cet exemple, le point d'équilibre utile semble être autour de 100 forks, car l'augmentation à 150 n'a apporté aucun gain de temps supplémentaire et a probablement ajouté des frais généraux inutiles. Traitez cela comme un modèle de test, pas comme une référence. Votre propre résultat peut se stabiliser à 20 forks, 75 forks ou une autre valeur.

Interaction avec les types de connexion

Le paramètre forks fonctionne en tandem avec le plugin de connexion que vous avez choisi, le plus souvent ssh.

Latence de connexion SSH

Si la latence de votre connexion est élevée (par exemple, entre continents ou via des VPN lents), vous pourriez constater des rendements décroissants en augmentant les forks, car le temps passé à attendre l'établissement des connexions domine le temps d'exécution. Dans ces cas, réduire les paramètres de délai d'attente pourrait être plus bénéfique que d'augmenter les forks.

Connexions persistantes (Async/ControlPersist)

Pour les environnements utilisant des configurations SSH modernes, telles que ControlPersist (qui maintient les sockets SSH ouverts entre les exécutions Ansible), les frais généraux d'établissement de la connexion initiale sont amortis. Cela vous permet d'utiliser en toute sécurité des nombres de forks plus élevés sans être fortement pénalisé par le temps d'établissement de la connexion initiale.

Éviter les pièges courants

Définir forks trop élevé est une erreur de performance courante. Voici des avertissements critiques :

Avertissement : Soyez prudent lorsque vous définissez forks égal au nombre total d'hôtes dans un grand inventaire. Cela peut fonctionner dans un petit laboratoire, mais en production, cela doit d'abord être testé. Pour les grands inventaires, combinez un nombre de forks raisonnable avec serial, throttle, le traitement par lots ou des groupes d'inventaire séparés afin qu'une seule exécution de playbook ne crée pas une tempête de connexions.

Si vous observez des erreurs liées à Impossible de se connecter à l'hôte ou Connexion expirée lors de l'augmentation des forks, c'est un indicateur fort que vous avez dépassé la capacité de la pile réseau de votre nœud de contrôle ou du démon SSH des nœuds gérés.

Un guide pratique de réglage

La façon la plus simple de régler les forks Ansible est d'utiliser un playbook qui ressemble au travail normal de votre environnement. Un test ping est utile pour vérifier la connectivité, mais il est trop léger pour vous renseigner sur la pression réelle du déploiement. Un meilleur test est quelque chose comme l'actualisation des métadonnées des packages, un petit déploiement de modèle, une vérification de l'état du service ou une exécution à sec du rôle que vous exécutez le plus souvent.

Commencez par enregistrer le comportement actuel. Exécutez le playbook avec votre paramètre existant et enregistrez le temps écoulé, le nombre d'hôtes en échec et tout élément inhabituel provenant du nœud de contrôle. Vous n'avez pas besoin d'un harnais de benchmark complexe. time ansible-playbook -i inventory site.yml --limit web est souvent suffisant pour un premier passage. Dans un autre terminal, surveillez le nœud de contrôle avec top, htop, vm_stat, iostat, ou tout ce que votre système d'exploitation fournit. Si le nœud de contrôle fait du swapping, augmenter les forks n'aidera pas.

Ensuite, augmentez lentement. Si la valeur actuelle est 5, essayez 10, 20 et 40. Si la valeur actuelle est 50, essayez 75 et 100 avant de passer à plusieurs centaines. Après chaque exécution, posez-vous trois questions :

  • Le playbook s'est-il terminé plus rapidement ?
  • Des échecs ou des nouvelles tentatives sont-ils apparus ?
  • L'utilisation du CPU, de la mémoire, des descripteurs de fichier ou du réseau est-elle devenue inconfortable ?

La meilleure valeur se situe généralement juste avant que la courbe ne s'aplatisse. Si 20 forks prennent 12 minutes, 50 forks prennent 6 minutes et 100 forks prennent 5 minutes 40 secondes, la pression supplémentaire de 100 n'en vaut peut-être pas la peine. Je choisirais généralement 50 dans ce cas, à moins que les secondes économisées ne comptent et que l'environnement ait été testé sous charge.

Soyez particulièrement prudent avec les plays qui redémarrent des services, exécutent des migrations de base de données, reconstruisent des caches ou touchent au stockage partagé. Une concurrence élevée peut amener chaque hôte à effectuer un travail coûteux en même temps. Cela peut être exactement ce que vous voulez pour une vérification de fichier inoffensive, mais cela peut être une mauvaise journée si tous les nœuds d'application redémarrent ensemble ou si toutes les répliques de base de données commencent à compacter des fichiers en même temps.

Faites également attention au volume de sortie. Une tâche qui renvoie quelques lignes de chaque hôte se comporte différemment d'une tâche qui diffuse une sortie de commande volumineuse, des journaux de gestionnaire de packages ou des faits JSON provenant de centaines de machines. Le nœud de contrôle doit collecter, analyser et imprimer ces données. Si une exécution semble lente même si les hôtes gérés sont inactifs, essayez de réduire la sortie bruyante, d'enregistrer uniquement ce dont vous avez besoin ou de réduire la collecte de faits avant d'augmenter à nouveau les forks.

Il y a aussi un aspect humain à la concurrence. Un playbook qui échoue sur 3 hôtes sur 20 est facile à analyser. Un playbook qui échoue sur 47 hôtes sur 800 produit un long rapport, et la première erreur utile peut être enterrée. Des forks plus élevés peuvent raccourcir l'exécution mais rendre l'analyse des échecs plus encombrée. Pour le travail opérationnel, je préfère un paramètre de fork qui maintient la sortie lisible, à moins que le travail ne soit entièrement automatisé et dispose déjà d'une bonne alerte autour des échecs.

forks n'est pas non plus le seul contrôle dont vous disposez. Utilisez serial lorsque vous souhaitez parcourir les hôtes par lots :

- name: Déployer l'application web en toute sécurité
  hosts: serveurs_web
  serial: 10
  tasks:
    - name: Mettre à jour le package de l'application
      ansible.builtin.package:
        name: monapp
        state: latest

Avec serial: 10, Ansible traite dix hôtes à la fois pour ce play, même si forks est beaucoup plus élevé. Cela vous donne un plafond de concurrence global provenant de forks et une politique de déploiement provenant de serial.

Utilisez throttle lorsqu'une tâche est plus sensible que le reste du play :

- name: Redémarrer le service API en petits groupes
  ansible.builtin.service:
    name: api
    state: restarted
  throttle: 3

Cela permet aux tâches précédentes de s'exécuter largement tout en limitant la tâche risquée. C'est une option plus propre que d'abaisser forks pour l'ensemble de l'exécution lorsqu'une seule étape a besoin de retenue.

Pour les systèmes CI, notez la valeur choisie dans le ansible.cfg du projet ou la configuration du pipeline. Les paramètres locaux cachés sont une source courante de confusion. Un ingénieur exécute depuis un ordinateur portable avec forks = 5, un autre exécute depuis CI avec ANSIBLE_FORKS=100, et soudainement le même playbook se comporte très différemment. Gardez la valeur par défaut simple et explicite, puis remplacez-la uniquement pour les cas connus.

Un modèle qui fonctionne bien est de conserver une valeur par défaut prudente dans le dépôt :

[defaults]
forks = 25

Ensuite, remplacez-la pour les tâches connues comme sûres :

ANSIBLE_FORKS=75 ansible-playbook -i inventory.ini actualisation-faits.yml

Cela rend l'exception visible au niveau de l'appel. Une actualisation des faits sur des hôtes sains peut tolérer plus de concurrence qu'un déploiement progressif ou une maintenance lourde en redémarrages. Traitez forks comme un paramètre par charge de travail avec une valeur par défaut raisonnable, pas comme un nombre global que vous réglez une fois et oubliez.

Si vous utilisez Ansible Automation Platform, AWX ou un autre exécuteur, n'oubliez pas qu'il peut y avoir des contrôles de concurrence supplémentaires en dehors du processus du playbook. Le découpage des tâches, la capacité du groupe d'instances, les limites de conteneur et les ressources de l'environnement d'exécution peuvent tous limiter ou amplifier l'effet de forks. Lorsqu'une exécution ignore vos attentes, vérifiez à la fois le paramètre d'Ansible et le planificateur qui l'entoure.