Aller au contenu

Les tâches asynchrones sous Ansible

Mise à jour :

Logo Ansible

L’automatisation des tâches via Ansible permet de simplifier la gestion des infrastructures, mais certaines opérations peuvent prendre beaucoup de temps. Par exemple, l’installation de logiciels lourds, le provisionnement de machines virtuelles ou encore des opérations de sauvegarde. Par défaut, Ansible exécute les tâches de manière synchrone, c’est-à-dire qu’il attend que chaque tâche se termine avant de passer à la suivante. Cela peut parfois ralentir le déploiement global.

C’est ici que les tâches asynchrones entrent en jeu. Elles permettent à Ansible de démarrer une tâche et de continuer l’exécution des autres instructions sans attendre que la tâche initiale se termine. En parallèle, Ansible peut vérifier de façon périodique l’avancement de ces tâches. Ce modèle est particulièrement utile pour les opérations longues, car il permet de gagner du temps tout en gardant un contrôle sur l’avancement des tâches.

Utilisation de la commande ad-hoc Ansible avec async

Ansible permet l’exécution de tâches individuelles sur des hôtes distants via des commandes ad-hoc. Ces commandes sont particulièrement utiles pour lancer rapidement une tâche sans avoir à écrire un playbook complet. Il est également possible d’exécuter des tâches en mode asynchrone avec la commande ad-hoc, offrant ainsi une flexibilité supplémentaire pour les administrateurs qui souhaitent effectuer des opérations rapides sans bloquer leur terminal.

Rappel : Qu’est-ce qu’une commande ad-hoc ?

Une commande ad-hoc dans Ansible est une instruction simple lancée directement depuis la ligne de commande. Elle permet d’exécuter une tâche unique sur un ou plusieurs hôtes sans avoir besoin d’écrire un fichier de playbook. Voici un exemple classique d’une commande ad-hoc pour redémarrer un service :

Terminal window
ansible all -m service -a "name=httpd state=restarted"

Cette commande redémarre le service httpd sur tous les hôtes inventoriés.

Ajouter async à une commande ad-hoc

Ansible permet d’ajouter les paramètres async et poll aux commandes ad-hoc pour exécuter une tâche en arrière-plan et continuer immédiatement à d’autres opérations. La syntaxe est assez simple : vous ajoutez l’option -B (pour async) et -P (pour poll) à la commande ad-hoc.

Syntaxe de base avec async dans une commande ad-hoc :

Terminal window
ansible all -m shell -a "/path/to/long_task.sh" -B 600 -P 5
  • -B 600 indique que la tâche peut s’exécuter en arrière-plan pendant un maximum de 600 secondes (10 minutes).
  • -P 5 spécifie que l’état de la tâche sera vérifié toutes les 5 secondes.

Exemple d’utilisation

Imaginons que vous souhaitiez exécuter une sauvegarde longue sur plusieurs serveurs. Plutôt que d’attendre que chaque serveur termine sa sauvegarde, vous pouvez utiliser une commande ad-hoc avec async pour lancer la tâche sur tous les serveurs en même temps et vérifier son état périodiquement.

Voici un exemple où l’on lance un script de sauvegarde sur tous les serveurs du groupe backup_servers en mode asynchrone :

Terminal window
ansible backup_servers -m shell -a "/usr/local/bin/backup.sh" -B 3600 -P 60

Dans cet exemple :

  • Le script de sauvegarde backup.sh peut s’exécuter pendant un maximum de 3600 secondes (1 heure).
  • Ansible vérifiera l’état de la tâche toutes les 60 secondes pour voir si elle est terminée.

Vérification de l’état des tâches asynchrones

Une fois la tâche lancée en arrière-plan, vous pouvez vérifier son statut à tout moment à l’aide de la commande async_status. Cela permet de suivre l’avancement de la tâche sans attendre qu’elle se termine.

Pour vérifier l’état d’une tâche asynchrone spécifique lancée avec une commande ad-hoc, vous devez utiliser le Job ID (identifiant de tâche), que vous pouvez récupérer depuis les résultats de la commande initiale. Voici un exemple de vérification du statut de la tâche :

