AnsibleVoici 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!

...

AstuceLa 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’environnement ANSIBLE_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’occurrence localhost.
  • 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 cible localhost. 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

AstuceDans 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.

 

Catégories : Formation

0 commentaire

Laisser un commentaire

Emplacement de l’avatar

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *