Voici le neuvième article de la formation Ansible. Dans mon précédent article, je vous ai parlé d’idempotence, une caractéristique centrale d’Ansible et qui constitue un de ses atouts majeurs. Aujourd’hui nous allons nous atteler à la rédaction de nos premiers playbooks. Rien de bien spectaculaire ici, vu qu’il s’agit avant tout de découvrir le concept en douceur.
Hello Ansible ! Mon premier playbook
Les playbooks sont le point de départ d’un projet Ansible. Dans le cas de figure le plus simple, ils contiennent un ensemble ordonné de tâches (tasks) qui décrivent comment atteindre les objectifs de configuration des Target Hosts. Là encore, c’est un petit peu abstrait, et vous allez mieux comprendre en mettant la main à la pâte.
Placez-vous dans le répertoire du huitième atelier pratique :
$ cd ~/formation-ansible/atelier-08
Voici les quatre machines virtuelles de cet atelier :
Machine virtuelle | Adresse IP |
---|---|
ansible |
10.23.45.10 |
rocky |
10.23.45.20 |
debian |
10.23.45.30 |
suse |
10.23.45.40 |
Démarrez les VM :
$ vagrant up
Connectez-vous au Control Host :
$ vagrant ssh ansible
L’environnement de cet atelier est préconfiguré et prêt à l’emploi :
- Ansible est installé sur le Control Host.
- Le fichier
/etc/hosts
du Control Host est correctement renseigné. - L’authentification par clé SSH est établie sur les trois Target Hosts.
- Le répertoire du projet existe et contient une configuration de base et un inventaire.
- Direnv est installé sans toutefois être activé pour le projet.
Rendez-vous dans le répertoire du projet :
$ cd ansible/projets/ema/ $ ls ansible.cfg inventory
Ansible n’est pas un langage de programmation au sens strict du terme. En revanche, c’est bien quelque chose avec un vocabulaire et une syntaxe qui nous permet d’automatiser notre travail. Nous allons donc respecter la tradition et rédiger un premier playbook « Hello world« .
Les playbooks sont écrits en YAML avec une extension de fichier .yml
ou .yaml
par convention. Créez un fichier hello-ansible.yml
à la racine du projet et éditez-le comme ceci :
--- # hello-ansible.yml
- hosts: localhost
tasks:
- debug:
msg: Hello Ansible!
...
La syntaxe de YAML est assez pointilleuse pour les espaces et les alignements, et vous pouvez vite vous retrouver à vous arracher les cheveux face à un dysfonctionnement inexplicable. Pour ma part, j’aime bien me servir de l’outil yamllint
, un simple vérificateur de syntaxe en ligne de commande. Installez-le avec le gestionnaire de paquets de la distribution et invoquez-le en fournissant simplement le nom de votre fichier YAML en argument. Si yamllint
ne vous dit rien, c’est que c’est bon.
Ansible nous laisse pas mal de libertés pour organiser nos projets. En revanche, une série de bonnes pratiques a fini par s’imposer pour éviter que les projets ne finissent en pagaille. Une de ces pratiques consiste à réunir les playbooks dans un répertoire playbooks
à la racine du projet. Nous allons donc créer ce répertoire pour y ranger notre premier playbook :
$ mkdir -v playbooks mkdir: created directory 'playbooks' $ mv -v hello-ansible.yml playbooks/ renamed 'hello-ansible.yml' -> 'playbooks/hello-ansible.yml'
Et c’est là où les choses se compliquent un peu. En effet, tant que vous vous trouvez à la racine du projet, Ansible identifie correctement la configuration et l’inventaire :
$ ansible --version | head -n 2 ansible [core 2.14.14] config file = /home/vagrant/ansible/projets/ema/ansible.cfg
Or, si vous vous placez dans le répertoire playbooks
pour éditer ou lancer vos playbooks, Ansible est à l’ouest pour la configuration du projet :
$ cd playbooks/ $ pwd /home/vagrant/ansible/projets/ema/playbooks $ ansible --version | head -n 2 ansible [core 2.14.14] config file = /etc/ansible/ansible.cfg
Ici, vous avec grosso modo deux solutions :
- Vous restez cantonné à la racine du projet pour le gérer. Vous pouvez toujours lancer vos playbooks en utilisant le chemin relatif
playbooks/playbook.yml
, mais ce n’est pas très commode. C’est un peu ce qui se passe dans la plupart des tutos que j’ai pu glaner sur le web. - Vous utilisez
direnv
avec la variable d’environnementANSIBLE_CONFIG
. C’est un petit investissement qui en vaut la peine, étant donné que vous pouvez naviguer librement dans l’arborescence du projet sans vous soucier de la prise en charge correcte de la configuration.
Nous allons opter pour la deuxième solution :
$ cd .. $ pwd /home/vagrant/ansible/projets/ema $ echo 'export ANSIBLE_CONFIG=$(expand_path ansible.cfg)' > .envrc direnv: error /home/vagrant/ansible/projets/ema/.envrc is blocked. Run `direnv allow` to approve its content $ direnv allow direnv: loading ~/ansible/projets/ema/.envrc direnv: export +ANSIBLE_CONFIG
À partir de là, la configuration d’Ansible reste valable indépendamment de l’endroit où je me trouve dans l’arborescence du projet :
$ pwd /home/vagrant/ansible/projets/ema $ ansible --version | head -n 2 ansible [core 2.14.14] config file = /home/vagrant/ansible/projets/ema/ansible.cfg $ cd playbooks/ $ pwd /home/vagrant/ansible/projets/ema/playbooks $ ansible --version | head -n 2 ansible [core 2.14.14] config file = /home/vagrant/ansible/projets/ema/ansible.cfg
Maintenant que ce problème est réglé, regardons un peu le contenu de ce premier playbook :
--- # hello-ansible.yml
- hosts: localhost
tasks:
- debug:
msg: Hello Ansible!
...
- Les trois signes
---
sont un marqueur YAML qui indique le début du document. - De même, les trois signes
...
indiquent la fin d’un document YAML. Ils ne sont pas obligatoires, mais j’aime bien faire les choses proprement. - Le mot-clé
hosts
indique les Target Hosts auxquels je veux m’adresser. - Le mot-clé
tasks
nous indique une série de tâches à accomplir pour amener les Target Hosts vers un état souhaité. - Pour l’instant, l’appel du module
debug
constitue notre seule et unique tâche. - Le paramètre
msg
permet de définir le message à afficher. - Du point de vue de YAML, notre playbook est une liste avec un seul élément. Ne vous cassez pas trop la tête là-dessus pour l’instant.
Après toutes ces palabres, nous allons enfin pouvoir lancer notre playbook :
$ ansible-playbook hello-ansible.yml PLAY [localhost] ********************************************************************* TASK [Gathering Facts] *************************************************************** ok: [localhost] TASK [debug] ************************************************************************* ok: [localhost] => { "msg": "Hello Ansible!" } PLAY RECAP *************************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
- Le résultat est initié par
PLAY
et la liste des Target Hosts, en l’occurrencelocalhost
. - Ensuite nous avons la liste des tâches avec une description succincte et le résultat correspondant.
- Notez que la tâche
Gathering Facts
ne figure pas dans notre playbook. Le comportement par défaut d’Ansible consiste à commencer un play en rassemblant tout un tas d’informations sur les Target Hosts concernés. - La dernière ligne nous offre un récapitulatif. Deux tâches ont été effectuées avec succès (
ok=2
) sur la ciblelocalhost
. Il n’y a pas eu de changement (changed=0
). La cible était bien joignable (unreachable=0
) et aucune tâche n’a échoué (failed=0
).
Maintenant que nous connaissons le fonctionnement de base d’un playbook, nous allons nous adresser à tous les hôtes de notre labo. Éditez un deuxième playbook hello-all.yml
comme ceci :
--- # hello-all.yml
- hosts: all
tasks:
- debug:
msg: Hello Ansible!
...
Lancez ce playbook :
$ ansible-playbook hello-all.yml PLAY [all] *************************************************************************** TASK [Gathering Facts] *************************************************************** ok: [debian] ok: [suse] ok: [rocky] TASK [debug] ************************************************************************* ok: [rocky] => { "msg": "Hello Ansible!" } ok: [debian] => { "msg": "Hello Ansible!" } ok: [suse] => { "msg": "Hello Ansible!" } PLAY RECAP *************************************************************************** debian : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 rocky : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 suse : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Essayons de voir ce qui se passe lorsqu’un des systèmes cible devient injoignable :
$ exit $ vagrant halt suse $ vagrant ssh ansible $ cd ansible/projets/ema/playbooks/
Voilà ce que ça donne :
$ ansible-playbook hello-all.yml PLAY [all] *************************************************************************** TASK [Gathering Facts] *************************************************************** ok: [debian] ok: [rocky] fatal: [suse]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host suse port 22: Connection timed out", "unreachable": true} TASK [debug] ************************************************************************* ok: [rocky] => { "msg": "Hello Ansible!" } ok: [debian] => { "msg": "Hello Ansible!" } PLAY RECAP *************************************************************************** debian : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 rocky : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 suse : ok=0 changed=0 unreachable=1 failed=0 skipped=0 rescued=0 ignored=0
- Une fois qu’Ansible s’est rendu compte que l’hôte
suse
n’était pas joignable, il l’a éliminé du jeu d’emblée. - Les hôtes ne sont pas gérés dans un ordre particulier, ce qui est dû à la gestion parallèle des tâches dans la configuration par défaut d’Ansible.
- Le récapitulatif final est plutôt évident.
Relancez l’hôte suse
:
$ exit $ vagrant up suse $ vagrant ssh ansible $ cd ansible/projets/ema/ $ ansible all -m ping
L’inventaire cite les Target Hosts dans un certain ordre :
$ head -n 4 inventory [testing] rocky debian suse
Dans la configuration par défaut, Ansible lance cinq forks en parallèle pour accélérer les opérations. Si votre réseau est performant et que vous disposez d’un nombre important de Target Hosts, vous pouvez augmenter le nombre de forks grâce au paramètre -f
(--forks
).
Dans notre environnement labo, nous ne verrons pas vraiment la différence. En revanche, nous pouvons nous amuser à limiter l’exécution à un seul fork, ce qui forcera Ansible à traiter une cible après l’autre :
$ cd playbooks/ $ ansible all -m ping -f 1 $ ansible-playbook hello-all.yml -f 1
Lire la suite : Un serveur web simple
La rédaction de cette documentation demande du temps et des quantités significatives de café espresso. Vous appréciez ce blog ? Offrez un café au rédacteur en cliquant sur la tasse.
2 commentaires
Gilles · 11 octobre 2024 à 14 h 53 min
Hello,
Apparemment il manque un « . » à la fin du fichier hello-all.yaml 🙂
kikinovak · 11 octobre 2024 à 15 h 02 min
Corrigé, merci !