Comment accélérer drastiquement vos playbooks Ansible ?

Introduction
Ansible est un moteur d’automatisation open source bien connu qui permet d’automatiser, de provisionner, de gérer la configuration et l’orchestration. Comme il n’a pas besoin d’agent grâce au protocole SSH, et parce que vous n’avez pas besoin d’écrire de code en utilisant des modules simples, Ansible facilite le déploiement et la gestion de vos applications !
Avant de discuter de la façon dont nous pouvons optimiser votre configuration Ansible, voici un petit rappel de son fonctionnement. Vous pouvez voir sur l’image suivante un “nœud de gestion Ansible” (Ansible Management node). Cet hôte effectue des opérations sur l’infrastructure cible en poussant des configurations via des playbooks et des roles. Chaque hôte appelé dans le playbook sera configuré comme vous le souhaitez. Enfin, les hôtes peuvent être organisés en groupes via un fichier d’inventaire qui nous aide à décider quels hôtes nous contrôlons, quand et dans quel but :
Nous pouvons utiliser Ansible pour de nombreuses tâches comme provisionner des machines virtuelles, appliquer des configurations ou même les patcher.
Cependant, dans certains contextes comme la livraison continue, disposer de scripts Ansible rapides (appelés playbooks) est nécessaire pour obtenir un retour rapide ainsi que pour réduire la charge Ansible possible sur les serveurs cibles.
Dans cet article, nous allons voir des concepts importants qui permettent d’offrir à Ansible d’excellentes performances, puis nous passerons en revue quelques benchmarks pour quantifier les améliorations possibles.
Bonnes pratiques pour accélérer les playbooks
Les appels Yum sont coûteux !
Un module Ansible important est le module yum . Vous pouvez l’utiliser pour installer, mettre à niveau, rétrograder, supprimer ou lister des paquets et des groupes avec le gestionnaire de paquets yum (ou apt pour debian). Un problème courant consiste à invoquer plusieurs fois le même module dans plusieurs tâches, comme ceci :
|
Les commandes Yum sont coûteuses ! Ansible est intelligent et sait regrouper les transactions yum ou apt pour installer plusieurs paquets en une seule transaction. C’est donc une optimisation majeure d’installer tous les paquets requis en une seule tâche :
|
Évitez les modules Shell ou Command
Pour exécuter une commande shell sur un hôte Ansible, vous pouvez utiliser des modules comme shell ou command. Les deux sont vraiment consommateurs de temps, comme nous le verrons dans le benchmark. Vérifiez toujours s’il n’existe pas un module plus approprié :
|
Ce ne sera pas seulement plus rapide, mais cela permettra également de tirer parti de la propriété d’idempotence des modules. Cela signifie qu’après une exécution d’un playbook pour amener les choses à l’état désiré, les exécutions ultérieures du même playbook devraient aboutir à 0 changement. En termes plus simples, l’idempotence signifie que les playbooks Ansible peuvent être exécutés plusieurs fois sans effet de bord, de sorte que la cohérence de l’environnement soit maintenue.
Nous verrons dans notre benchmark à quel point c’est efficace lorsque vous utilisez des modules plutôt que des commandes shell.
Choisissez la meilleure stratégie
Lors de l’exécution d’un playbook, Ansible utilise une stratégie qui correspond essentiellement au workflow du playbook. Il est important de sélectionner la bonne stratégie si nous voulons améliorer l’efficacité. La stratégie par défaut est linear : elle exécute chaque tâche sur un certain nombre d’hôtes et attend que chaque tâche soit terminée avant de démarrer la suivante.
Si la cible est indépendante, nous pouvons envisager la stratégie “free”. Les tâches seront traitées indépendamment du statut des tâches sur les autres hôtes, comme l’explique l’image suivante :

Nous pouvons définir des stratégies personnalisées en développant des plugins ou en utilisant des plugins existants comme mitogen, dont nous parlerons plus loin dans cet article.
Augmentez les forks
Les forks définissent le nombre maximum de connexions simultanées qu’Ansible établit pour chaque tâche. Cela vous aide à gérer combien d’hôtes doivent être affectés simultanément. Par défaut, le paramètre est à 5, ce qui signifie que seuls 5 hôtes seront configurés en même temps. Nous pouvons augmenter cette valeur tant que cela n’interfère pas avec les ressources de votre infrastructure.
Les forks peuvent être configurés dans le fichier ansible.cfg :
|
Configurez les tâches Async
Par défaut, Ansible exécute les tâches de manière synchrone, en maintenant la connexion au nœud distant ouverte jusqu’à ce que l’action soit terminée. Lorsque la tâche est réellement indépendante, c’est-à-dire qu’aucune autre tâche n’attend qu’elle soit terminée pour démarrer, définir la tâche comme asynchrone peut vraiment optimiser l’exécution globale, comme le montre l’exemple ci-dessous :
|
Dans cet exemple, nous pourrons installer le paquet httpd même si le script est encore en cours d’exécution. Ansible fera entre-temps un poll toutes les 5 secondes et permettra au script de se terminer dans les 120 secondes sans afficher d’erreurs.
Divisez les playbooks et utilisez des tags
Si vous souhaitez n’exécuter qu’une petite partie de votre playbook, vous devriez envisager d’utiliser des tags.
C’est vraiment simple à utiliser et efficace. Vous avez seulement besoin de découper vos playbooks en petites parties et d’ajouter des “tags” dans vos blocs.
Voici un exemple simple d’utilisation des tags dans un playbook :
|
Si vous souhaitez exécuter uniquement la première partie de votre playbook, vous pouvez lancer les commandes suivantes :
|
Optimisez la collecte de facts
Par défaut, lorsqu’Ansible se connecte à un hôte, il collecte des informations telles que les informations système (processeurs, OS, CPU), la connectivité réseau, les informations sur les périphériques, etc.
C’est ce qu’on appelle les “facts”. Cette opération peut être coûteuse en temps et devrait être évitée lorsqu’elle n’est pas nécessaire, ou optimisée avec un cache de facts lorsque cela est nécessaire.
Je suggère de laisser la collecte activée par défaut dans votre fichier ansible.cfg et de la désactiver quand vous n’en avez pas besoin, directement dans vos playbooks :
|
La collecte de facts peut être améliorée grâce au fact caching. Elle peut utiliser redis ou créer des fichiers JSON sur votre hôte Ansible. Les options suivantes dans le fichier ansible.cfg utilisent le smart gathering et la mise en cache des facts avec un fichier json local :
|
L’option “Smart” signifie que chaque nouvel hôte dont les facts n’ont pas encore été découverts sera scanné, mais si le même hôte est sollicité dans plusieurs plays, il ne sera pas recontacté pendant l’exécution du playbook. Le cache sera conservé au format JSON dans le répertoire “tmp”.
Optimisation SSH
Plusieurs paramètres SSH peuvent être ajustés pour de meilleures performances. Tout d’abord, nous devrions configurer ControlPersist afin que les connexions aux serveurs puissent être recyclées, et définir PreferredAuthentications sur “publickey” pour éviter les délais sur les serveurs ayant GSSAPIAuthentication activé.
Le paramètre le plus important pour SSH est, à mon avis, le “pipelining”, car il réduit le nombre de connexions SSH nécessaires pour exécuter certains modules.
Voici un exemple de ansible.cfg avec de telles optimisations SSH :
|
Plugin Mitogen

