Pour un rendu optimal, activez JavaScript

[My Ansible Journey] Bases et premier Playbook

 ·  ☕ 11 min de lecture  ·  🩆 Jeremy

Seconde partie de la sĂ©rie d’article consacrĂ©e Ă  Ansible. L’objectif est toujours le mĂȘme, monter en puissance sur l’outil en s’appuyant sur des cas d’usages. De mĂȘme que pour la partie prĂ©requis, ce sera un mĂ©lange de bonnes pratiques provenant de l’Ă©diteur mais aussi de mon retour d’expĂ©rience.
Dans cet article, on revient sur les bases du produit ainsi que sur la rĂ©alisation d’un premier Playbook. On va commencer le dĂ©ploiement du Stack TIG (Telegraf, InfluxDB, Grafana) pour la supervision d’un environnement VMware (vCenter + ESXi), ce sera notre fil rouge :)

Pour rappel, les prĂ©requis c’est ici.

Le code des exemples qui suivent est sur GitHub

Introduction et objectifs d’Ansible

J’avais dĂ©jĂ  eu l’occasion de faire une rapide introduction Ă  Ansible dans la premiĂšre partie, ici on va approfondir en citant quelques cas d’usage, puis on couvrira principales fonctionnalitĂ©s avec une explication plus thĂ©orique.

A l’origine, le produit a Ă©tĂ© dĂ©veloppĂ© pour la gestion de la configuration de serveurs Linux. Il est sans agent, il s’appuie donc sur SSH pour l’accĂšs aux nƓuds (serveurs qui seront gĂ©rĂ©s par Ansible). Une fois connectĂ© sur les nƓuds, c’est du code Python qui est exĂ©cutĂ©. Pour moi les points forts du produit :

  • Agentless
  • Utilisation de SSH qui va permettre d’accĂ©der Ă©galement Ă  des Ă©quipements rĂ©seaux ou de sĂ©curitĂ© et ainsi Ă©largir le panel des nƓuds gĂ©rĂ©s.
  • ExĂ©cution de code Python (2 et 3), qui est prĂ©sent par dĂ©faut sur une majoritĂ© de systĂšme

On notera Ă©galement qu’il est possible de gĂ©rer des nƓuds Windows avec PowerShell mais Ă©galement n’importe quel Ă©quipement ou application que l’on peut accĂ©der via des web services (Rest API par exemple). Dans ce dernier cas, le code Python est exĂ©cutĂ© en local.
Si vous avez rĂ©alisĂ© les Ă©tapes listĂ©es dans les prĂ©requis, vous devez avoir un environnement Ansible prĂȘt pour Ă©crire vos premiers Playbook. Comme vous l’avez vu, c’est plutĂŽt simple Ă  mettre en place. Un Playbook s’exĂ©ucte un peu Ă  la maniĂšre script Python ou PowerShell, avec la commande ansible-playbook suivi du Playbook :

1
ansible-playbook playbook.yml

Cas d’usage

Un des cas d’usage le plus souvent citĂ© quand on parle d’Ansible, c’est le dĂ©ploiement et la configuration, sur un serveur Linux, d’un ensemble applicatif (par exemple serveur web + base de donnĂ©es). Du fait de l’idempotence native des modules, on peut rejouer le Playbook sur le serveur Linux, il n’y aura pas d’incidence, au contraire on corrigera une Ă©ventuelle dĂ©rive de la configuration.

En vrac, quelques cas d’usages:

  • Automatiser le dĂ©ploiement d’équipements rĂ©seaux
  • DĂ©ploiement de machines virtuelles
  • Configuration de serveurs ESXi
  • DĂ©ploiement d’un stack applicatif

Composants

  • Control Node : LĂ  oĂč on installe Ansible.
  • Managed Nodes : Equipements pilotĂ©s Ă  distance. Ils sont listĂ©s dans le fichier Inventory
  • Tower : Portail web de pilotage. AWX est l’Upstream de Tower.
  • Playbook : Description de la configuration souhaitĂ©e
  • Modules : Code qui est exĂ©cutĂ© sur les nƓuds

On reviendra sur les Playbook et Modules un peu plus loin dans l’article.

Fichiers nécessaires

Pour commencer on va parler des fichiers les plus importants, ceux qui sont suffisants pour faire un premier Playbook qui sera fonctionnel.

