Changeons de sujet par rapport Ă  mes prĂ©cĂ©dents articles. Travaillant dans le domaine de l’intĂ©gration et dĂ©ploiement continu (CICD), j’ai eu envie de faire dĂ©couvrir des outils que j’utilise au quotidien. Le premier est Ansible, un outil de dĂ©ploiement que j’affectionne particulièrement.

Ce premier article sur Ansible vous présentera son histoire, ses concepts, ses principes, et les commandes.

ansibl

Introduction

Ansible est un outil d’automatisation et de dĂ©ploiement crĂ©Ă© en fĂ©vrier 2012 par Michael DeHaan. Son nom s’inspire d’un dispositif prĂ©sentĂ© dans le roman Le Monde de Rocannon Ă©crit par Urusula K. Le Guin dans lequel l’ansible est un moyen de communication plus rapide que la lumière. L’entreprise AnsibleWorks Inc. crĂ©Ă©e pour apporter un support commercial Ă  l’outil, a Ă©tĂ© rachetĂ©e en 2015 par Red Hat. Depuis, Ansible est intĂ©grĂ© Ă  l’offre de l’Ă©diteur spĂ©cialisĂ© dans le logiciel libre.

DĂ©veloppĂ© principalement en Python, il est fourni sous licence GNU General Public Licence 3 et s’installe nativement sur les principales distributions Linux du marchĂ© telles que Fedora, RHEL, CentOS, Debian, Ubuntu, etc.

Ansible travaille majoritairement avec le monde Linux et d’autres Unix-Like tels que les BSD et MacOS, mais aussi avec Microsoft Windows.

Les concepts

L’un des points clĂ©s d’Ansible est que son architecture repose sur l’absence d’agent local sur les cibles. C’est très simple : une machine avec Python installĂ© dessus et pouvant se connecter en SSH sur les cibles pourra faire office de contrĂ´leur. Et voilĂ  !

L’architecture dĂ©centralisĂ©e d’Ansible fait qu’il ne nĂ©cessite aucune machine dĂ©diĂ©e pour faire un dĂ©ploiement. Si le code est versionnĂ© par un gestionnaire de sources tel que Git, celui-ci pourra ĂŞtre exĂ©cutĂ© depuis n’importe quel machine ayant la possibilitĂ© de se connecter en SSH aux noeuds. Ansible dispose de son propre langage dĂ©claratif pour Ă©crire les scripts de dĂ©ploiement.

Un des dĂ©fauts de cette architecture cependant est qu’Ansible ouvre et ferme une connexion SSH Ă  chaque action. Par consĂ©quent, il peut devenir assez lent quand on gère un parc consĂ©quent de machines. A ce moment-lĂ , sĂ©parer les exĂ©cutions ou utiliser des plugins d’optimisation peut avoir un intĂ©rĂŞt. Dans la section “plugins” on parlera de Mitogen qui permet d’accĂ©lĂ©rer les perfs de ce dernier.

Les principes clĂ©s d’Ansible sont :

  • Conception minimaliste : les systèmes gĂ©rĂ©s ne requièrent aucun composant supplĂ©mentaire. Seul le contrĂ´leur peut avoir besoin de modules complĂ©mentaires selon les besoins. (exemple : modules python-docker pour piloter des containers de ce type)
  • HomogĂ©nĂ©itĂ© : un dĂ©ploiement Ansible permet de dĂ©ployer des environnements homogènes et cohĂ©rents.
  • SecuritĂ© : Ansible ne nĂ©cessite pas d’agent sur les noeuds, il n’a besoin que d’une connexion SSH et Python installĂ© dessus.
  • FiabilitĂ© : Ansible repose sur le principe d'indempotence et l’Ă©criture des playbooks se doit d’appliquer cette règle pour garantir que l’exĂ©cution sur un noeud aura systĂ©matiquement le mĂŞme rĂ©sultat.
  • Apprentissage rapide : Les playbooks Ansible sont Ă©crits en YAML et en templates Jinja, des syntaxes strictes mais simples Ă  maĂ®triser et faciles Ă  lire mĂŞme pour un non initiĂ©.

