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.