Les développeurs du plugin Mitogen affirment que vous pouvez multiplier par 7 la vitesse d’Ansible et réduire de moitié l’utilisation du CPU.
Ansible utilise une implémentation centrée sur le shell, tandis que Mitogen utilise des équivalents purement Python, invoqués via des appels de procédure distante hautement efficaces vers des interpréteurs persistants tunnelisés via SSH.
Cela signifie :
- Une seule connexion est utilisée par cible.
- Un seul aller-retour réseau est utilisé.
- Les processus sont réutilisés de manière agressive.
- Le code est mis en cache de façon éphémère en RAM.
- Moins d’écritures se produisent sur le système de fichiers cible.
Aucune modification n’est requise sur les hôtes cibles et c’est vraiment simple à déployer et à installer :
- Téléchargez et extrayez mitogen-0.2.9.tar.gz.
- Modifiez ansible.cfg : [defaults] strategy_plugins = /path/to/mitogen-0.2.9/ansible_mitogen/plugins/strategy strategy = mitogen_linear
Et voilà !
Nous verrons à quel point c’est efficace dans notre benchmark. L’extension est considérée comme stable et son usage en conditions réelles est encouragé. Cependant, Mitogen n’est pas officiellement supporté par Ansible. Vous devez donc être très prudent en l’utilisant ! Validez vos workflows en lab avant la production !
Configuration de l’environnement de lab pour le benchmarking
Utiliser Docker pour mettre en place un lab sur votre laptop
Je voulais utiliser un environnement Ansible léger. Pour cela, j’ai décidé d’utiliser des conteneurs Docker sur mon laptop. Vous pouvez trouver tous mes scripts et images dans le repository suivant. Il y a un contrôleur Ansible (version 2.9 pour pouvoir utiliser le plugin Mitogen) et autant d’hôtes Centos 7 que vous voulez (8 dans mon cas). Ce lab peut être utilisé directement sur un environnement Docker. Il suffit d’exécuter la commande indiquée dans le fichier README.md. Vous serez directement connecté dans votre conteneur contrôleur Ansible après avoir exécuté run.sh.
Vous pouvez exécuter la commande suivante pour vérifier que tout fonctionne comme prévu :
|

Si tout est correct, vous pouvez utiliser mon script run-benchmark.sh ou jouer avec vos propres réglages !
Playbook utilisé pour le benchmarking
J’ai utilisé le playbook benchmark.yml pour benchmarker les optimisations.
Dans ce playbook, il y a essentiellement deux parties.
Dans la première partie, je voulais vérifier combien de temps je pouvais économiser en utilisant des modules plutôt que des commandes.
Dans la seconde partie, je voulais installer plusieurs roles et disposer de suffisamment de temps écoulé pour comparer.
Configurer Ansible pour afficher les logs et le temps écoulé sur les tâches
Une chose importante avant de benchmarker est d’activer le logging afin de pouvoir voir ce qui prend du temps dans nos playbooks. Il suffit d’ajouter la ligne suivante dans votre ansible.cfg :
|
Vous aurez en effet plus d’informations affichées après chaque exécution de playbook (premier play, sans aucune optimisation) :

Résultats du benchmark
Je voulais voir quelles optimisations affectaient le plus mon playbook, donc j’ai fait deux choses.
- Exécuter chacune des optimisations suivantes indépendamment :

- J’ai ajouté les optimisations de façon cumulative pour tracer le graphique en barres suivant :

Comme on peut le voir en examinant les résultats, le plugin Mitogen, les forks et le pipelining SSH étaient, dans mon cas, les meilleures optimisations testées. Sans changements, mon playbook prenait presque 8 minutes pour se terminer. Après cela, seulement 3 minutes !
Évidemment, cela dépend aussi de vos playbooks et de votre environnement, mais toutes ces optimisations peuvent vraiment améliorer la vitesse d’Ansible tout en conservant le formidable outil qu’il est, même lorsque la rapidité est critique !
Sources
Pour aller plus loin, découvrez notre formation Ansible et notre consulting DevOps.