Terminal window
ansible backup_servers -m async_status -a "jid=123456789"

Cette commande utilise le module async_status pour vérifier l’état de la tâche associée à l’identifiant de travail 123456789.

Commande ad-hoc avec async et throttle

Dans certaines situations, vous pourriez vouloir limiter le nombre de tâches asynchrones exécutées simultanément sur vos serveurs afin d’éviter une surcharge. Vous pouvez utiliser l’option --forks pour limiter le nombre d’hôtes auxquels Ansible se connecte simultanément, ce qui permet de contrôler la charge.

Exemple de commande ad-hoc avec une exécution limitée à 5 hôtes simultanément :

Terminal window
ansible backup_servers -m shell -a "/usr/local/bin/backup.sh" -B 3600 -P 60 --forks 5

Cette commande exécute la tâche de sauvegarde sur les serveurs, mais seulement sur 5 serveurs à la fois, ce qui est utile lorsque vous voulez éviter de surcharger vos ressources.

Limites des commandes ad-hoc avec async

Bien que les commandes ad-hoc avec async soient très pratiques, elles présentent certaines limites. En effet, contrairement aux playbooks, les commandes ad-hoc ne conservent pas l’historique des tâches exécutées sur plusieurs hôtes et le suivi des erreurs peut être plus difficile à gérer. De plus, l’utilisation d’async dans des commandes ad-hoc convient mieux pour des tâches rapides et ponctuelles plutôt que pour des opérations complexes nécessitant un suivi détaillé.

Utilisation d’asyncdans les playbooks

Le paramètre async spécifie le temps maximum (en secondes) qu’Ansible alloue à une tâche pour s’exécuter en arrière-plan. Cela permet à Ansible de lancer la tâche et de la laisser tourner indépendamment, tout en continuant à traiter d’autres tâches. Une fois la tâche terminée ou le délai atteint, Ansible vérifiera son état pour savoir si elle a réussi ou échoué.

Voici la syntaxe de base d’une tâche asynchrone dans un playbook Ansible :

- name: Exemple de tâche asynchrone
ansible.builtin.command: /usr/bin/long_running_task
async: 600 # Exécution maximum de 10 minutes (600 secondes)
poll: 5 # Vérification toutes les 5 secondes

Dans cet exemple, la commande /usr/bin/long_running_task est exécutée en asynchrone, avec un temps d’attente maximum de 600 secondes. Le paramètre poll détermine la fréquence à laquelle Ansible vérifiera l’état de la tâche. Cela permet d’éviter d’attendre la fin de la tâche tout en maintenant un contrôle périodique sur son état.

Si vous ne souhaitez pas que la tâche soit vérifiée périodiquement, vous pouvez définir le paramètre poll à 0. Dans ce cas, Ansible lancera la tâche, mais ne vérifiera pas son état, ce qui la laisse fonctionner complètement en arrière-plan sans intervention. Vous devrez alors utiliser une autre tâche pour vérifier manuellement son statut.

Exemple sans vérification périodique :

- name: Lancer une tâche longue sans suivi immédiat
ansible.builtin.command: /usr/bin/long_process
async: 1800 # 30 minutes d'exécution maximale
poll: 0 # Pas de vérification périodique

Dans ce cas, la tâche est simplement lancée en arrière-plan et Ansible ne fera pas de vérification automatique. Cela peut être utile pour des tâches très longues où vous ne voulez pas consommer des ressources inutiles en vérifiant l’état trop souvent.

Attendre la fin des taches asynchrones

Le module async_status d’Ansible est le module pour suivre l’avancement des tâches asynchrones exécutées avec le paramètre async. Ce module permet de vérifier si une tâche exécutée en arrière-plan est toujours en cours, si elle a réussi, échoué, ou si elle est encore en attente. Utiliser async_status est indispensable pour gérer les tâches longues ou parallélisées sans bloquer l’exécution d’autres opérations dans un playbook.

Chaque tâche asynchrone en cours d’exécution est associée à un Job ID unique, également appelé ansible_job_id. Ce Job ID est généré automatiquement par Ansible lorsque la tâche asynchrone est lancée. Vous devez enregistrer ce Job ID afin de pouvoir utiliser async_status pour interroger l’état de la tâche.

