Identifier et Résoudre les Goulots d'Étranglement dans les Playbooks Ansible Lents

Accélérez considérablement vos déploiements Ansible en identifiant et en éliminant les goulots d'étranglement de performance. Ce guide fournit des étapes pratiques, des exemples de configuration et des bonnes pratiques pour profiler les playbooks lents, optimiser la collecte de faits, gérer les connexions et ajuster l'exécution des tâches. Apprenez à tirer parti des fonctionnalités d'Ansible pour une automatisation efficace et rapide de l'infrastructure.

Identifier et Résoudre les Goulots d'Étranglement dans les Playbooks Ansible Lents

Les playbooks Ansible lents sont frustrants car le retard est rarement à un endroit évident. Une exécution peut passer quelques secondes à rassembler des faits, quelques autres à ouvrir des connexions SSH, puis des minutes à copier des fichiers un hôte à la fois. Si vous devinez seulement, vous ajustez généralement la mauvaise chose.

Commencez par mesurer où le temps est passé. Ensuite, corrigez la plus grande source de retard en premier. Dans un petit environnement, cela peut être une seule tâche shell qui exécute un gestionnaire de packages à chaque fois. Dans un environnement plus grand, il s'agit souvent de la configuration de connexion, de la collecte de faits, d'un faible forks, ou d'un playbook qui sérialise le travail plus que prévu.

Comprendre les Métriques de Performance d'Ansible

Avant de plonger dans des techniques d'optimisation spécifiques, il est crucial de comprendre comment mesurer et interpréter les performances d'Ansible. Ansible fournit des informations de chronométrage intégrées qui peuvent être inestimables pour le diagnostic.

Utiliser la Sortie de Chronométrage Avant les Journaux Verbeux

Une sortie très verbeuse peut aider avec les problèmes de connexion, mais elle est bruyante pour le travail de performance. Un premier passage plus propre est le callback profile_tasks, qui montre les durées des tâches à la fin de l'exécution.

Dans ansible.cfg :

[defaults]
callbacks_enabled = profile_tasks

Exécutez ensuite le playbook normalement :

ansible-playbook my_playbook.yml

Regardez d'abord les tâches les plus lentes. Si une tâche prend la majeure partie de l'exécution, ne passez pas la matinée à débattre de forks.

Contrôler la Verbeosité de la Sortie

Utilisez -vvv lorsque vous avez besoin de voir les détails SSH, le comportement de transfert de module, les tentatives, ou la découverte de l'interpréteur. Pour le chronométrage de routine, cela peut cacher le signal sous des pages de sortie de journal.

Goulots d'Étranglement Courants et Stratégies d'Optimisation

Plusieurs facteurs peuvent contribuer à la lenteur des playbooks Ansible. Ici, nous explorerons les goulots d'étranglement courants et fournirons des stratégies actionnables pour les résoudre.

1. Collecte Excessive de Faits

Par défaut, Ansible rassemble des faits (informations système) des hôtes gérés au début de chaque play. Bien qu'utile, cela peut prendre du temps, surtout sur un grand nombre d'hôtes ou des réseaux lents. Si votre playbook n'a pas besoin de tous les faits rassemblés, vous pouvez désactiver ou limiter la collecte de faits.

Désactiver la Collecte de Faits

Pour désactiver complètement la collecte de faits pour un play, utilisez la directive gather_facts: no :

- name: Mon Playbook
  hosts: serveursweb
  gather_facts: no
  tasks:
    - name: Assurer qu'Apache est installé
      apt: name=apache2 state=present

Limiter la Collecte de Faits

Si vous avez besoin de certains faits mais pas de tous, vous pouvez spécifier quels faits rassembler en utilisant gather_subset.

- name: Mon Playbook
  hosts: serveursweb
  gather_facts: yes
  gather_subset:
    - '!all'
    - '!any'
    - hardware
    - network
  tasks:
    - name: Utiliser les faits réseau
      debug: var=ansible_default_ipv4.address

Mise en Cache des Faits