Voyons plus en détails les concepts et mots clés qui ont été balancés comme ça.

Le contrĂ´leur

Le contrĂ´leur est la machine qui lance un dĂ©ploiement Ansible. Il peut s’agir de n’importe quel serveur, PC, etc, sur lequel Ansible est installĂ© et qui invoque les commandes /usr/bin/ansible ou /usr/bin/ansible-playbook. On peut avoir plusieurs contrĂ´leurs.

Noeuds managés

Il s’agit des machines qui seront ciblĂ©es par le dĂ©ploiement Ansible, elles sont dĂ©finies dans l’inventaire qu’il utilise. On parle principalement de “hosts”.

L’inventaire

L’inventaire est une liste de hosts, aussi appelĂ© “hostfiles”. Il peut s’agir d’une simple liste d’adresses IP ou de noms de machines, mais aussi d’un fichier plus hiĂ©rarchisĂ© avec des groupes, des dĂ©pendances, ou encore des variables. On le dĂ©taillera plus loin dans la section “Construire son inventaire”.

Les Modules

Les Modules sont le code qu’Ansible exĂ©cute. Un module est un outil dont le but est de remplir une action particulière en prenant des paramètres d’entrĂ©es qui peut ensuite retourner un rĂ©sultat. Les modules d’Ansible vont du simple ping Ă  la gestion d’interfaces rĂ©seau, de bases de donnĂ©es, de fichiers, ou encore de commandes système.

Et comme si cela ne suffisait pas, en plus des modules officiels, il est possible d’Ă©tendre les possibilitĂ©s avec des modules communautaires.

Un module peut être invoqué unitairement via la commande ansible ou être exploité dans un ensemble de tâches écrites dans un playbook exécuté par ansible-playbook.

Les tâches

Une tâche est une unitĂ© d’action dans Ansible. Elle appelle un module et peut ĂŞtre soit lancĂ©e via un Playbook, soit via la ligne de commande directement.

Les Playbooks

Les Playbooks sont un ensemble de tâches ordonnĂ©es qui seront exĂ©cutĂ©es dans l’ordre du script. Ils peuvent Ă©galement contenir des variables en plus des tâches. Ils sont Ă©crits en YAML et sont faciles Ă  lire, Ă©crire et comprendre. Les playbooks sont lancĂ©s par la commande ansible-playbook.

Les rĂ´les

Les rĂ´les sont un ensemble de diffĂ©rents fichiers contenant des tâches, mais aussi des variables, des templates Ă  dĂ©ployer, des fichiers, des dĂ©clencheurs, etc. Un rĂ´le est un moyen d’Ă©crire un code rĂ©utilisable et pouvant ĂŞtre contextualisĂ© selon l’inventaire utilisĂ©.

DĂ©marrage rapide

Nous allons rapidement installer Ansible sur notre contrôleur et exécuter notre première commande ad-hoc.

Prérequis

Les prĂ©requis d’Ansible sont simples, mais indispensables.

Sur le contrĂ´leur :

  • OS basĂ© sur Linux (REHL, Debian, CentOS, Fedora…) ou Unix-like (BSD, MacOS…)
    • A ce jour, Windows n’est pas officiellement supportĂ© comme noeud de contrĂ´le.
  • Python 2.7 ou Python 3.5+
  • Une certaine proximitĂ© avec les hosts contrĂ´lĂ©s permet d’amĂ©liorer les performances. S’il est parfaitement possible d’attaquer des serveurs Cloud depuis son PC avec Ansible sur sa connexion domestique, un contrĂ´leur dans le mĂŞme rĂ©seau sera plus efficace.
  • Certains modules peuvent requĂ©rir un complĂ©ment pour fonctionner. Ceci doit ĂŞtre indiquĂ© dans leur documentation.

