Ansible: Fondations et logique de base
Notions abordées
- Introduction à Ansible
- Installation et premiers pas
- Structure YAML
- Premier playbook
Création des machines virtuelles
Pour ce premier cours, nous allons utiliser des machines virtuelles locales. Voici les liens pour télécharger les .ova:
Importez ces fichiers .ova dans VMWare Workstation pour créer les machines virtuelles.
Connexion aux machines virtuelles
Une fois les machines créées, démarrez-les. Faites la commande ip a dans chaque machine pour obtenir leur adresse IP et prenez-en note.
Sur votre machine hôte, ouvrez un terminal et connectez-vous à chaque machine virtuelle en utilisant SSH. Par exemple, pour vous connecter à la machine "control", utilisez la commande suivante :
ssh ansible@192.168.56.10 #Adresse IP de la machine "control"
# Tapez "yes" si demandé (première connexion)
# Mot de passe : Ansible123!
exit
Répétez cette étape pour les machines "node1" et "node2" pour s'assurer que vous pouvez vous y connecter via SSH.
Le mot de passe par défaut est Ansible123!
Si certaines de vos machines ont la même adresse IP, vous pouvez réappliquer les configurations réseau en effectuant la commande suivante sur chaque machine virtuelle:
sudo netplan apply
Installation d'Ansible
La machine ansible-controller a déjà le package ansible installé dessus. Pour en avoir la confirmation, exécutez la commande suivante:
ansible --version
Si ansible n'est pas installé, vous pouvez le faire avec les commandes suivantes:
sudo apt update
sudo apt install -y ansible sshpass
ansible --version
Vous devriez voir la version d'Ansible installée affichée dans le terminal.
Test de l'authentification SSH
Ansible utilise SSH pour se connecter aux machines gérées. Pour ce cours, nous allons utiliser l'authentification par mot de passe, qui est plus simple pour débuter.
Note importante : En production, on utilise plutôt des clés SSH (plus sécurisées). Nous verrons cette méthode dans les cours avancés.
Pour vérifier que vous pouvez vous connecter manuellement aux nœuds, testez la connexion SSH depuis la machine "control" (remplacez les adresses IP par celles de vos nœuds) :
# Test de connexion manuelle à node1
ssh ansible@192.168.56.11
# Tapez "yes" si demandé (première connexion)
# Mot de passe : Ansible123!
# Vous êtes maintenant sur node1!
exit
# Test de connexion manuelle à node2
ssh ansible@192.168.56.12
# Mot de passe : Ansible123!
exit
Ce que vous venez de faire : Vous avez testé que SSH fonctionne. Ansible va faire exactement la même chose automatiquement pour vous!
Vérification de la connectivité Ansible
Pour vérifier que Ansible peut se connecter aux nœuds gérés, créez un fichier d'inventaire simple. Depuis la machine "control", créez un fichier nommé hosts.ini:
cd ~
mkdir ansible-semaine-10
cd ansible-semaine-10
nano hosts.ini
Ajoutez le contenu suivant au fichier hosts.ini en remplaçant les adresses IP par les adresses de vos noeuds gérés:
[web]
node1 ansible_host=192.168.56.11
[db]
node2 ansible_host=192.168.56.12
[all:vars]
ansible_user=ansible
ansible_ssh_pass=Ansible123!
ansible_become_pass=Ansible123!
Explications:
[web]et[db]sont des groupes d'hôtes. Vous pouvez organiser vos nœuds gérés en groupes selon leur rôle.- La ligne
node1 ansible_host=192.168.56.11associe le nom d'hôtenode1à son adresse IP. - La section sous
[all:vars]définit des variables pour l'utilisateur SSH et les mots de passe pour tous les nœuds.
Ensuite, testez la connectivité avec la commande Ansible suivante :
ansible all -i hosts.ini -m ping
Vous devriez voir une sortie indiquant que chaque nœud a répondu avec "pong", ce qui signifie qu'Ansible peut communiquer avec succès avec les nœuds gérés.
Que s'est-il passé ici?
- La commande commence par
ansible all, ce qui signifie que nous ciblons tous les hôtes définis dans l'inventaire. - L'option
-i hosts.inispécifie le fichier d'inventaire à utiliser. - Le module
-m pingest utilisé pour envoyer une requête ping simple à chaque nœud.
Chaque nœud qui répond avec "pong" confirme que la connexion SSH fonctionne correctement et qu'Ansible peut gérer ces nœuds.
Cette première commande montre que nous pouvons exécuter une action simple sur tous les nœuds gérés. Dans les prochains cours, nous explorerons des actions plus complexes et la création de playbooks Ansible pour automatiser la gestion d'infrastructures.
L'image suivante illustre l'architecture de base que nous venons de mettre en place :