Pour les environnements où les faits ne changent pas fréquemment, les mettre en cache peut considérablement accélérer les exécutions ultérieures du playbook. Ansible prend en charge plusieurs plugins de mise en cache de faits (par exemple, jsonfile, redis, memcached).

Pour activer la mise en cache des faits, configurez-la dans votre fichier ansible.cfg :

[defaults]
fact_caching = jsonfile
fact_caching_connection = /path/to/ansible/facts_cache
fact_caching_timeout = 86400 # Mettre en cache pendant 24 heures

Ensuite, votre playbook utilisera automatiquement les faits en cache lorsqu'ils sont disponibles.

2. Exécution Inefficace des Tâches

Certaines tâches peuvent être intrinsèquement lentes, ou elles peuvent être exécutées de manière inefficace.

Exécution Parallèle (Forking)

Le comportement par défaut d'Ansible est d'exécuter les tâches sur les hôtes de manière séquentielle dans un play. Vous pouvez augmenter le nombre de processus parallèles (forks) qu'Ansible utilise pour gérer les hôtes simultanément. Cela est contrôlé par le paramètre forks dans ansible.cfg ou via l'option de ligne de commande -f.

ansible.cfg :

[defaults]
forks = 10

Ligne de commande :

ansible-playbook my_playbook.yml -f 10

Conseil : Commencez avec un nombre modéré de forks et augmentez-le progressivement tout en surveillant le nœud de contrôle, le réseau et le service cible. Plus de forks peuvent accélérer un déploiement, mais ils peuvent aussi submerger un dépôt de paquets, un équilibreur de charge ou une étape de migration de base de données.

Idempotence et Gestion d'État

Assurez-vous que vos tâches sont idempotentes. Cela signifie qu'exécuter une tâche plusieurs fois doit avoir le même effet que de l'exécuter une fois. Les modules Ansible sont généralement conçus pour être idempotents, mais les scripts ou commandes personnalisés peuvent ne pas l'être. Des vérifications inefficaces dans les tâches peuvent également ajouter une surcharge.

Par exemple, au lieu d'exécuter une commande qui vérifie si un service est en cours d'exécution puis le démarre, utilisez le module dédié service :

Inefficace :

- name: Démarrer le service (vérification inefficace)
  command: systemctl start my_service.service || true
  when: "'inactive' in service_status.stdout"
  register: service_status
  changed_when: false # Cette tâche ne change pas d'état

Efficace (en utilisant le module service) :

- name: Assurer que my_service est en cours d'exécution
  service:
    name: my_service
    state: started

Utiliser async et poll pour les Opérations Longues

Pour les tâches qui peuvent prendre beaucoup de temps à terminer (par exemple, mises à niveau de paquets, migrations de base de données), l'utilisation des directives async et poll d'Ansible peut empêcher votre playbook de se bloquer.

  • async : Spécifie le temps maximum que la tâche doit s'exécuter en arrière-plan.
  • poll : Spécifie la fréquence à laquelle Ansible doit vérifier l'état de la tâche asynchrone.
- name: Effectuer une opération longue
  command: /usr/local/bin/long_script.sh
  async: 3600 # Exécuter pendant un maximum d'une heure
  poll: 60    # Vérifier l'état toutes les 60 secondes

3. Optimisation de la Connexion

La manière dont Ansible se connecte à vos nœuds gérés joue un rôle crucial dans les performances.

Multiplexage de Connexion SSH

Le multiplexage SSH (ControlMaster) permet à plusieurs sessions SSH de partager une seule connexion réseau. Cela peut considérablement accélérer les connexions ultérieures au même hôte.

Activez-le dans votre ansible.cfg :

[ssh_connection]
control_master = auto
control_path = ~/.ansible/cp/ansible-%%r@%%h:%%p
control_persist = 600 # Garder la connexion de contrôle ouverte pendant 10 minutes

Tentatives et Délai d'Attente SSH

Ajuster les paramètres de connexion SSH peut éviter des retards inutiles lorsque les hôtes sont temporairement indisponibles.

[ssh_connection]
sf_retries = 3
sf_delay = 1
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o ConnectionAttempts=5 -o ConnectTimeout=10

Utiliser pipelining