Sur les cibles :

  • PossibilitĂ© de se connecter dessus en SSH. Ansible utilise par dĂ©faut SFTP pour transfĂ©rer ses scripts, mais il peut ĂŞtre configurĂ© pour SCP si besoin.
  • Python 2.6+ ou Python 3.5+
  • Si SELinux est activĂ©, le package libselinux-python doit ĂŞtre prĂ©sent sur le noeud. Il est parfaitement possible de l’installer via Ansible avant d’utiliser des modules en ayant besoin (comme les modules de copie ou de template notamment).
  • Par dĂ©faut, Ansible utilise l’interprĂ©teur Python /usr/bin/python, mais certains peuvent avoir python3 directement. Il est possible de changer ça dans la config sur le contrĂ´leur.

Installer Ansible

Pour ma part, étant sous Fedora, Ansible est disponible directement dans les dépôts de la distribution.

dnf install ansible

Autrement vous pouvez l’installer :

  • Par le gestionnaire de paquets de votre distribution
  • Via l’utilitaire pip de Python
  • Via les sources

Voir les méthodes possibles sur la documentaire officiel.

Configurer Ansible

La configuration d’Ansible se fait au moyen d’un fichier ansible.cfg dont le chargement est dĂ©fini par un ordre de prioritĂ© :

  1. variable d’environnement ANSIBLE_CONFIG avec l’emplacement du fichier
  2. ansible.cfg dans le répertoire courant où est exécuté la commande ansible
  3. ~/.ansible.cfg depuis le répertoire home
  4. /etc/ansible/ansible.cfg

Le contenu du fichier de configuration peut aussi ĂŞtre surchargĂ© directement en variables d’environnement.

Par exemple l’emplacement par dĂ©faut de l’inventaire /etc/ansible/hosts peut ĂŞtre changĂ© en modifiant le paramètre inventory dans le fichier ansible.cfg, mais aussi surchargĂ© via la variable d’environnement ANSIBLE_INVENTORY sur son profil.

Pour ma part, j’ai pour habitude de mettre un fichier ansible.cfg avec le code de mes playbooks pour ĂŞtre sĂ»r d’avoir les mĂŞmes pramètres selon l’endroit depuis lequel ils sont exĂ©cutĂ©s. J’y mets Ă  minima cette valeur pour avoir un rĂ©capitulatif Ă  la fin de la durĂ©e des tâches :

[defaults]
callback_whitelist = profile_tasks

Ce qui donne à la fin le résultat suivant par exemple :

Tuesday 03 March 2020  18:19:54 +0100 (0:00:01.062)       0:01:00.064 *********
===============================================================================
Install packages-------------------------------------------------------- 37.30s
Gathering Facts --------------------------------------------------------- 9.44s

Lancer une première commande

Tout d’abord, on va crĂ©er un inventaire.

Dans un emplacement de votre choix, créez un fichier nommé par exemple inventory. Mettez dedans des machines distances, IP ou FQDN, peu importe.

Exemple :

192.168.1.20
192.168.1.22

Assurez-vous que vous pouvez bien vous connecter en SSH sur ces machines, de prĂ©fĂ©rence avec un Ă©change de clĂ©s permettant d’Ă©viter la saisie du mot de passe.

Lançons ensuite un simple ping :

ansible -i inventory all -m ping

Explication de la commande :

  • ansible est la commande ad hoc permettant de faire une exĂ©cution unitaire d’une tâche
  • L’argument -i inventory lui indique de charger le fichier d’inventaire que nous avons prĂ©cĂ©demment crĂ©Ă©
  • all lui demande de prendre tous les noeuds de l’inventaire
  • -m ping lui dit de lancer le module “ping”

RĂ©sultat :

192.168.1.20 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
192.168.1.22 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Le ping Ansible est très pratique car avec cette rapide commande vous pouvez valider que :

  • Votre inventaire fonctionne
  • Votre connexion SSH fonctionne
  • L’exĂ©cution d’un script par Ansible fonctionne

Soit … Le nĂ©cessaire :)

Comprendre l’Ă©lĂ©vation de privilège

Allons un peu plus loin pour lui faire exĂ©cuter une commande permettant de comprendre l’Ă©lĂ©vation de privilège.

