Meilleures pratiques pour optimiser les déploiements Ansible à grande échelle
Moyens pratiques d'accélérer les exécutions Ansible volumineuses avec forks, plugins de stratégie, mise en cache des faits, réutilisation SSH et une meilleure conception de playbook.
Meilleures pratiques pour optimiser les déploiements Ansible à grande échelle
Les déploiements Ansible à grande échelle deviennent généralement lents pour des raisons simples : trop de poignées de main SSH, trop de collecte de faits, un contrôleur avec trop peu de CPU, ou un playbook qui fait attendre chaque hôte pour le plus lent. La solution est rarement un seul paramètre. Vous obtenez le meilleur résultat en réduisant la surcharge de connexion, en ajustant la concurrence et en écrivant des plays qui font moins de travail par hôte.
Je ne définirais pas « grande échelle » par un nombre strict d'hôtes. Un inventaire de 300 hôtes peut sembler important si chaque tâche installe des paquets sur des liaisons lentes. Un inventaire de 3 000 hôtes peut être gérable si le contrôleur est bien dimensionné et les playbooks sont serrés. Traitez les chiffres ci-dessous comme des points de départ, puis mesurez avec votre propre inventaire et vos modules.
Ajustez le parallélisme avant de blâmer Ansible
Le parallélisme est généralement le premier levier à tester car Ansible passe beaucoup de temps à attendre les hôtes distants. L'objectif n'est pas « le plus grand nombre de forks gagne ». L'objectif est d'avoir suffisamment de concurrence pour maintenir le contrôleur occupé sans submerger SSH, l'élévation de privilèges, les dépôts de paquets ou les cibles elles-mêmes.
Contrôle de la concurrence avec forks
Le paramètre forks définit le nombre de processus de travail parallèles que le contrôleur Ansible peut lancer. Trouver le nombre optimal nécessite d'équilibrer les ressources du contrôleur (CPU et mémoire) avec les limites de connexion de l'environnement cible.
Définissez forks dans votre ansible.cfg ou via la ligne de commande (-f ou --forks).
[defaults]
forks = 100
Commencez plus bas que vous ne le pensez nécessaire. Exécutez le même playbook contre le même groupe d'hôtes avec 25, 50, 100 et 200 forks tout en surveillant le CPU, la mémoire, les échecs SSH et le temps d'exécution. Si le CPU reste principalement inactif et que les hôtes passent du temps à attendre, augmentez les forks. Si le contrôleur commence à swapper, les processus Python s'accumulent ou les cibles rejettent les connexions, réduisez.
Choisir le bon plugin de stratégie
La stratégie d'exécution par défaut d'Ansible est linear, ce qui signifie que les tâches doivent être terminées sur tous les hôtes ciblés avant de passer à la tâche suivante dans le playbook. Pour des milliers de nœuds, un seul hôte lent peut créer un goulot d'étranglement pour toute l'exécution.
Pour certains grands déploiements, utilisez la stratégie free.
Stratégie libre (strategy = free) :
free permet aux hôtes de progresser indépendamment dans le playbook dès qu'ils terminent une tâche, sans attendre les hôtes plus lents. Cela peut améliorer le débit lorsque les tâches sont indépendantes. Ne l'utilisez pas aveuglément pour les déploiements progressifs, les migrations partagées ou les plays où l'ordre des tâches sur l'ensemble du parc est important.
# Exemple de définition de playbook
---
- hosts: all
strategy: free
tasks:
- name: Assurez-vous que le service est en cours d'exécution
ansible.builtin.service:
name: httpd
state: started
Mettez en cache les faits lorsque vous les réutilisez
La collecte de faits est utile, mais il est facile de payer pour cela à plusieurs reprises. Si vos playbooks utilisent des faits sur plusieurs exécutions, mettez-les en cache. Si un play n'a pas besoin de faits d'hôte du tout, désactivez la collecte pour ce play.
Utilisation de caches externes (Redis ou Memcached)
Pour un seul contrôleur, la mise en cache de fichiers JSON peut suffire. Pour plusieurs contrôleurs ou workers d'automatisation, utilisez un cache externe tel que Redis ou Memcached afin que chaque worker voie le même cache de faits.
Configuration exploitable dans ansible.cfg :
[defaults]
gathering = smart
fact_caching = redis
fact_caching_timeout = 7200 ; Mettre en cache les faits pendant 2 heures (en secondes)
fact_caching_prefix = ansible_facts
; Si vous utilisez Redis
fact_caching_connection = localhost:6379:0
Définissez gathering = smart lorsque les faits mis en cache font partie de votre flux de travail. Si vous n'avez besoin que d'une petite partie des données de l'hôte, utilisez gather_subset au lieu de tout collecter.
3. Optimisation de la connexion et du transport
La réduction de la surcharge associée à l'établissement de connexions est primordiale lorsqu'il s'agit de milliers de sessions SSH simultanées.
Pipelining SSH
Le pipelining réduit le nombre d'allers-retours SSH qu'Ansible utilise pour de nombreuses exécutions de modules. Cela vaut souvent la peine d'être activé, mais testez-le avec vos règles d'élévation de privilèges.
Réutilisation de la connexion SSH (ControlPersist)
Pour les cibles de type Unix, les paramètres ControlMaster et ControlPersist empêchent Ansible d'initier une toute nouvelle session SSH pour chaque tâche. Il maintient un socket de contrôle ouvert pendant une durée spécifiée, permettant aux tâches suivantes d'utiliser la connexion existante.
Configuration exploitable dans ansible.cfg :
[ssh_connection]
pipelining = True
; Utilisez une réutilisation agressive de la connexion (par exemple, 30 minutes)
ssh_args = -C -o ControlMaster=auto -o ControlPersist=30m -o ServerAliveInterval=15
Le pipelining peut entrer en conflit avec les configurations sudo qui nécessitent un TTY. Si vous avez toujours Defaults requiretty dans sudoers, supprimez-le pour l'utilisateur d'automatisation ou gardez le pipelining désactivé pour ces hôtes.
Optimisation Windows (WinRM)
Lorsque vous ciblez des nœuds Windows, ajustez WinRM séparément. Kerberos est généralement un meilleur choix de production que l'authentification de base, et les limites du service WinRM peuvent nécessiter une révision si de nombreux travaux se connectent en même temps.
4. Gestion de l'inventaire pour l'échelle
Les fichiers d'inventaire statiques deviennent pénibles lorsque les hôtes sont créés et détruits fréquemment. L'inventaire dynamique n'est pas obligatoire pour tous les grands environnements, mais c'est la valeur par défaut appropriée pour les parcs cloud, les groupes de mise à l'échelle automatique et l'infrastructure basée sur CMDB.
Sources d'inventaire dynamiques
Utilisez des plugins d'inventaire pour votre fournisseur cloud (AWS EC2, Azure, Google Cloud) ou votre système CMDB. L'inventaire dynamique garantit qu'Ansible ne cible que les hôtes actifs avec des données à jour.
# Exemple : Exécution contre un inventaire AWS filtré dynamiquement
ansible-playbook -i aws_ec2.yml site.yml --limit 'tag_Environment_production'
Ciblage et filtrage intelligents
Évitez d'exécuter des playbooks contre l'ensemble de l'inventaire (hosts: all) sauf si cela est absolument nécessaire. Utilisez des groupes granulaires, des limites (--limit) et des tags (--tags) pour garantir que l'ensemble des cibles d'exécution est minimisé.
5. Considérations architecturales et dimensionnement du contrôleur
Pour les déploiements à grande échelle, l'environnement dans lequel Ansible s'exécute doit être correctement provisionné.
Dimensionnement du contrôleur
Ansible est très dépendant des ressources du contrôleur, principalement le CPU et la RAM, en raison de la nécessité de forker des processus pour une exécution parallèle.
- CPU : Plus de forks signifie généralement plus de travail Python sur le contrôleur. Surveillez la charge moyenne et la saturation par cœur lors des exécutions réelles de playbook.
- RAM : Chaque fork consomme de la mémoire. Les grands modèles, les grandes variables et les plugins de callback bavards peuvent rapidement augmenter l'utilisation de la mémoire.
- E/S de stockage : Un stockage local rapide aide lorsque le contrôleur écrit de nombreux fichiers temporaires, journaux, artefacts ou entrées de cache de faits basées sur des fichiers.
Utilisation des plateformes d'automatisation
Pour les équipes qui ont besoin de planification, de RBAC, de pistes d'audit et de plusieurs workers d'exécution, utilisez Ansible Automation Platform ou AWX plutôt qu'une seule session shell de longue durée sur un nœud de contrôle.
AAP fournit :
- Planification et historique des travaux : Journalisation et audit centralisés.
- Environnements d'exécution : Environnements d'exécution cohérents et reproductibles.
- Clustering et mise à l'échelle : Distribuez l'exécution sur plusieurs nœuds de travail pour gérer des besoins de concurrence massifs sans surcharger un seul contrôleur.
- Gestion des identifiants : Gestion sécurisée des secrets à grande échelle.
6. Conception de playbook pour l'efficacité
Même avec une infrastructure optimisée, des playbooks mal écrits peuvent annuler les gains de performance.
Minimisez la collecte de faits
Si vous utilisez des faits mis en cache (Section 2), désactivez activement la collecte redondante de faits lorsque cela est possible :
- hosts: web_servers
gather_facts: no # Désactiver la collecte de faits pour ce play
tasks:
# ... exécutez uniquement les tâches qui ne reposent pas sur des faits système collectés
Utilisez run_once et delegate_to avec parcimonie
Les tâches qui doivent s'exécuter séquentiellement ou centralement (par exemple, lancer un déploiement progressif, mettre à jour un équilibreur de charge) doivent être gérées via run_once: true et delegate_to: management_node. Cela évite un parallélisme inutile lorsqu'un seul hôte doit effectuer l'action.
Préférez les opérations par lots
Dans la mesure du possible, utilisez des modules qui gèrent les opérations par lots de manière native (par exemple, les gestionnaires de paquets comme apt ou yum qui acceptent une liste de paquets) plutôt que d'itérer sur une grande liste en utilisant une loop ou with_items sur des tâches package séparées.
# Mieux : une tâche de paquet avec une liste
- name: Installer les dépendances nécessaires
ansible.builtin.package:
name:
- nginx
- python3-pip
- firewall
state: present
7. Mesurez le playbook, pas seulement le nombre d'hôtes
Lorsqu'une exécution Ansible est lente, ajoutez du chronométrage avant de modifier d'autres boutons. Le callback intégré profile_tasks est un bon premier passage :
[defaults]
callbacks_enabled = profile_tasks, timer
Exécutez le playbook une fois contre un groupe d'hôtes représentatif et examinez les tâches les plus lentes. Vous constaterez peut-être que la majeure partie du temps est consacrée à une installation de paquet, une étape de rendu de modèle ou une commande qui attend un service externe. Dans ce cas, augmenter forks crée simplement plus de pression sur le même goulot d'étranglement.
Pour un test reproductible, maintenez la tranche d'inventaire stable :
ansible-playbook -i inventory site.yml --limit 'web:&production' -f 50
ansible-playbook -i inventory site.yml --limit 'web:&production' -f 100
ansible-playbook -i inventory site.yml --limit 'web:&production' -f 200
Enregistrez le temps d'exécution total, les hôtes en échec, le CPU du contrôleur, la mémoire du contrôleur et toute erreur SSH ou sudo côté cible. Surveillez également le dépôt de paquets ou le serveur d'artefacts pendant le test. Un playbook peut ressembler à un problème Ansible alors que le vrai problème est que chaque hôte télécharge le même paquet en même temps à partir d'un miroir interne surchargé.
8. Réduisez le travail avant d'augmenter la concurrence
Les exécutions Ansible à grande échelle s'améliorent souvent plus en faisant moins qu'en le faisant plus vite. Quelques exemples reviennent souvent :
- Une tâche de modèle rend un grand fichier de configuration à chaque exécution même si seule une petite inclusion a changé.
- Une tâche shell exécute une commande de découverte sur chaque hôte alors que la valeur est déjà dans l'inventaire.
- Un rôle installe des paquets un par un dans une boucle.
- Un gestionnaire redémarre un service après plusieurs modifications de modèle non liées alors qu'un seul rechargement suffirait.
Utilisez l'idempotence du module au lieu de shell lorsque cela est possible. Une commande shell qui signale toujours un changement peut déclencher des gestionnaires sur des centaines d'hôtes et transformer une vérification inoffensive en un redémarrage progressif. Si vous devez utiliser command ou shell, définissez soigneusement changed_when et creates ou removes.
- name: Initialiser le répertoire de l'application une fois
ansible.builtin.command: /usr/local/bin/app-init /srv/app
args:
creates: /srv/app/.initialized
Cette petite garde empêche le travail répété et évite les faux rapports de modification.
9. Utilisez des lots pour le contrôle des risques
La performance n'est pas la seule préoccupation à grande échelle. Parfois, le playbook le plus rapide est dangereux sur le plan opérationnel. Pour les parcs de services, utilisez serial pour contrôler le rayon d'explosion :
- hosts: app_servers
serial: 10%
max_fail_percentage: 5
tasks:
- name: Déployer le paquet de l'application
ansible.builtin.package:
name: myapp
state: latest
serial rendra l'exécution plus longue que de tirer sur tous les hôtes en même temps, mais cela donne aux équilibreurs de charge, à la surveillance et aux humains le temps de réagir. Cela protège également les dépendances partagées. Un miroir de paquets, un point de terminaison de migration de base de données ou un gestionnaire de secrets peuvent ne pas survivre à des milliers de requêtes simultanées.
Les déploiements Ansible à grande échelle mettent sous tension des systèmes faciles à oublier : les résolveurs DNS, les dépôts de paquets, les magasins de secrets, les pipelines de journalisation et les points de terminaison de surveillance. Si un playbook ralentit uniquement à des nombres de forks plus élevés, vérifiez ces services partagés avant de blâmer Ansible.
Gardez également la sortie du callback sous contrôle. Les journaux très verbeux sont utiles lors du débogage, mais ils peuvent ralentir les grandes exécutions et enterrer la véritable défaillance. Utilisez une verbosité élevée pour une tranche d'hôtes étroite, puis revenez à une sortie normale pour l'exécution sur l'ensemble du parc.
10. Divisez les plays par domaine de défaillance
Une astuce de mise à l'échelle souvent négligée consiste à cesser de traiter l'ensemble du parc comme une seule unité de déploiement. Si les hôtes de base de données, les hôtes Web, les files d'attente et les nœuds de cache vivent tous dans le même play géant, un groupe lent ou cassé peut retarder un travail non lié. Séparez les plays par domaine de défaillance et ordre de dépendance.
Par exemple, exécutez la configuration de base du système d'exploitation largement, mais déployez le code de l'application par niveau de service. Mettez à jour les nœuds de cache dans leur propre play. Vidangez et redémarrez les nœuds Web par lots. Appliquez la configuration de la base de données avec des vérifications supplémentaires et une concurrence plus faible. Cela rend les nouvelles tentatives plus sûres car vous pouvez réexécuter la partie défaillante sans répéter le travail sur chaque hôte.
Cela clarifie également la propriété. L'équipe responsable d'un service peut ajuster sa taille de lot, ses contrôles de santé et son comportement de restauration sans modifier les valeurs par défaut globales de l'automatisation. Ansible à grande échelle reste maintenable lorsque la structure du playbook correspond à la façon dont l'infrastructure échoue réellement lors d'incidents réels et de fenêtres de maintenance.
Le travail de performance Ansible le plus percutant est généralement simple : réutiliser les connexions SSH, éviter la collecte inutile de faits, dimensionner correctement forks et empêcher les playbooks d'effectuer des opérations minuscules répétées. Ensuite, regardez l'architecture. Si un seul contrôleur ne peut pas suivre proprement, répartissez le travail sur les nœuds d'exécution et rendez l'inventaire, les identifiants et la journalisation reproductibles.