Playbook

Dans le Playbook on dĂ©crit la configuration que l’on souhaite retrouver appliquĂ©e sur les nƓuds. A la diffĂ©rence d’un script, on dĂ©crit des Ă©tats que l’on souhaite obtenir, plutĂŽt que des tĂąches prĂ©cises Ă  exĂ©cuter.
Les Playbook sont Ă©crits en YAML, un langage proche du JSON. Explication sur la syntaxe ici : https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html

Inventaire

C’est dans le fichier d’inventaire que l’on dĂ©clare les nƓuds qui pourront ĂȘtre accĂ©dĂ©s avec le Playbook. Il en existe un par dĂ©faut : /etc/ansible/hosts.

Le fichier inventaire peut ĂȘtre sous deux formats :

  • Fichier .ini
  • Fichier .yml

Format .ini

Vous retrouverez plus souvent l’inventaire sous le format .ini, mais le second n’est pas Ă  ignorer car suivant l’inventaire souhaitĂ©, il peut apporter davantage de souplesse.

Dans notre exemple oĂč l’on va dĂ©ployer le stack TIG, voici Ă  quoi pourrait ressembler le fichier inventaire en .ini :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[tig]
influxdb ansible_host=192.168.1.10
telegraf ansible_host=192.168.1.11
grafana ansible_host=192.168.1.12

[webservers]
nginx01.domain.local
nginx02.domain.local

dns01.domain.local

Si on décompose le fichier :

  • [tig] est le nom du groupe et qui intĂ©gĂšre les 3 lignes suivantes. Le groupe peut-ĂȘtre utilisĂ© dans le Playbook.
  • influxdb est le nom que l’on souhaite donner au host. Il peut Ă©galement ĂȘtre utilisĂ© dans le Playbook.
  • ansible_host est une variable associĂ©e au host, chaque variable est sĂ©parĂ©e par un espace, puis on indique sa valeur prĂ©cĂ©dĂ© de =. Dans ce cas c’est l’ip du host.

J’ai ajoutĂ© un second groupe (qui ne servira pas dans notre exemple) pour montrer que l’on peut en crĂ©er autant que l’on souhaite. Il n’est pas indispensable de prĂ©ciser l’ip du host si on une rĂ©solution de nom fonctionnelle.

Et enfin, un host qui n’appartient Ă  aucun groupe, lĂ  encore il ne sera utilisĂ© dans l’exemple

Pour la dĂ©monstration, on va installer les 3 produits sur le mĂȘme serveur, donc on ne mettra qu’une seule ligne.

Format YAML

Voici l’Ă©quivalent en YAML :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
all:
  hosts:
  children:
    tig:
      hosts:
        influxdb:
            ansible_host: 192.168.1.10
        telegraf:
            ansible_host: 192.168.1.11
        grafana:
            ansible_host: 192.168.1.12
    webservers:
      hosts:
        nginx01.domain.local:
        nginx02.domain.local:
    dns01.domain.local:

Je pense que c’est suffisament clair pour comprendre la logique de ce format. A noter que les mots clĂ©s “all”, “hosts” et “children” sont rĂ©servĂ©s. Point important, “all” est implicitement prĂ©sent dans le fichier inventaire.ini, il correspond Ă  l’ensemble des hosts prĂ©sent dans l’inventaire.

Inventaire dynamique

Je ne vais pas rentrer dans les dĂ©tails sur les inventaires dynamiques dans cet article, car on n’en aura pas besoin tout de suite. Mais sachez que c’est trĂšs pratique qu’un inventaire puisse ĂȘtre gĂ©rĂ© depuis une source de donnĂ©es externe. Deux exemples :

  • Un serveur vCenter, permet d’avoir une liste VM Ă  jour dans l’inventaire
  • Un IPAM qui sert de Source of Truth, comme l’excellent NetBox

Je vous encourage Ă  vous rendre sur la documentation officielle pour approfondir la gestion des inventaires avec Ansible : https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html

Mode interactif

La fonction premiĂšre d’Ansible est bien d’exĂ©cuter des Playbook, mais Ă  des fins de test/debug, il est possible de l’utiliser en mode interactif. Par exemple si on souhaite savoir si un nƓud est accessible :

