19 mars 2024

Introduction à Ansible

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.

 

Laisser un commentaire

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