Voici un exemple de syntaxe pour vérifier l’état d’une tâche asynchrone :

- name: Lancer une tâche asynchrone
ansible.builtin.command: /path/to/long_running_task.sh
async: 600
poll: 0 # Exécution sans vérification immédiate
register: async_task # Enregistre l'état de la tâche asynchrone
- name: Vérifier l'état de la tâche asynchrone
ansible.builtin.async_status:
jid: "{{ async_task.ansible_job_id }}"
register: job_result # Enregistre le résultat de l'exécution de la tâche
- name: Afficher le résultat de la tâche
ansible.builtin.debug:
var: job_result

Le module async_status retourne des informations sur l’état de la tâche. Voici quelques exemples des données que vous pouvez recevoir dans le retour :

  • finished : Indique si la tâche est terminée ou non. Un résultat de True signifie que la tâche est terminée.
  • started : Confirme si la tâche a bien démarré. Si la tâche n’a pas pu être initiée, cela peut indiquer un problème.
  • exit_code : Le code de retour de la tâche. Un exit_code de 0 signifie généralement que la tâche s’est bien déroulée.
  • stdout et stderr : Ces champs contiennent respectivement la sortie standard et les messages d’erreur de la tâche asynchrone.
  • failed : Si ce champ est défini à True, cela signifie que la tâche a échoué.

Exemple de résultat de l’utilisation d’async_status :

job_result:
finished: true
started: true
exit_code: 0
stdout: "Tâche terminée avec succès"
stderr: ""
failed: false

Comment stopper le parralèlisme des tâches

Voici un playbook Ansible que je vais expliquer en détail pour illustrer l’utilisation des tâches asynchrones et du module async_status pour attendre la fin des tâches synchrones en cours :

- name: Lancer une tâche longue en asynchrone
ansible.builtin.command: /path/to/backup.sh
async: 1800 # La tâche peut durer jusqu'à 30 minutes
poll: 0 # Pas de vérification automatique
register: async_task # Enregistre l'état de la tâche asynchrone
- name: Attendre quelques secondes avant de vérifier
ansible.builtin.pause:
seconds: 30 # Pause de 30 secondes avant de vérifier l'état de la tâche
- name: Vérifier l'état de la tâche asynchrone
ansible.builtin.async_status:
jid: "{{ async_task.ansible_job_id }}" # Utilise l'ID de la tâche enregistrée
register: job_result
until: job_result.finished
retries: 100
delay: 10

Quelques explications :

  1. register: job_result : Le résultat de la commande async_status (c’est-à-dire l’état actuel de la tâche asynchrone) est stocké dans la variable job_result. Cette variable contient des informations telles que si la tâche est terminée ou encore en cours d’exécution, ainsi que les messages de sortie et les éventuelles erreurs.
  2. until: job_result.finished : Cette condition signifie que la tâche continuera de s’exécuter tant que la variable job_result.finished est False. Autrement dit, Ansible va répéter cette tâche de vérification jusqu’à ce que la tâche asynchrone soit terminée.
  3. retries: 100 : Ansible tentera de vérifier l’état de la tâche jusqu’à 100 fois maximum. Si la condition until n’est pas remplie après 100 tentatives, la tâche échouera.
  4. delay: 10 : Il y aura un délai de 10 secondes entre chaque tentative de vérification de l’état de la tâche asynchrone. Cela permet de ne pas interroger trop souvent le statut de la tâche.

Limiter les ressources avec throttle

Lorsque vous exécutez des tâches asynchrones sur plusieurs hôtes dans Ansible, il est important de prendre en compte l’impact sur les ressources système. Lancer trop de tâches en parallèle peut surcharger vos serveurs ou votre réseau, entraînant une dégradation des performances. Pour éviter cela, Ansible offre la possibilité de limiter le nombre de tâches qui s’exécutent simultanément en utilisant le paramètre throttle.

