ANSIBLE : Installation et premiers pas
Introduction
Ansible est un moteur d’automatisation open-source qui simplifie de nombreux processus informatiques complexes, de la gestion de configurations à l’orchestration de tâches multi-niveaux.
Dans cet article, nous explorerons les bases d’Ansible, en commençant par son installation sur une distribution Linux. Ensuite, nous plongerons dans les fondamentaux de sa prise en main et avancerons pas à pas vers la création d’un premier playbook.
Architecture et Principes de Base
Ansible repose sur une architecture sans agent, signifiant qu’il n’est pas nécessaire d’installer un logiciel supplémentaire sur les nœuds gérés. Il utilise SSH (ou WinRM pour les machines Windows) pour communiquer avec les hôtes, garantissant ainsi sécurité et simplicité. Cette approche minimaliste réduit la courbe d’apprentissage et facilite la maintenance.
Playbooks et Automatisation
Au cœur d’Ansible se trouvent les « Playbooks », des fichiers de configuration écrits en YAML (Yet Another Markup Language). Ces Playbooks définissent les tâches à exécuter et dans quel ordre. Chaque Playbook peut contenir une ou plusieurs « plays », chaque play étant destiné à un groupe de machines cibles et peut être composé de plusieurs tâches.
L’inventaire
L’inventaire est un autre composant clé d’Ansible. Il s’agit d’un fichier qui liste toutes les machines à gérer, organisées en groupes. L’inventaire peut être simple (un fichier texte plat) ou complexe (une source dynamique), offrant une flexibilité pour gérer divers environnements.
Les modules
Ansible possède une riche bibliothèque de modules qui peuvent être appelés dans les Playbooks. Ces modules permettent d’effectuer une variété de tâches sur les machines distantes, allant de la gestion des fichiers à la configuration des services réseau.
Idempotence et Convergence
L’un des principes fondamentaux d’Ansible est l’idempotence, la capacité d’exécuter plusieurs fois une même commande sans changer l’état final du système après la première exécution réussie. Cela garantit la convergence, où Ansible effectue les modifications nécessaires pour atteindre l’état désiré défini dans le Playbook.
Installation d’Ansible
La plupart des distributions Linux proposent une installation d’Ansible dans les packages officiels. Ceci dit, ces packages ne sont que très rarement à jour.
Ansible étant développé en Python, la solution la plus universelle consiste à l’installer via pip
ou pipx
. Ce dernier assurant que les dépendances d’Ansible soient isolées et ne puissent pas entrer en conflit avec d’autres applications sur le système.
Installation des pré-requis
Vous devez bien évidemment disposer d’un interpréteur Python récent, comme c’est le cas sur la majorité des distributions linux, je vais considérer ce point comme acquis.
Ensuite nous allons utiliser pipx
pour installer Ansible, que nous pouvons installer lui-même à l’aide de pip
. (Si vous n’avez pas pip
installé sur votre machine, vous trouverez les différentes méthode possible ici: https://pip.pypa.io/en/stable/installation/)
pip install pipx
Si vous voyez le message ci-dessous s’afficher, cela signifie que le chemin d’accès à pipx n’est pas dans votre $PATH et qu’il est nécessaire de l’y ajouter:
WARNING: The script pipx is installed in '/home/steve/.local/bin' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Pour ça il vous suffit de rajouter PATH="$HOME/.local/bin:$PATH"
à la fin de votre .bashrc
.
echo 'PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
Installation d’Ansible avec pipx
pipx install --include-deps ansible
Ce qui donnera…
steve@jsi-ubuntu ~ $ pipx install --include-deps ansible
installed package ansible 9.1.0, installed using Python 3.10.12
These apps are now globally available
- ansible
- ansible-community
- ansible-config
- ansible-connection
- ansible-console
- ansible-doc
- ansible-galaxy
- ansible-inventory
- ansible-playbook
- ansible-pull
- ansible-test
- ansible-vault
done! ✨ 🌟 ✨
steve@jsi-ubuntu ~ $
Tester l’installation d’Ansible
Pour tester l’installation d’Ansible, nous pouvons afficher sa version: ansible --version
steve@jsi-ubuntu ~ $ ansible --version
ansible [core 2.16.2]
config file = None
configured module search path = ['/home/steve/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/steve/.local/share/pipx/venvs/ansible/lib/python3.10/site-packages/ansible
ansible collection location = /home/steve/.ansible/collections:/usr/share/ansible/collections
executable location = /home/steve/.local/bin/ansible
python version = 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] (/home/steve/.local/share/pipx/venvs/ansible/bin/python)
jinja version = 3.1.3
libyaml = True
steve@jsi-ubuntu ~ $
Ou encore tester une première commande ad-hoc (je reviens sur ce principe plus tard): ansible -m ping localhost
steve@jsi-ubuntu ~ $ ansible -m ping localhost
[WARNING]: No inventory was parsed, only implicit localhost is available
localhost | SUCCESS => {
"changed": false,
"ping": "pong"
}
steve@jsi-ubuntu ~ $
L’alerte concernant l’absence d’inventaire est normale à ce point. Nous y reviendrons plus tard.
Créer la configuration d’Ansible
Le configuration d’Ansible peut être définie à plusieurs endroits de telle sorte qu’elle soit chargée automatiquement. Par ordre de priorité, Ansible chargera sa configuration depuis:
- La variable d’environnement
ANSIBLE_CONFIG
- Le fichier
ansible.cfg
dans le répertoire courant - Le fichier
~/.ansible.cfg
(donc relatif à l’utilisateur) - Le fichier
/etc/ansible/ansible.cfg
Dés que l’une de ces options est disponible, la configuration est chargée et les suivant ne sont pas utilisés. Ceci permet par exemple d’avoir une configuration générale et une plus spécifique pour chaque utilisateur ou même chaque projet.
Dans le cadre de cet article, nous utiliserons le fichier
~/.ansible.cfg
.
Pour commencer simplement, nous allons partir d’un fichier vierge et y ajouter quelques options, tout ce qui n’est pas défini utilisera les valeurs par défaut d’Ansible.
[defaults]
# Définit où se trouve le fichier d'inventaire par défaut
inventory = ~/ansible/hosts.yaml
# Définit combient de jobs peuvent être lancés en simultanés
forks = 20
# Supprime les alertes de dépréciation
deprecation_warnings = False
# Defines how tasks are handled
strategy = linear
# Désactive la vérification des clés SSH des hôtes
host_key_checking = False
# Désactive les alertes de découvertes d'interpréteurs
interpreter_python = auto_silent
# Affiche les hôtes ignorés lors d'une exécution de tâche
display_skipped_hosts = no
Ce sont généralement les paramètres généraux que j’utilise toujours, le plus important étant bien sûr la localisation de l’inventaire. Le nombre de forks
permettra d’exécuter des tâches sur plusieurs hôtes en parallèle. La stratégie linéaire assure que chaque tâche sera complètement terminée avant de passer à la suivante. Et le host_key_checking
désactivé nous épargnera des soucis avec les clés d’hôtes SSH enregistré dans ~/.ssh/known_hosts
surtout dans le cadre d’un laboratoire de test.
Création de l’inventaire d’Ansible
L’inventaire est la pierre angulaire d’Ansible. Il s’agit d’un fichier contenant la définition des hôtes et groupes d’hôtes avec lesquels Ansible pourra interagir. Cet inventaire peut être écrit soit selon un format INI soit selon un format YAML.
Lister les hôtes dans l’inventaire
Nous allons commencer ici par un inventaire au format INI le plus basique possible, mais pour ce faire voici un tableau récapitulatif des hôtes que je vais utiliser dans les exemples suivants (à vous d’adapter):
Hôte | Adresse IPv4 | Username | Password | Sudo Password |
---|---|---|---|---|
jsi-ubuntu | 192.168.242.10 | steve | jsi_password | jsi_sudo_password |
jsi-debian | 192.168.242.11 | steve | jsi_password | jsi_sudo_password |
jsi-rocky8 | 192.168.242.12 | steve | jsi_password | jsi_sudo_password |
jsi-arch | 192.168.242.13 | steve | jsi_password | jsi_sudo_password |
Comme dit dans l’introduction Ansible se repose sur SSH pour les hôtes Linux, il est donc nécessaire d’avoir un services SSH fonctionnel sur les hôtes qu’on souhaite gérer. Toujours dans le but de rester aussi simple que possible, l’authentification SSH se fera par mot de passe, ce qui n’est pas idéal en soi, mais fonctionnera très bien pour de premiers essais.
jsi-ubuntu
jsi-debian
jsi-rocky8
jsi-arch
Difficile de faire plus basique que ça. Nous avons ici un inventaire avec quatre hôtes définis.
Un hôte peut être défini par son adresse, par son nom de domaine (donc qui peut être résolu par DNS) ou un nom fictif, ce qui est le cas ici, donc nous devons fournir à Ansible les informations nécessaires pour établir la connexion SSH (adresse, utilisateur, mot de passe, …). Ces informations sont des variables qui peuvent être définies en de multiples endroits, y compris directement dans l’inventaire, mais il existe une méthode simple et efficace pour que chaque information soit à sa place: les host_vars
et group_vars
.
Définition des host_vars
Le concept est simple: a l’exécution d’une commande ou d’un playbook, Ansible chargera automatiquement certains fichiers de variables si ils sont au bon endroit et nommés en fonction des groupes te des noms d’hôtes. Les fichiers « host_vars » doivent résider dans un répertoire host_vars
situé au même endroit que l’inventaire. Donc dans notre cas ~/ansible/host_vars/
et il devra contenir un fichier pour chaque hôte qui contiendra les variables propre à ce même hôte. Ce fichier peut être de type INI ou YAML, ici, nous utiliserons le format YAML (c’est plus lisible quand ça devient complexe).
Voici donc les quatre fichiers de variables d’hôtes:
ansible_host: 192.168.242.10
ansible_user: steve
ansible_password: jsi_password
ansible_become: true
ansible_become_method: sudo
ansible_become_password: jsi_sudo_password
ansible_host: 192.168.242.11
ansible_user: steve
ansible_password: jsi_password
ansible_become: true
ansible_become_method: sudo
ansible_become_password: jsi_sudo_password
ansible_host: 192.168.242.12
ansible_user: steve
ansible_password: jsi_password
ansible_become: true
ansible_become_method: sudo
ansible_become_password: jsi_sudo_password
ansible_host: 192.168.242.13
ansible_user: steve
ansible_password: jsi_password
ansible_become: true
ansible_become_method: sudo
ansible_become_password: jsi_sudo_password
Et voici la description des variables utilisées:
Variable | Description |
---|---|
ansible_host | l’adresse ou le nom DNS de l’hôte |
ansible_user | l’utilisateur utilisé pour établir la connexion SSH |
ansible_password | le mot de passe utilisé pour la connexion SSH |
ansible_become | l’escalation e privilège doit elle être utilisée (true/false) |
ansible_become_method | la méthode d’escalation de privilège (par défaut: sudo) |
ansible_become_password | le mot de passe pour l’escalation de privilège |
A partir de là nous devrions pouvoir gérer ces quatre hôtes à l’aide d’Ansible.
Exécution de commandes ad-hoc avec Ansible
Bien que l’automatisation de tâche soit le principal objectif d’Ansible, il est possible d’exécuter des commandes ad-hoc. C’est à dire d’exécuter des commandes Ansible à directement depuis la ligne de commande sans passer par un playbook.
Tester la connectivité Ansible
Nous l’avons déjà utilisé tout au début sans l’expliquer, Ansible dispose d’un module « ping » qui permet de tester la connectivité Ansible de l’hôte. Concrètement, ce module établi une connexion SSH vers l’hôte et teste la présence d’un interpréteur Python et retourne le résultat.
ansible -m ping <hôte|groupe>
Exemple:
steve@jsi-ubuntu ~ $ ansible -m ping jsi-debian
jsi-debian | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
steve@jsi-ubuntu ~ $
N’importe quel hôte ou groupe peut être ciblé, y compris le groupe implicite « all » qui comme son nom l’indique regroupe tous les hôtes de l’inventaire.
steve@jsi-ubuntu ~ $ ansible -m ping all
jsi-debian | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
jsi-rocky8 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/libexec/platform-python"
},
"changed": false,
"ping": "pong"
}
jsi-arch | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3.11"
},
"changed": false,
"ping": "pong"
}
jsi-ubuntu | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
steve@jsi-ubuntu ~ $
Récupérer les « ansible_facts »
Ansible ne se contente pas d’exécuter des commandes, mais génère l’exécution de commandes pour obtenir un résultat décrit dans un playbook. Ce qui veut dire que la ré-éxécution d’un même playbook n’entraîne pas forcément de changements si ce n’est pas nécessaire.
Pour ce faire Ansible a besoin de connaître l’état du système et dispose d’un module dont c’est le rôle: « gather_facts ». Nous pouvons aussi l’exécuter en mode ad-hoc. Le résultat sera l’ensemble des informations collectées par Ansible depuis l’hôte concerné.
ansible -m gather_facts <hôte|groupe>
Exemple quelque peut raccourci tellement il y a d’informations à afficher:
steve@jsi-ubuntu ~ $ ansible -m gather_facts jsi-ubuntu
jsi-ubuntu | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.168.122.175",
"192.168.242.10"
],
"ansible_all_ipv6_addresses": [
"fe80::5054:ff:fe90:5e78",
"fe80::5054:ff:fe13:6cc7"
],
"ansible_apparmor": {
"status": "enabled"
},
},
.... plusieurs dizaines de lignes coupées ....
"changed": false,
"deprecations": [],
"warnings": []
}
steve@jsi-ubuntu ~ $
Création et exécution d’un premier playbook Ansible
Les commandes ad-hoc c’est bien, mais la puissance d’Ansible réside dans la possibilité d’exécuter des chaînes de tâches par l’intermédiaire d’un playbook.
Un playbook est un document YAML qui contient au moins un « play » qui décrit une ou plusieurs tâches à effecter sur une série d’hôtes.
Pour l’exemple, nous allons ici créer un playbook qui contiendra un seul play qui lui même ne décrira qu’une seule tâche: installer le package « htop ».
Un premier playbook
---
- name: Installation de HTOP
hosts:
- jsi-ubuntu
- jsi-debian
- jsi-arch
- jsi-rocky8
tasks:
- name: Utilisation du module package pour installer HTOP
ansible.builtin.package:
name: htop
state: present
Comme vous pouvez le voir, un playbook est relativement lisible. Notez que les « — » du début de fichier sont obligatoires, c’est la syntaxe de base d’un document YAML.
Autre remarque importante, dans un fichier YAML l’indentation est extrêmement importante, et chaque niveau d’indentation doit être faire avec deux espaces (ou du moins toujours le même nombre d’espaces).
Le module « package » permet d’installer un package indépendamment du gestionnaire de package utilisé (apt, dnf ou encore pacman). Pour plus d’information sur ce module, jetez un oeil à la documentatio d’Ansible: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/package_module.html
Exécution du playbook
Une fois le playbook rédigé, il ne nous reste plus qu’à l’exécuter:
ansible-playbook <chemin/vers/le/playbook>
Par exemple:
steve@jsi-ubuntu playbooks $ ansible-playbook ./install_htop.yaml
PLAY [Installation de HTOP] ************************************************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************************************************************
ok: [jsi-arch]
ok: [jsi-debian]
ok: [jsi-ubuntu]
ok: [jsi-rocky8]
TASK [Utilisation du module package pour installer HTOP] *******************************************************************************************************
ok: [jsi-ubuntu]
changed: [jsi-debian]
changed: [jsi-arch]
changed: [jsi-rocky8]
PLAY RECAP *****************************************************************************************************************************************************
jsi-arch : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
jsi-debian : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
jsi-rocky8 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
jsi-ubuntu : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
steve@jsi-ubuntu playbooks $
Vous noterez q’ici, sur ma VM ubuntu, la tâche n’a pas été exécutée (statut: OK), tout simplement parce que HTOP y était déjà installé. Les tâches exécutées sont indiquées par le statut « changed ». LEs autres statuts étant plus relatifs à des soucis d’exécution.
Comme dit précédemment, Ansible tient compte de l’état du système et n’exécute les commande que si c’est nécessaire. Si je réexécute le même playbook, je ne devrais plus avoir aucun changement:
steve@jsi-ubuntu playbooks $ ansible-playbook ./install_htop.yaml
PLAY [Installation de HTOP] ************************************************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************************************************************
ok: [jsi-arch]
ok: [jsi-debian]
ok: [jsi-ubuntu]
ok: [jsi-rocky8]
TASK [Utilisation du module package pour installer HTOP] *******************************************************************************************************
ok: [jsi-debian]
ok: [jsi-ubuntu]
ok: [jsi-rocky8]
ok: [jsi-arch]
PLAY RECAP *****************************************************************************************************************************************************
jsi-arch : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
jsi-debian : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
jsi-rocky8 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
jsi-ubuntu : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
steve@jsi-ubuntu playbooks $
Nous voyons bien ici que tous les statuts sont OK, ce qui signifie qu’aucun changement n’a dû être effectué.
Conclusion
Nous avons maintenant une base Ansible fonctionnelle permettant de gérer une série d’hôtes et un premier playbook gérant le déploient du package « htop » sur l’ensemble de ces hôtes par un simple exécution.
Dans les prochains articles nous verrons comment organiser notre inventaire, comment utiliser l’authentification par clé pour SSH ainsi que la création de playbooks plus complexes.
Laisser un commentaire
Vous devez vous connecter pour publier un commentaire.