# je suis connecté sur mon contrôleur avec user1
[user1@localhost ~]$ ansible -i inventory all -m command -a "whoami"

De la mĂŞme manière que le ping, je demande Ă  exĂ©cuter le module command en lui donnant l’argument -a pour qu’il lance la commande whoami afin de savoir avec quel utilisateur Ansible s’est connectĂ©.

192.168.1.20 | CHANGED | rc=0 >>
user1
192.168.1.22 | CHANGED | rc=0 >>
user1

Le problème, c’est que mon profil user1 est vite limitĂ© car il ne pourra pas faire d’action administrative de lui-mĂŞme… Cependant, il dispose d’un sudoers lui permettant de passer avec le compte root pour faire des actions spĂ©ciales. Cela se traduit dans Ansible par l’argument become et become-user.

[user1@localhost ~]$ ansible -i inventory all -m command -a "whoami" --become
192.168.1.20 | CHANGED | rc=0 >>
root
192.168.1.22 | CHANGED | rc=0 >>
root

Par défaut, --become fait un sudo root. Mais on peut lui spécifier un utilisateur précis.

[user1@localhost ~]$ ansible -i inventory all -m command -a "whoami" --become --become-user jeanmichel
192.168.1.20 | CHANGED | rc=0 >>
jeanmichel
192.168.1.22 | CHANGED | rc=0 >>
jeanmichel

Les commandes Ansible

Nous avons vu la commande ad hoc ansible et parlé rapidement de ansible-playbook, mais ce ne sont pas les seules. Pour les détails sur les arguments, suivez le lien ci après.

ansible

Commande de base permettant d’exĂ©cuter un module sur un ensemble de noeuds d’un inventaire.

Exemple pour redémarrer le service HTTPD des serveurs de notre inventaire membres du groupe webservers.

ansible -i inventory webservers -m service -a "name=httpd state=restarted"

ansible-config

La commande ansible-config permet par exemple de consulter le contenu du fichier de configuration actuellement utilisé par Ansible. Elle peut aussi générer un fichier de config en concaténant les différentes disponibles.

ansible-console

Un outil très pratique pour administrer rapidement un ensemble de noeuds ! A la même manière que la commande ad hoc ansible, la console permet cependant de rester en mode intéractif et diffuse les appels sur tous les noeuds concernés.

Exemple pour lancer une session interactive sur tous les Webservers :

ansible-console -i inventory webservers

ansible-doc

Permet de retourner la documentation d’un module ou d’un plugin et gĂ©nère directement un code exemple pour l’utiliser.

Exemple pour le module yum

# en mode info sur le module