Le paramètre throttle permet de contrôler le nombre de tâches asynchrones qui peuvent être exécutées en parallèle sur les hôtes cibles. Cette fonctionnalité est particulièrement utile dans des environnements avec des ressources limitées ou lors de déploiements sur des centaines de machines. Par exemple, si vous devez installer un logiciel sur plusieurs dizaines de serveurs, vous pouvez limiter le nombre d’installations exécutées simultanément afin de ne pas surcharger vos systèmes ou votre réseau.

En utilisant throttle, Ansible s’assure de ne pas lancer trop de tâches à la fois, ce qui permet de mieux gérer les ressources et d’éviter des pics de charge inutiles.

Utilisation de throttle dans un playbook

Le paramètre throttle est spécifié directement dans une tâche de votre playbook. Il définit le nombre maximum d’instances de cette tâche qui peuvent être exécutées simultanément sur les hôtes définis.

Voici un exemple de tâche où throttle limite le nombre de tâches simultanées à 3 :

- name: Installation d'un logiciel sur plusieurs serveurs avec throttle
ansible.builtin.apt:
name: nginx
state: present
async: 600 # Exécution en mode asynchrone avec un délai maximum de 10 minutes
poll: 5 # Vérification toutes les 5 secondes
throttle: 3 # Limite à 3 installations simultanées
when: inventory_hostname in groups['webservers']

Dans cet exemple :

  • async: 600 exécute la tâche d’installation en arrière-plan pendant un maximum de 600 secondes.
  • poll: 5 signifie qu’Ansible vérifie l’état de chaque tâche toutes les 5 secondes.
  • throttle: 3 limite le nombre de tâches asynchrones exécutées simultanément à 3. Cela signifie que, même si vous avez 10 serveurs web, seules 3 installations seront lancées à la fois. Les autres serveurs attendent leur tour pour que les ressources ne soient pas surchargées.

Comparaison avec forks

Il est important de différencier throttle de forks, une autre option d’Ansible qui contrôle le nombre d’hôtes auxquels Ansible se connecte simultanément. Forks définit le nombre de connexions en parallèle aux hôtes, tandis que throttle limite le nombre d’exécutions simultanées d’une tâche donnée.

Par exemple, vous pourriez utiliser forks pour limiter Ansible à se connecter à 10 hôtes simultanément et à l’intérieur de ces connexions, utiliser throttle pour limiter à 2 le nombre de tâches asynchrones exécutées simultanément.

Limites de throttle

Même si throttle est un outil puissant, il a certaines limitations :

  • Synchronisation globale : Si vous avez besoin de synchroniser des actions sur plusieurs hôtes en fonction de l’état global du groupe, throttle seul ne suffira pas. Vous devrez peut-être utiliser d’autres techniques de gestion d’état ou de déploiement progressif.
  • Gestion fine des priorités : Throttle ne permet pas de donner la priorité à certaines tâches ou hôtes. Si vous avez des serveurs plus critiques, vous devrez les gérer différemment.

Conclusion

Les tâches asynchrones sous Ansible offrent une solution puissante pour exécuter des opérations longues ou complexes sans bloquer l’ensemble du déploiement. Grâce à l’utilisation des paramètres async et poll, il est possible de lancer des tâches en arrière-plan, de les surveiller et de continuer l’exécution d’autres parties du playbook en parallèle. Cela permet non seulement d’optimiser les temps d’exécution, mais aussi de mieux gérer les ressources disponibles sur vos serveurs.

Que ce soit pour installer des paquets sur plusieurs serveurs, gérer des sauvegardes volumineuses, ou orchestrer des déploiements complexes sur des infrastructures distribuées, les tâches asynchrones sont une fonctionnalité essentielle pour les administrateurs systèmes. Elles permettent d’éviter les blocages inutiles et de garantir une meilleure efficacité dans la gestion des ressources.

Ces fonctionnalités rendent Ansible encore plus adapté aux déploiements à grande échelle ou à des opérations complexes, tout en offrant une meilleure gestion des performances. Toutefois, comme pour toute opération, il est essentiel de bien configurer et surveiller l’utilisation des tâches asynchrones pour s’assurer que les ressources sont utilisées efficacement et que les tâches s’exécutent dans les temps prévus.