1
ansible grafana --inventoy inventory.ini --user jlg -m ping

Ici on se connecte Ă  l’hĂŽte grafana qui est dĂ©fini dans le fichier d’inventaire inventory.ini avec l’utilisateur jlg. Sur cet hĂŽte on exĂ©cute le module ping.

RĂ©sultat attendu :

1
2
3
4
5
6
7
grafana | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

On retrouve la documentation de chaque module via la commande :

1
ansible-doc ping

Ou directement sur le site d’Ansible : https://docs.ansible.com/ansible/latest/modules/modules_by_category.html

Un autre exemple est l’utilisation du module command qui est celui par dĂ©faut (on a donc pas besoin de le prĂ©ciser). Ce module attend un argument qui correspond Ă  la commande Ă  exĂ©cuter sur l’hĂŽte. Exemple :

1
ansible grafana --inventory inventory.ini --user jlg -a hostname

Comme indiquĂ©, on ne prĂ©cise pas le nom du module command car il est sĂ©lectionnĂ© par dĂ©faut, cependant on prĂ©cise l’argument avec -a, ici il va donc exĂ©cuter la commande hostname et nous renvoyer le rĂ©sultat :

1
2
grafana | CHANGED | rc=0 >>
tig

Enfin, la fonctionnalitĂ© la plus utile en mode interactif est la dĂ©couverte et l’affichage des Facts. Je reviendrai plus en dĂ©tail sur les Facts et variables avec Ansible dans le prochain article. Ce qu’il faut retenir ici, c’est la capacitĂ© qu’a Ansible Ă  dĂ©couvrir des informations sur le nƓud gĂ©rĂ© et Ă  les enregistrer dans des variables. Par exemple :

  • Informations Hardware, CPU, mĂ©moire, disques, rĂ©seau etc

  • Informations systĂšmes comme les adresses IP, nom et version de l’OS
  • Et plein d’autres choses

Le mieux étant encore de le tester, pour cela on exécute le module setup, sans argument :

1
ansible grafana --inventoy inventory.ini --user jlg -m setup

Le résultat est trÚs long, voici une partie :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
grafana | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.x.x"
        ],
        "ansible_all_ipv6_addresses": [
            "fe80::20c:xxxx:xxxx:xxxx"
        ],
        "ansible_apparmor": {
            "status": "enabled"
        },
        "ansible_architecture": "x86_64",
        "ansible_bios_date": "02/27/2020",
        "ansible_bios_version": "6.00",
        "ansible_cmdline": {
            "BOOT_IMAGE": "/boot/vmlinuz-5.4.0-42-generic",
            "maybe-ubiquity": true,
            "ro": true,
            "root": "UUID=4ac4c187-b2ed-49ff-aa93-5b4ac3f13156b"
        },

On peut Ă©galement appliquer un filtre afin de limiter l’affichage, ici un exemple pour affichier les points de montage :

1
ansible grafana --inventory inventory.ini --user jlg -m setup -a 'filter=ansible_mounts'

Toutes ces informations vont pouvoir ĂȘtre rĂ©utilisĂ©es plus tard dans le Playbook, par exemple si vous souhaitez faire un contrĂŽle sur une version d’OS avant d’exĂ©cuter un tĂąche.

Composition d’un Playbook

On arrive maintenant au Playbook, on va rester simple ici aussi, le but Ă©tant d’introduire le concept.

Voici le Playbook que l’on va exĂ©cuter :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
---
- name: Prerequisites
  hosts: tig
  gather_facts: yes
  remote_user: jlg
  become: true
  tasks:
    - name: Set timezone to Europe/Paris
      timezone:
        name: Europe/Paris
    
    - name: Local packages should be up-to-date
      apt:
        pkg: "*"
        state: latest

    - name: Add InfluxData Apt key
      apt_key:
        url: https://repos.influxdata.com/influxdb.key
        state: present

    - name: Add stable InfluxData Apt repository
      apt_repository:
        repo: "deb https://repos.influxdata.com/{{ ansible_facts['distribution']|lower }} {{ ansible_facts['distribution_release'] }} stable"
        state: present

- name: InfluxDB
  hosts: tig
  remote_user: jlg
  become: true
  gather_facts: false
  tasks:
    - name: Install InfluxDB
      apt:
        update_cache: yes
        pkg: influxdb

    - name: Service started and enabled
      service:
        name: influxdb
        enabled: yes
        state: started