Le pipelining permet à Ansible d'exécuter des commandes directement sur l'hôte distant sans créer une nouvelle session SSH pour chaque commande. Cela peut considérablement réduire la surcharge pour de nombreuses tâches.

Activez-le dans ansible.cfg :

[ssh_connection]
pipelining = True

Avertissement : Le pipelining peut entrer en conflit avec certaines configurations d'élévation de privilèges, surtout lorsque requiretty est activé pour sudo sur des distributions plus anciennes. Testez-le avec le même chemin become que vos playbooks de production utilisent.

4. Optimiser la Structure et la Logique du Playbook

Parfois, la manière dont un playbook est écrit peut être la source de lenteur.

Utiliser delegate_to et run_once

Si une tâche ne doit être effectuée que sur un seul hôte mais en affecte plusieurs autres (par exemple, redémarrer un équilibreur de charge), utilisez delegate_to et run_once pour l'exécuter efficacement.

- name: Redémarrer l'équilibreur de charge
  service: name=haproxy state=restarted
  delegate_to: lb_server_1
  run_once: true

Utilisation Stratégique des Rôles et des Inclusions

Bien que les rôles et les inclusions aident à l'organisation, des inclusions profondément imbriquées ou structurées de manière inefficace peuvent ajouter une petite surcharge. Assurez-vous que vos dépendances de rôles et votre logique d'inclusion sont propres.

Mot-clé serial

Le mot-clé serial limite le nombre d'hôtes qui peuvent être traités simultanément dans un play. Bien qu'il soit souvent utilisé pour des déploiements contrôlés, il peut également être un goulot d'étranglement s'il est réglé trop bas pour les performances souhaitées.

- name: Déployer l'application sur un sous-ensemble de serveurs
  hosts: serveursapp
  serial: 2 # Exécuter seulement sur 2 hôtes à la fois
  tasks:
    - name: Mettre à jour le code de l'application
      copy: src=app/ dest=/opt/app/

Si vous ne limitez pas intentionnellement le parallélisme, assurez-vous que serial n'est pas défini ou est défini à un nombre suffisamment élevé.

Corriger les Tâches Lentes, Pas Seulement le Transport Lent

L'optimisation de la connexion aide lorsque le playbook a de nombreuses tâches courtes. Elle ne corrige pas une tâche qui fait trop de travail à chaque fois.

Un exemple courant est l'utilisation de shell pour exécuter une commande de paquet :

- name: Installer nginx avec shell
  shell: apt-get update && apt-get install -y nginx

Cette tâche est difficile à raisonner pour Ansible. Elle peut signaler un changement à chaque fois, elle peut mettre à jour les métadonnées du paquet à chaque exécution, et elle vous donne moins d'informations structurées sur les échecs. Préférez les modules qui comprennent l'état :

- name: Rafraîchir le cache apt si nécessaire
  apt:
    update_cache: true
    cache_valid_time: 3600

- name: Installer nginx
  apt:
    name: nginx
    state: present

La même idée s'applique au déploiement de fichiers. Copier un grand répertoire avec des centaines de petits fichiers via le module copy peut être lent car Ansible vérifie et transfère fichier par fichier. Pour les versions d'application, il peut être plus rapide de construire un artefact une fois, de télécharger l'archive et de la décompresser sur la cible :

- name: Télécharger l'artefact de version
  copy:
    src: dist/app.tar.gz
    dest: /tmp/app.tar.gz

- name: Décompresser la version
  unarchive:
    src: /tmp/app.tar.gz
    dest: /opt/app
    remote_src: true

Ce n'est pas toujours la bonne conception, mais c'est la bonne question : demandez-vous à Ansible de synchroniser des milliers de petites décisions alors qu'un artefact serait plus clair ?

Vérifier l'Inventaire et le Travail des Variables

L'inventaire dynamique peut être un autre retard caché. Si chaque exécution de playbook appelle une API cloud, attend la pagination et reconstruit toute la liste des hôtes, le playbook peut sembler lent avant même que la première tâche ne commence. Mettez en cache les données d'inventaire lorsque votre plugin le prend en charge et gardez les modèles d'hôtes étroits. Exécuter un déploiement web contre all puis ignorer la plupart des hôtes avec des conditions when gaspille du temps.