ansible-doc yum
> YUM    (/usr/lib/python3.7/site-packages/ansible/modules/packaging/os/yum.py)

        Installs, upgrade, downgrades, removes, and lists packages and groups with the `yum' package manager. This module only works on Python 2. If you require Python 3 support see the
        [dnf] module.

  * This module is maintained by The Ansible Core Team
  * note: This module has a corresponding action plugin.

OPTIONS (= is mandatory):

- allow_downgrade
        Specify if the named package and version is allowed to downgrade a maybe already installed higher version of that package. Note that setting allow_downgrade=True can make this module
        behave in a non-idempotent way. The task could end up with a set of packages that does not match the complete list of specified packages to install (because dependencies between the
        downgraded package and others can cause changes to the packages which were in the earlier transaction).
        [Default: no]
        type: bool
        version_added: 2.4


# en mode générateur de code

ansible-doc yum -s
- name: Manages packages with the `yum' package manager
  yum:
      allow_downgrade:       # Specify if the named package and version is allowed to downgrade a maybe already installed higher version of that package. Note that setting allow_downgrade=True can make this module behave in a non-
                               idempotent way. The task could end up with a set of packages that does not match the complete list of specified packages to install (because dependencies between the
                               downgraded package and others can cause changes to the packages which were in the earlier transaction).
      autoremove:            # If `yes', removes all "leaf" packages from the system that were originally installed as dependencies of user-installed packages but which are no longer required by any such package. Should be used alone or
                               when state is `absent' NOTE: This feature requires yum >= 3.4.3 (RHEL/CentOS 7+)

ansible-galaxy

Ansible Galaxy est un service complémentaire sur lequel la communauté peut publier du contenu Ansible (playbooks, rôles, etc).

Cette commande permet donc d’obtenir ou d’y pousser son contenu. Elle est aussi pratique pour gĂ©nĂ©rer en automatique l’arborescence et la structure d’un rĂ´le ansible avec tous les fichiers prĂ© renseignĂ©s. C’est principalement l’utilisation que j’en fais.

Pour initialiser l’arborescence d’un rĂ´le :

ansible-galaxy init mon_nouveau_role --offline
- Role mon_nouveau_role was created successfully

tree mon_nouveau_role/
mon_nouveau_role/
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

ansible-inventory

La commande ansible-inventory permet d’afficher l’inventaire et les variables associĂ©es de la manière qu’Ansible le voit lors d’une exĂ©cution. Elle peut servir pour contrĂ´ler la qualitĂ© d’une config par exemple.

ansible-playbook

L’une des commandes les plus utilisĂ©es, il s’agit lĂ  du lanceur d’un playbook Ansible. L’appel ressemble Ă  la commande ad hoc ansible Ă  la diffĂ©rence que vous n’avez pas besoin de spĂ©cifier sur quel groupe de hosts faire l’action. En effet, ceci est spĂ©cifiĂ© dans le playbook.

Exemple :

ansible-playbook -i inventory mon_playbook.yml

ansible-pull

Cette commande sert Ă  exĂ©cuter des scripts Ansible en les rĂ©cupĂ©rant depuis un gestionnaire de sources. Je ne m’en sers jamais, utilisant directement Git pour ça.

ansible-vault

Ansible Vault est une fonctionnalité permettant de chiffrer un fichier avec Ansible. Ceci peut être utile par exemple pour vos fichiers de variables qui contiendraient des mots de passe ou des données sensibles. Il est par contre nécessaire de fournir le mot de passe Vault pour exécuter le code.

Exemple pour chiffrer l’inventaire :

# on affiche l'inventaire actuel

cat inventory

192.168.1.20
192.168.1.22

# on le chiffre en specifiant un mot de passe

ansible-vault encrypt inventory --ask-vault-pass

# on reaffiche le fichier

cat inventory

$ANSIBLE_VAULT;1.1;AES256
643834373838393832346161393539(...)

Vault peut chiffrer aussi un playbook entier, ou seulement une unique variable dans le contenu. Par contre cela devient vite peu pratique car il faut Ă©diter le fichier avec la commande Vault ou le dĂ©chiffrer/rechiffrer. On peut Ă©galement crĂ©er un fichier Ă  charger contenant le mot de passe pour l’exĂ©cution. Bien Ă©videmment, il convient de ne pas pousser ce fichier de mot de passe sur un repository quelquonque, sans quoi l’intĂ©rĂŞt devient nul.

Cette commande dĂ©panne pour un besoin rapide, mais l’utilisation d’un vrai gestionnaire de secrets est recommandĂ©e pour des besoins plus consĂ©quents ou dynamiques (rotation de secrets, etc). Ansible dispose de plugins permettant de s’interfacer avec diffĂ©rents gestionnaires du marchĂ©, comme Hashicorp Vault ou ceux fournis sur les services Cloud d’Azure, AWS et compagnie par exemple.

Attention ! Vault permet seulement de chiffrer les fichiers. Si vous demandez Ă  Ansible d’afficher le secret dans une tâche debug, il vous l’affichera ! (pensez Ă  utiliser l’action “no_log: true” pour censurer une tâche qui retournerait du contenu sensible)

Fin première partie

Ce premier chapitre vous a prĂ©sentĂ© les principes d’Ansible et les commandes pour l’exploiter. Le prochain article vous prĂ©sentera de manière plus dĂ©taillĂ©e l’inventaire Ansible.