Explications:
- Le noeud (ou la machine) de gestion (Management Node) exécute Ansible et envoie des commandes aux nœuds gérés (Host 1, Host 2, etc.). Dans notre cas, c'est la machine "control".
- Le noeud (ou la machine) géré (Managed Node) utilise un fichier Playbook (que nous verrons plus tard) et un fichier d'inventaire (hosts.ini).
- Le fichier d'inventaire (Inventory File) contient la liste des nœuds gérés et leurs adresses IP, et les regroupe en groupes logiques (comme "web" et "db" dans notre cas, ou "group A" et "group B" dans l'image).
- Ansible utilise SSH pour se connecter aux nœuds gérés et exécuter les commandes.
Fichiers de configuration Ansible
Ansible utilise un fichier de configuration principal nommé ansible.cfg. Par défaut, Ansible cherche ce fichier dans plusieurs emplacements, mais pour ce cours, nous allons créer un fichier de configuration spécifique dans notre répertoire de travail. Depuis la machine "control", créez un fichier nommé ansible.cfg dans le répertoire ansible-semaine-10 (à côté de hosts.ini) :
nano ansible.cfg
Ajoutez le contenu suivant au fichier ansible.cfg :
[defaults]
inventory = ./hosts.ini
host_key_checking = False
Explications:
- La section
[defaults]contient les paramètres par défaut pour Ansible. - La ligne
inventory = ./hosts.iniindique à Ansible d'utiliser le fichierhosts.inicomme inventaire par défaut. - La ligne
host_key_checking = Falsedésactive la vérification des clés d'hôte SSH, ce qui est utile dans un environnement de laboratoire où les hôtes peuvent être recréés fréquemment.
Avec cette configuration, vous n'aurez plus besoin de spécifier le fichier d'inventaire à chaque commande Ansible, car Ansible utilisera automatiquement hosts.ini dans le répertoire courant.
Vous pouvez maintenant exécuter à nouveau la commande ping sans l'option -i :
ansible all -m ping
Cela devrait fonctionner de la même manière qu'avant, confirmant que la configuration est correcte.
Le langage YAML
Ansible utilise le langage YAML (YAML Ain't Markup Language) pour définir les playbooks et les fichiers de configuration. YAML est un format de sérialisation de données lisible par l'humain, souvent utilisé pour les fichiers de configuration.
Anecdote: Le nom YAML est un acronyme récursif signifiant "YAML Ain't Markup Language", soulignant que YAML est conçu pour être un format de données plutôt qu'un langage de balisage (markup).
Voici quelques concepts de base de YAML que vous devez connaître pour travailler avec Ansible :
Début et fin d'un document: Un document YAML commence par
---et peut se terminer par...(optionnel).Indentation: YAML utilise l'indentation pour définir la hiérarchie des données. Assurez-vous d'utiliser des espaces (et non des tabulations) pour l'indentation.
Listes: Les listes sont définies en utilisant des tirets (
-). Par exemple :fruits: - pomme - banane - ceriseDictionnaires: Les dictionnaires sont définis en utilisant des paires clé-valeur. Par exemple :
personne: nom: Alex age: 30 ville: MontrealListes de dictionnaires: Vous pouvez combiner les listes et les dictionnaires. Par exemple :
personnes: - nom: Alex age: 30 - nom: Marie age: 25
Ici, personnes est une liste contenant deux dictionnaires, chacun représentant une personne avec des attributs nom et age. Ainsi, le premier élément de la liste personnes est un dictionnaire avec les clés nom et age (soit Alex, 30), et le second élément est un autre dictionnaire avec les clés nom et age (soit Marie, 25).
- Commentaires: Les commentaires commencent par le symbole
#et s'étendent jusqu'à la fin de la ligne. Par exemple :# Ceci est un commentaire nom: Alex # Nom de la personne
Facultatif mais recommandé: Éditeur de texte avec support YAML
Pour faciliter l'écriture et la lecture des fichiers YAML, il est recommandé d'utiliser un éditeur de texte qui prend en charge la coloration syntaxique et la validation YAML.
Sur Visual Studio Code, vous pouvez installer l'extension "YAML" de Red Hat, qui offre une excellente prise en charge de YAML, y compris la validation et l'autocomplétion. Pour l'installer :
- Ouvrez Visual Studio Code.
- Allez dans l'onglet des extensions (icône de carré avec quatre carrés) ou utilisez le raccourci
Ctrl+Shift+X. - Recherchez "YAML" et installez l'extension développée par Red Hat.
- Redémarrez Visual Studio Code si nécessaire. Cela vous aidera à éviter les erreurs courantes liées à la syntaxe YAML lors de la création de vos playbooks Ansible.
Un premier playbook Ansible
Un playbook Ansible est un fichier YAML qui décrit une série de tâches à exécuter sur un ou plusieurs hôtes. Voici un exemple simple de playbook qui pinge tous les nœuds gérés :
--- # début du fichier YAML
- name: Mon premier playbook # description de ce qu'on fait
hosts: all # sur quelles machines?
tasks: # liste des actions
- name: Première tâche # toujours nommer!
ping: # module à utiliser
Explications:
- La ligne
---indique le début d'un document YAML. - La clé
namefournit une description lisible du playbook. - La clé
hostsspécifie les hôtes cibles pour le playbook (ici, tous les hôtes). - La clé
taskscontient une liste de tâches à exécuter. - Chaque tâche a également une clé
namepour la décrire et spécifie le module Ansible à utiliser (ici, le moduleping).
Vous remarquerez l'importance de l'indentation en YAML. Chaque niveau d'indentation représente une hiérarchie dans les données.
Pour exécuter ce playbook, enregistrez-le dans un fichier nommé mon_premier_playbook.yml dans le répertoire ansible-semaine-10, puis exécutez la commande suivante depuis la machine "control" :
ansible-playbook mon_premier_playbook.yml
Vous devriez voir une sortie indiquant que chaque nœud a répondu avec "pong", confirmant que le playbook a été exécuté avec succès:
PLAY [Mon premier playbook] ********************************************************
TASK [Gathering Facts] *************************************************************
ok: [node2]
ok: [node1]
TASK [Première tâche] **************************************************************
ok: [node2]
ok: [node1]
PLAY RECAP *************************************************************************
node1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node2 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Analyse de la sortie:
- La section
PLAY [Mon premier playbook]indique le début de l'exécution du playbook. - La tâche
Gathering Factsest une tâche automatique qu'Ansible exécute pour collecter des informations sur les nœuds gérés. - La tâche
Première tâchecorrespond à notre tâche définie dans le playbook. - La section
PLAY RECAPrésume les résultats de l'exécution pour chaque nœud, indiquant le nombre de tâches réussies (ok), modifiées (changed), inaccessibles (unreachable), échouées (failed), etc.
Ici, nous voyons que les deux nœuds (node1 et node2) ont répondu avec succès à la tâche de ping, ce qui signifie qu'Ansible a pu se connecter et exécuter la tâche sur ces nœuds sans problème. À noter que nous ne voyons pas la sortie du module ping lui-même (le "pong") dans cette sortie, mais le fait que la tâche soit marquée comme ok indique que le ping a réussi.
Vous remarquerez que le nombre de tâches ok est de 2 pour chaque nœud (une pour Gathering Facts et une pour Première tâche), et le nombre de tâches changed est de 0, ce qui signifie qu'aucune modification n'a été apportée aux nœuds, ce qui est attendu pour une tâche de ping.
Explorons d'autres playbooks simples pour bien comprendre la structure et les fonctionnalités d'Ansible.
Affichage de noms d'hôtes
Voici un playbook qui affiche le nom d'hôte de chaque nœud géré en
utilisant le module command pour exécuter la commande hostname :
--- # début du fichier YAML
- name: Afficher les noms d'hôtes des nœuds gérés
hosts: all # cibler tous les hôtes
tasks:
- name: Afficher le nom d'hôte
command: hostname # exécuter la commande 'hostname'
Ici, à la différence du playbook précédent, nous utilisons le module command pour exécuter une commande shell sur les nœuds gérés. Nous fournissons à ce module la commande hostname, qui renvoie le nom d'hôte de la machine. Lors de l'exemple précédent, le module ping était un module Ansible spécifique conçu pour tester la connectivité, tandis que le module command permet d'exécuter n'importe quelle commande shell.
Enregistrez ce playbook dans un fichier nommé afficher_hostname.yml et exécutez-le avec la commande suivante :
ansible-playbook afficher_hostname.yml
Vous devriez voir la sortie affichant le nom d'hôte de chaque nœud géré:
PLAY [Afficher les noms d'hôtes des nœuds gérés] ***********************************
TASK [Gathering Facts] *************************************************************
ok: [node1]
ok: [node2]
TASK [Afficher le nom d'hôte] ******************************************************
changed: [node1]
changed: [node2]
PLAY RECAP *************************************************************************
node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node2 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Analyse de la sortie:
- Le nombre de tâches
okest de 2 pour chaque nœud (une pourGathering Factset une pourAfficher le nom d'hôte). - Le nombre de tâches
changedest de 1 pour chaque nœud, ce qui indique que la tâcheAfficher le nom d'hôtea été exécutée avec succès. Pourquoichanged? Parce que le modulecommandconsidère qu'une exécution de commande modifie l'état du système, même si la commande elle-même ne change rien. Contrairement au moduleping, qui est idempotent et ne modifie pas l'état du système, le modulecommandexécute la commande à chaque fois, ce qui est interprété comme un changement. - La sortie de la commande
hostnamen'est pas affichée directement dans la sortie standard d'Ansible. Pour voir la sortie réelle de la commande, vous pouvez utiliser le moduledebugpour afficher la sortie.
Modifiez le playbook pour capturer et afficher la sortie de la commande hostname :
--- # début du fichier YAML
- name: Afficher les noms d'hôtes des nœuds gérés
hosts: all # cibler tous les hôtes
tasks:
- name: Exécuter la commande hostname
command: hostname
register: hostname_output # enregistrer la sortie dans une variable
- name: Afficher le nom d'hôte
debug:
msg: "Le nom d'hôte est {{ hostname_output.stdout }}" # afficher la sortie
Changements apportés:
- La tâche
Exécuter la commande hostnameutilise le moduleregisterpour capturer la sortie de la commandehostnamedans une variable nomméehostname_output. - La tâche
Afficher le nom d'hôteutilise le moduledebugpour afficher un message contenant la sortie capturée, en accédant àhostname_output.stdout, qui contient la sortie standard de la commande exécutée.
Enregistrez ce playbook modifié dans un fichier nommé afficher_hostname_debug.yml et exécutez-le avec la commande suivante :
ansible-playbook afficher_hostname_debug.yml
Vous devriez voir une sortie affichant le nom d'hôte de chaque nœud. Nous avons maintenant 3 tâches: Gathering Facts, Exécuter la commande hostname, et Afficher le nom d'hôte. Nous avons cependant encore qu'un seul "play" (le playbook entier). Nous verrons plus tard comment structurer des playbooks avec plusieurs plays.
Création d'un répertoire
Voici un playbook qui crée un répertoire nommé /tmp/ansible_test sur chaque
nœud géré en utilisant le module file :
--- # début du fichier YAML
- name: Créer un répertoire sur les nœuds gérés
hosts: all # cibler tous les hôtes
tasks:
- name: Créer le répertoire /tmp/ansible_test
file:
path: /tmp/ansible_test
state: directory
mode: '0755'
Explications:
- Le module
fileest utilisé pour gérer les fichiers et les répertoires. - La clé
pathspécifie le chemin du répertoire à créer. - La clé
state: directoryindique que nous voulons créer un répertoire. - La clé
modedéfinit les permissions du répertoire en notation octale. 0755 signifie que le propriétaire a tous les droits (lecture, écriture, exécution), tandis que les autres utilisateurs ont des droits de lecture et d'exécution (allez revoir vos notes du cours 2C2 si besoin!). Enregistrez ce playbook dans un fichier nommécreer_repertoire.ymlet exécutez-le avec la commande suivante :
ansible-playbook creer_repertoire.yml
Analyse de la sortie:
PLAY [Créer un répertoire sur les nœuds gérés] *************************************
TASK [Gathering Facts] *************************************************************
ok: [node1]
ok: [node2]
TASK [Créer le répertoire /tmp/ansible_test] ***************************************
changed: [node2]
changed: [node1]
PLAY RECAP *************************************************************************
node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node2 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
- Le nombre de tâches
okest de 2 pour chaque nœud (une pourGathering Factset une pourCréer le répertoire /tmp/ansible_test). - Le nombre de tâches
changedest de 1 pour chaque nœud, ce qui indique que le répertoire/tmp/ansible_testa été créé avec succès sur chaque nœud géré.
Pour vérifier que le répertoire a bien été créé, vous pouvez vous connecter à chaque nœud géré et lister le contenu du répertoire /tmp :
ssh vagrant@192.168.56.11
ls -ld /tmp/ansible_test
exit
ssh vagrant@192.168.56.12
ls -ld /tmp/ansible_test
exit
Installation d'un package
Voici un playbook qui installe le package curl sur chaque nœud géré en utilisant le module apt (puisque nos nœuds sont basés sur Ubuntu) :
--- # début du fichier YAML
- name: Installer curl sur les nœuds gérés
hosts: all # cibler tous les hôtes
tasks:
- name: Installer le package curl
apt:
name: curl
state: present
update_cache: yes
Explications:
- Le module
aptest utilisé pour gérer les packages sur les systèmes basés sur Debian/Ubuntu. - La clé
namespécifie le nom du package à installer. - La clé
state: presentindique que nous voulons que le package soit installé. - La clé
update_cache: yesmet à jour le cache des packages avant l'installation. Enregistrez ce playbook dans un fichier nomméinstaller_curl.ymlet exécutez-le avec la commande suivante :
ansible-playbook installer_curl.yml
Cette commande devrait échouer! Vous devrez voir un message d'erreur :
fatal: [node1]: FAILED! => {"changed": false, "msg": "Failed to lock apt for exclusive operation"}
fatal: [node2]: FAILED! => {"changed": false, "msg": "Failed to lock apt for exclusive operation"}
Cela se produit parce que l'installation de packages nécessite des privilèges élevés (sudo). Pour résoudre ce problème, nous devons exécuter la tâche avec des privilèges élevés en ajoutant la directive become: yes au playbook. Ajoutons cette directive dans le playbook.
Enregistrez le playbook modifié dans un fichier nommé installer_curl_sudo.yml :
- name: Installer curl sur les nœuds gérés avec sudo
hosts: all # cibler tous les hôtes
become: yes # exécuter les tâches avec des privilèges élevés
tasks:
- name: Installer le package curl
apt:
name: curl
state: present
update_cache: yes
Cela indique à Ansible d'exécuter les tâches avec des privilèges élevés (sudo). Maintenant, exécutez le playbook modifié avec la commande suivante :
ansible-playbook installer_curl_sudo.yml
Vous devriez voir une sortie indiquant que le package curl a été installé avec succès sur chaque nœud géré. Vous remarquerez que le nombre de tâches changed est à 0. Cela signifie que curl était déjà installé sur les nœuds gérés, donc aucune modification n'a été nécessaire.
Nous pouvons essayer de modifier notre playbook pour installer un package qui n'est pas encore installé, comme cowsay. Voici le playbook modifié :
--- # début du fichier YAML
- name: Installer htop sur les nœuds gérés avec sudo
hosts: all # cibler tous les hôtes
become: yes # exécuter les tâches avec des privilèges élevés
tasks:
- name: Installer le package cowsay
apt:
name: cowsay
state: present
update_cache: yes
Enregistrez ce playbook dans un fichier nommé installer_cowsay.yml et exécutez-le avec la commande suivante :
ansible-playbook installer_cowsay.yml
Vous devriez voir une sortie indiquant que le package cowsay a été installé avec succès sur chaque nœud géré, et cette fois, le nombre de tâches changed sera de 1, indiquant qu'une modification a été apportée (l'installation de cowsay).
Playbooks plus complexes
Ansible propose de nombreux modules pour effectuer diverses tâches, telles que l'installation de packages, la gestion des services, la copie de fichiers, et bien plus encore. Voici un exemple de playbook plus complexe qui installe et démarre un serveur web Apache sur le nœud node1 :
--- # début du fichier YAML
- name: Installer et démarrer Apache sur le serveur web
hosts: web # cibler le groupe 'web' défini dans l'inventaire
become: yes # exécuter les tâches avec des privilèges élevés
tasks:
- name: Installer Apache
apt:
name: apache2
state: present
update_cache: yes
- name: Démarrer Apache
service:
name: apache2
state: started
enabled: yes
Explications:
- La clé
hosts: webcible uniquement le groupewebdéfini dans le fichier d'inventaire, qui contientnode1. - La clé
become: yesindique qu'Ansible doit exécuter les tâches avec des privilèges élevés (sudo). - Le module
aptest utilisé pour gérer les packages sur les systèmes basés sur Debian/Ubuntu. - Le module
serviceest utilisé pour gérer les services système.
Vous pouvez enregistrer ce playbook dans un fichier nommé install_apache.yml et l'exécuter de la même manière que précédemment :
ansible-playbook install_apache.yml
Cela installera et démarrera Apache sur le nœud node1. Vous pouvez vérifier que le serveur web fonctionne en accédant à l'adresse IP de node1 avec la commande curl depuis la machine "control" :
curl http://192.168.56.11/index.html
Vous devriez voir la page par défaut d'Apache s'afficher dans le terminal... même si elle est difficile à lire dans ce format!
Pour une meilleure expérience, vous pouvez utiliser le navigateur web de votre machine hôte et accéder à l'adresse http://192.168.56.11/index.html.
Le concept de idempotence
L'idempotence est un concept clé dans Ansible et l'automatisation en général. Cela signifie que l'exécution répétée d'une opération produit le même résultat que son exécution une seule fois. En d'autres termes, si vous exécutez un playbook plusieurs fois, le système restera dans le même état après la première exécution.
Par exemple, si vous exécutez le playbook install_apache.yml plusieurs fois, Apache sera installé et démarré la première fois, et les exécutions suivantes ne feront rien car le système est déjà dans l'état désiré (Apache est installé et en cours d'exécution).
Cela permet d'éviter les modifications inutiles et garantit que les systèmes restent cohérents.
L'idempotence est essentielle pour la gestion de la configuration, car elle permet aux administrateurs système de s'assurer que les systèmes sont toujours dans l'état souhaité, sans craindre que des exécutions répétées de scripts ou de playbooks n'introduisent des erreurs ou des incohérences.