Le chargement des variables peut également devenir désordonné. De grands fichiers group_vars/all.yml, des recherches coûteuses et un rendu de modèles répété peuvent s'accumuler. Si une recherche atteint un gestionnaire de secrets ou un point de terminaison HTTP, stockez le résultat dans une variable une fois par play au lieu de l'appeler dans de nombreuses tâches.

Outils et Techniques de Profilage

Au-delà de la sortie verbeuse d'Ansible elle-même, un profilage dédié peut offrir des informations plus approfondies.

ansible-playbook --syntax-check

Cette commande vérifie la syntaxe de votre playbook mais ne l'exécute pas. C'est un moyen rapide de valider la structure de votre playbook avant une exécution complète.

Journalisation des Événements Ansible

Ansible peut enregistrer ses événements d'exécution dans un fichier, qui peut ensuite être analysé. Cela est particulièrement utile pour les playbooks de longue durée ou pour l'audit.

Configurez la journalisation des événements dans ansible.cfg :

[defaults]
log_path = /var/log/ansible.log

Plugins de Callback Personnalisés

Pour un profilage avancé, vous pouvez écrire des plugins de callback personnalisés pour capturer des métriques spécifiques ou créer des rapports personnalisés sur l'exécution du playbook.

Utiliser Async pour Attendre, Pas pour Tout

Une partie du temps du playbook est une véritable attente : un redémarrage de service, une construction de paquet, une instance cloud devenant prête, ou une migration de base de données qui prend légitimement quelques minutes. Si ces tâches n'ont pas besoin de bloquer chaque hôte en lockstep, async et poll d'Ansible peuvent aider.

- name: Démarrer la génération de rapport longue
  command: /opt/tools/build-report
  async: 1800
  poll: 0
  register: report_job

- name: Vérifier le travail de rapport
  async_status:
    jid: "{{ report_job.ansible_job_id }}"
  register: report_status
  until: report_status.finished
  retries: 60
  delay: 10

Utilisez cela avec précaution. Async n'est pas un raccourci pour rendre les tâches dangereuses parallèles. Si dix hôtes démarrent tous une migration de base de données en même temps, le playbook peut se terminer plus rapidement et toujours casser l'environnement. Async fonctionne mieux pour un travail indépendant où la cible peut continuer en toute sécurité pendant qu'Ansible vérifie plus tard.

Mesurer du Point de Vue de l'Utilisateur

Un playbook peut être techniquement plus rapide et sembler toujours lent si l'opérateur attend trop longtemps avant de voir un retour utile. Divisez un grand déploiement en phases avec des noms de tâches clairs : vérifications préliminaires, téléchargement d'artefact, mise à jour de service, vérification de santé, nettoyage. Lorsqu'une phase est lente, la sortie du profil et l'humain lisant le terminal comprennent tous les deux où le temps est passé.

Cela aide également avec les décisions de retour en arrière. Si le playbook passe 12 minutes avant la première vérification de santé, vous découvrez peut-être les échecs trop tard. Une petite tâche préliminaire qui vérifie l'espace disque, l'accès au dépôt de paquets et les informations d'identification du service peut faire gagner beaucoup plus de temps que de réduire d'une seconde la configuration SSH.

Le meilleur travail de performance Ansible est ennuyeux dans le bon sens : activez le chronométrage des tâches, trouvez l'étape la plus lente, changez une chose et mesurez à nouveau. Désactivez les faits seulement lorsque vous n'en avez pas besoin. Augmentez forks seulement lorsque les cibles et les dépendances peuvent gérer le parallélisme. Remplacez les commandes shell bruyantes par des modules conscients de l'état. Utilisez le multiplexage SSH et le pipelining après avoir confirmé que la surcharge de connexion fait réellement partie du problème.

Cette discipline maintient le playbook lisible tout en le rendant plus rapide. Un déploiement qui se termine rapidement mais que personne ne comprend est juste une panne de demain avec une barre de progression plus courte.