Dans cet article, je vais rapidement vous présenter Ansible.
Mais tout d’abord, qu’est ce qu’Ansible ?
C’est un gestionnaire de configuration : vous définissez un ensemble de tâches, des machines sur lesquelles les appliquer, et roule pedro, les actions se font comme par magie (ou presque…).
Dans le cadre de mon nouveau système de déploiement de VM KVM par un script maison, j’avais besoin de trouver comment rapidement configurer les services sur mes VMs. Bah oui, je met 10 secondes pour générer une nouvelle VM, mais si derrière, je dois perdre XX minutes à installer des choses, les configurer, etc…, c’est balo.
Après une recherche sur les outils disponibles où beaucoup font plus ou moins les mêmes choses ( Puppet, Chef, ….), Ansible m’a semblé être le plus intéressant. Pas de besoin de client sur les serveurs, tout se fait en ssh; c’est écrit en python (et le python, c’est le bien)…
Nul doute que je testerais les autres solutions un jour, mais pour le moment, c’est Ansible 🙂
Ici, nous verrons quelque chose de très basique, qui me sert pour préparer le socle commun de mes VMs.
I – Installation
Pour installer Ansible, c’est simple :
# apt install ansible -t buster-backports
Oui, j’utilise les backports, histoire d’avoir un paquet un peu plus à jour.
Ensuite, les prérequis sur les VMs à gérer :
- Python 3 installé
- Authentification par clé SSH (si besoin de rafraichissement, c’est ici)
Pour qu’Ansible sache quelles sont les machines qu’il gère, c’est dans le fichier /etc/ansible/hosts avec par exemple :
[proxy] proxy1 proxy2 [mariadb] mariadbX mariadbY [divers] divers1 [all] proxy1 proxy1 mariadbX mariaddbY divers1 [all:vars] ansible_python_interpreter=/usr/bin/python3
On déclare des groupes en fonction de qui fait quoi, dont un groupe « all » qui contient tous les serveurs. Et pour chaque groupe, on déclare nom DNS ou IP des serveurs de la catégorie.
Au passage, on glisse une petite variable utile (par défaut, Ansible utilise python 2.7)
Une fois le fichier configuré, on peut tester la bonne communication avec :
# ansible all -m ping
Qui doit vous donner pour chaque serveur :
nom_serveur | SUCCESS => { "changed": false, "ping": "pong" }
Bref, si ça fonctionne, on peut continuer.
II – Explication
Ansible utilise des modules pour effectuer les actions, La liste (gigantesque) est disponible ici : https://docs.ansible.com/ansible/latest/modules/list_of_all_modules.html (oui, il y en a énormément….)
Dans notre première commande, nous avons utilisé le module ping (-m ping).
Tout ceci, on peut donc le faire en CLI, mais il est plus simple d’utiliser des fichiers de définitions qui, dans le monde Ansible, s’appellent des Playbooks. Il s’agit tout simplement de fichiers au format YAML ( oui, j’en entends crier au loin).
Dans ces fichiers, on peut déclarer des tâches qui lancent des modules.
Il existe également la notion de rôles. Un rôle, c’est un regroupement de taches, et ce rôle, on peut l’appeler depuis un playbook.
Bon, je sens que je suis en train d’en perdre, donc je résume :
On a des playbooks qui lancent des rôles, qui lancent des tâches, qui lancent des modules.
Bon, le blabla, c’est bien beau, mais un exemple sera plus parlant :
A – Playbook
Direction le répertoire d’Ansible :
# cd /etc/ansible/
Dedans, créez un fichier init_vm.yml avec ceci :
- hosts: all gather_facts: no roles: - ssh-key - hosts: all gather_facts: yes vars: nameserver: 10.10.10.10 roles: - common
Ce playbook, que je lance après la génération d’une VM, exécute deux rôles.
Pourquoi deux ? Car les deux ont une configuration un peu différentes au niveau de la collecte des « facts », à savoir dans le langage Ansible, les configurations des serveurs.
B – Les Facts
Pour voir de quoi on parle, on va lancer une petite commande :
# ansible all -m setup
Ce qui vous donne les « facts » de vos serveurs, données qu’Ansible collecte (quand l’option est sur yes) et qui peuvent éventuellement être utilisées dans nos tâches.
Pour collecter, voir et stocker dans un répertoire, c’est en rajoutant :
# ansible all -m setup --tree monrepertoire
C – Rôles
Donc, je disais, deux rôles et je vous explique pourquoi.
Pour ce playbook « basique », j’utilise le groupe « all », ainsi, pas besoins de gérer un groupe spécifique pour de nouvelles vms.
Quand je génère une VM, je l’ajoute, à minima, au groupe « all » , et j’exécute ce playbook. Toutes les machines sont « impactées » mais seules celles qui ne sont pas « à jour » voient les tâches s’exécuter. Les autres répondent que c’est déjà OK.
Impeccable, mais…
Je vous disais qu’Ansible utilise la clé SSH pour se connecter aux machines, mais que se passe t’il la toute première fois ? Vous savez, quand on vous demande si vous acceptez le fingerprint ? Pour les VMs déjà en prod, a priori, c’est bon, mais pour une nouvelle ?
Comme je n’ai pas encore effectué de connexion SSH vers la machine, Ansible ne pourra pas s’y connecter et mon rôle common ne fonctionnera pas, la partie gather fact sera bloquante.
Résultat, un premier rôle récupère les fingerprints et les intègre, tout ça avec l’option gather_facts sur no.
Une fois la VM « accessible » par Ansible, on peut lancer des rôles avec l’option gather_facts sur yes.
Voila le pourquoi du comment. Ceci dit, j’aurais pu tout à faire faire autrement et intégrer les fingerprints durant la création de mes VMs, mais bon, je retoucherai mon script plus tard.
Après l’explication (laborieuse j’en conviens), la mise en pratique :
Tout d’abord, on va préparer le répertoire pour les rôles :
# cd /etc/ansible # mkdir roles # cd roles
Les rôles reposent sur un structure de répertoire précises, mais un outil existe pour faire ça bien :
# ansible-galaxy init common # ansible-galaxy init ssh
Vous trouverez vos deux sous répertoires avec un structure déjà en place.
1 – Rôle SSH
Pour le rôle SSH, vous éditez le fichier /etc/ansible/roles/ssh-key/tasks/main.yml :
--- - name: Test ssh connection: local command: ssh-keygen -F {{ inventory_hostname }} register: keygen failed_when: keygen.stderr != '' changed_when: False - name: get ssh key connection: local command: ssh-keyscan -H {{ inventory_hostname }} register: keyscan failed_when: keyscan.rc != 0 or keyscan.stdout == '' changed_when: False when: keygen.rc == 1 - name: add ssh-key to known_hosts connection: local lineinfile: name: ~/.ssh/known_hosts create: yes line: "{{ item }}" when: keygen.rc == 1 with_items: '{{ keyscan.stdout_lines|default([]) }}'
Les trois tâches ont en commun le connection: local qui indique que les commandes sont exécutées sur le serveur ou est intallé Ansible.
La première tâche stocke le retour de la commande ssh-keyscan (code erreur 1 si la clé n’est pas trouvée).
Les autres tâches (récupération de la clé et écriture dans le fichier) ne se font que si le code erreur est à 1 (when: keygen.rc == 1).
Pour voir un peu à quoi ressemble un variable de retour de commande, vous pouvez ajouter dans le fichier :
- name: debug debug: msg: Valeur de keygen : {{ keygen }}
et ainsi voir l’objet retourné.
2 – Rôle Common
Ensuite, on passe au fichier /etc/ansible/roles/common/tasks/main.yml :
--- - name : Update Resolver template: src: resolv.conf.j2 dest: /etc/resolv.conf - name: Paquets de base apt: name: - python3-apt - apt-transport-https - ca-certificates - curl - gnupg-agent - software-properties-common - sudo - htop - bash-completion - wget - man-db - parted - bsd-mailx - git state: latest update_cache: yes
La première tâche copie un fichier template et la seconde installe des paquets (avec un update au préalable).
Le fichier resolv.conf.j2 doit se trouver ici /etc/ansible/roles/common/templates/resolv.conf.j2 et contenir ceci :
nameserver {{ nameserver }}
La variable {{ nameserver }} sera changée quand le fichier sera copié sur la VM. Variable qu’on a définit dans le playbook.
E – Exécution
Il est maintenant temps de voir si on ne s’est pas « merdé » dans les YAMLS (indentation à respecter scrupuleusement…)
Pour lancer notre nouveau playbook :
# cd /etc/ansible # ansible-playbook init_vm.yml
Sous vos yeux ébahis se lance alors vos tâches et à la fin, un récapitulatif vous indiquant ce qui a été fait.
Et si on relance le playbook, le retour sera un peu différent nous indiquant que rien n’a été modifié (bah oui, c’est déjà fait…)
III – Conclusion
Oui, ce n’est qu’un très bref aperçu de l’outil, j’aurais souhaité en expliquer tout de suite d’avantage, mais je préférè le faire au fil de l’eau quand l’occasion se présente et venir indiquer une référence ici.