MĂȘme si un Playbook a le mĂ©rite d’ĂȘtre comprĂ©hensible, il mĂ©rite quelques explications 😊

  1. Configuration de la Timezone
  2. Mise Ă  jour de tous les paquets
  3. Ajout du Repository apt d’InfluxData (Ă©diteur de de InfluxDB et Telegraf) avec la clĂ© associĂ©e
    • Ici on a exemple de l’utilisation des Facts vu plus haut avec la rĂ©cupĂ©ration de l’OS (ubuntu) et de la version (focal)
  4. Si InfluxDB n’est pas prĂ©sent, on l’installe.
    • Ici on prĂ©cise present et non pas latest, on veut que InfluxDB soit installĂ©, si c’est dĂ©jĂ  le cas, on ne souhaite pas le mettre Ă  jour.
  5. Enfin on dĂ©marre le service et on l’active (dĂ©marrage automatique)

Play

Un Playbook est divisé en plusieurs Play, ici on en retrouve deux, le premier commence dÚs le début avec :

1
- name: prerequisites

Le fait qu’il commence par un tiret indique que c’est une liste, et donc qu’il est proabable qu’il y en est plusieurs.

Un Play correspond Ă  une liste de tĂąches qui seront exĂ©cutĂ©es suivant les informations dans l’en-tĂȘte. Ces tĂąches seront exĂ©cutĂ©es dans l’odre, sur chacun des nƓuds. Cependant le traitement se fait en parallĂšle sur les nƓuds (si on a prĂ©cisĂ© un groupe au lieu d’un hĂŽte simple).

En-tĂȘte

Une en-tĂȘte par Play, la premiĂšre :

1
2
3
4
5
- name: Prerequisites
  hosts: tig
  gather_facts: yes
  remote_user: jlg
  become: yes

On retrouve des informations qu’on avait dĂ©jĂ  pu prĂ©ciser dans le mode interactif comme le host (ou groupe de hosts, on aurait pu mettre all ou grafana). On retrouve aussi l’utilisateur pour se connecter exĂ©ucter les actions. On y prĂ©cise quelques informations supplĂ©mentaires comme le fait qu’on rĂ©cupĂšre les Facts, ainsi que le besoins d’obtenir une Ă©lĂ©vation de privilĂšge (become).

Task

C’est avec les Tasks qu’on indique quel est l’Ă©tat souhaitĂ© sur le nƓud. Si on regarde la premiĂšre Task :

1
2
3
    - name: Set timezone to Europe/Paris
      timezone:
        name: Europe/Paris

Le nom indiquĂ© est optionnel et servira notamment pour obtenir un affichage clair, mais aussi de marqueur si on souhaite relancer le Playbook depuis une tĂąche prĂ©cise. Ensuite vient l’appel du module timezone auquel on passe un argument correspondant Ă  la Timezone.

L’objectif de cette Task est donc de configurer la Timezone indiquĂ© en argument, sur le host. Si la configuration est dĂ©jĂ  correct, il remontera un statut Ok, si il doit la modifier, il prĂ©cisera Changed. On s’attend donc Ă  ce que si on relance le Playbook, le statut sera Ok

Module

Enfin le module est le composant qui renferme le code Python qui sera exĂ©cutĂ© sur le nƓud, c’est lui qui doit ĂȘtre idempotent et donc s’assurer de ne faire des modifications que si nĂ©cessaire. Les modules sont accessibles directement sur Github : https://github.com/ansible/ansible/tree/stable-2.9/lib/ansible/modules.

Exécution du Playbook

Enfin, on peut exécuter ce premier Playbook avec la commande suivante :

1
ansible-playbook playbook.yml --inventory inventory.ini -K

Cette fois on ne prĂ©cise pas d’utilisateur ni de nƓud sur lequel exĂ©cuter le Playbook, ces informations sont indiquĂ©es dans l’en-tĂȘte des Plays. On a cependant ajoutĂ© l’option -K afin que le prompt nous sollicite pour saisir le mot de passe permettant l’Ă©levation de privilĂšge (sudo par dĂ©faut).

Je vous encourage à le relancer plusieurs fois afin de voir la différence.

Cya!

Partagez