Table des matières

2022/09/19 (atelier) : Ansible

Présences :

Météo

Moment informel durant lequel on exprime en peu de mots comment on se sent et si on a une attente forte pour la réunion. Ce n'est pas un moment de discussion mais d'expression individuelle et ce n'est pas obligatoire 🙂

Attente(s) forte(s)

Si l'une ou l'autre personne exprime une attente forte, merci de vous en occuper en priorité ou de la noter dans le hub ou dans un point approprié.

Configuration de l'espace de travail

Vagrant

Vagrant est une boite à outils pour piloter un hyperviseur local. La différence avec VirtualBox, c'est qu'on peut dire à Vagrant de créer une VM avec tant de CPU, de RAM… tout cela au travers d'un fichier.

Dans le fichier, on a différents paramètres :

Par exemple, la config minimale est la suivante :

snippet.vagrant
Vagrant.configure("2") do |config|
  config.vm.box = "debian/bullseye64"
end

config.vm.box → indique ce qu'on va utiliser comme OS

Il y a des OS officiel mais aussi des OS qui sont faits par des gens. On peut en voir différents ici :

https://vagrantcloud.com/search

L'image est déployée pour certains providers comme libvirt ou virtualbox.

Grace à vagrant, on va dire sur quelle base on démarre et s'y connecter. Vagrant peut aussi exécuter un playbook ansible.

→ on appelle ça du provisionning.

libvirt vs virtualbox

L'intérêt de vagrant par rapport à Docker : il fait une VM avec un OS complet. Docker ne fait que des conteneurs.

Installation de vagrant

On installe vagrant via le package manager, par ex pour manjaro:

pacman -Syu vagrant

On va vouloir tester libvirt, donc on l'installe aussi:

pacman -Syu libvirt

Ensuite on installe le plugin libvirt pour vagrant:

vagrant plugin install vagrant-libvirt

Initialisation

On va générer un Vagrantfile pour pouvoir un peu comprendre comment fonctionne vagrant.

Pour cela, on crée un dossier vide sur notre ordinateur et on se place dedans:

mkdir vagrant-test
cd vagrant-test

Ensuite, on génère le Vagrantfile avec:

vagrant init debian/bullseye64

Avec un ls, on peut voir que le Vagrantfile a été créé.

Par défaut, la seule configuration active est celle qui indique l'OS utilisé :

snippet.vagrant
  config.vm.box = "debian/bullseye64"

On active le port-fowarding ssh. Cela permet de se connecter depuis localhost à la VM. C'est ce que fait Molecule par défaut, mais on le fait ici manuellement.

On donne aussi une quantité pour la RAM

snippet.vagrant
  config.vm.provider "libvirt" do |libvirt|
    # Enable forwarding of forwarded_port with id 'ssh'.
    libvirt.forward_ssh_port = true
 
    # Customize the amount of memory on the VM:
    libvirt.memory = "1024"
  end

Il y a moyen de configurer le nombre de CPU, etc. mais on ne va pas voir cela en détail, donc cf. la doc https://www.vagrantup.com/docs/vagrantfile

Pour démarrer la machine :

snippet.sh
vagrant up --provider=libvirt

Ici, on spécifie le provider. Il y a une variable d'environnement qui permet de dire quel est le provider par défaut, ce qui évite de devoir le spécifier par la suite:

snippet.sh
VAGRANT_DEFAULT_PROVIDER=libvirt

Si libvirt n'est pas démarré, il faut le démarrer :

systemctl start libvirtd
systemctl status libvirtd

Pour éviter de devoir taper son mot de passe admin, on peut ajouter son utilisateur au groupe libvirt :

sudo usermod -aG libvirt celo

Libvirt a besoin d'un hyperviseur. Il faut installer QEMU :

sudo pacman -S qemu-full virt-manager

Il y a un outil qui permet d'accélérer la virtualisation sur sa machine, c'est KVM. Mais on en reparlera.

Par défaut, Vagrant fait un partage NFS, un stockage externe de la VM sur le host et partagée avec le host. C'est utile pour les devs mais on n'a pas besoin de ça donc on peut changer son comportement (on pourrait le désactiver, à voir comment faire) :

snippet.vagrant
  config.vm.synced_folder "./", "/vagrant", type: "rsync"

Pour le désactiver, il faut mettre:

snippet.vagrant
  config.vm.synced_folder "./", "/vagrant", disabled: true

En le mettant en rsync, ce sera unidirectionnel, on peut juste déposer des fichiers sur la machine. Mais ça évite d'avoir un serveur NFS qui tourne et de devoir taper son mot de passe.

On va recommencer avec cette nouvelle configuration. Pour détruire la machine, on utilise :

vagrant destroy

Et on crée à nouveau la VM:

vagrant up --provider=libvirt

Et là on voit bien qu'il ne configure plus le serveur NFS, donc c'est chouette:

==> default: Installing rsync to the VM...
==> default: Rsyncing folder: /home/hadrien/Projets/vagrant/ => /vagrant

On peut se connecter à Vagrant en SSH avec :

vagrant ssh

On est connecté en tant qu'utilisateur vagrant et on peut faire un sudo -i sans avoir besoin de mot de passe.

Commandes vagrant

En résumé, on a pas besoin d'en connaître beaucoup pour utiliser Vagrant :

vagrant up --provider=libvirt
vagrant ssh
vagrant destroy

Pour connaître les images qui ont été téléchargées et les mettre à jour :

vagrant box list
vagrant box update

Ou pour installer les plugin :

vagrant plugin install <nom du plugin>

Ou encore démarrer un nouveau projet:

vagrant init <nom de la box>

A chaque fois, on a un dossier avec un Vagrantfile par VM.

Fonctionnalités avancées SSH

Depuis le dossier de la machine, si on va dans le dossier :

$ ls .vagrant/machines/default/libvirt/
action_provision  created_networks  id          logs  private_key     vagrant_cwd
box_meta          creator_uid       index_uuid  pids  synced_folde

On trouve la clé SSH privée de la machine.

On peut s'y connecter avec directement en SSH en bypassant le fonctionnement de vagrant :

ssh -i .vagrant/machines/default/libvirt/private_key vagrant@192.168.121.141

Ansible

Installation d'Ansible

Soit on installe Ansible dans le package manager, mais alors on a la même version pour tous les projets (qui n'est pas forcément à jour). Le mieux est de l'installer via un Virtualenv de python.

Entre deux virtualenv différents, on peut avoir des paquets qui n'ont pas la même version. Ca assure une compatibilité entre les différents projets auxquels on contribue.

Dans le projet Neutrinet, on a un requirements.txt de tous les paquets nécessaires.

Dans un nouveau dossier Ansible-Test, on va créer un virtualenv.

Pour créer le virtualenv, il suffit de lancer la commande suivante:

python3 -m venv <dossier>

En général, le nom du dossier va être .env, .venv, env, venv ou encore ve.

On peut aussi le faire dans le même dossier, mais en général on essaie de se mettre d'accord sur une norme commune. → car dans le fichier .gitignore, on ignore les .venv etc., pour indiquer qu'on ne prend pas ce fichier, mais on ne veut pas 2000 noms 🙂

Ensuite, on active le virtualenv avec

source .venv/bin/activate

Ou (cela revient au même):

. .venv/bin/activate

On peut voir qu'on utilise bien le virtualenv : il y a un préfixe avec le nom du virtualenv devant le prompt du terminal.

On peut aussi vérifier la version de pip:

pip --version

Tout ce qu'on installe via pip dans le virtual environnement va s'installer dans le dossier .venv et ne sera pas utilisé ailleurs dans le système.

On peut voir, depuis l'environnement virtuel, d'où sont exécutés les logiciels :

echo $PATH
~/Ateliers-Ansible/Ansible-Test/.venv/bin:~/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/var/lib/snapd/snap/bin

Si on une version d'Ansible déjà installée via le package manager, ce n'est pas celle qui sera utilisée lorsqu'on utilise Ansible dans l'environnement virtuel. En revanche, il faut faire attention à certains composants qu'on pourrait ne pas avoir installé dans l'environnement virtuel… le linter (ansible-lint, composant qui vérifie la syntaxe), ou encore molecule, par exemple…

On peut enfin installer Ansible !

pip install ansible

On vérifie que tout est bien installé:

ansible --version

Cette commande permet aussi de vérifier qu'Ansible fonctionne, et montre où sont stockés les différents modules (on peut on créer des custom notamment, même si on n'en a pas créé pour Neutrinet pour l'instant).

Utilisation d'Ansible avec Vagrant

On relance une machine vagrant :

vagrant up --provider=libvirt

On va se baser sur le cours suivant : https://supports.uptime-formation.fr/06-ansible/tp1/

Pour vérifier le fonctionnement, on peut faire une commande pour pinguer toutes les VM actives :

ansible all -m ping

A ce stade, ansible ne trouve pas de VM car l'inventaire n'est pas complété. Par défaut, ansible cherche l'inventaire dans /etc/ansible/hosts mais ne trouve rien.

On peut ajouter l'option -vvv à la commande pour voir plus de déuggage.

On constate en revanche que ansible localhost -m ping fonctionne.

On va créer un inventaire. Il y a deux formats, yaml et ini. Yaml est plus verbeux. Pour l'inventaire, ini suffit.

Dans un fichier ini, on formule les choses avec des équivalences : clé = valeurs. Et on a des sections entre crochets. Dans le cadre d'un inventaire de machine, les sections sont des groupes de machines, et chaque ligne est un host.

Dans un inventaire, on peut définir des variables propres à la machine ou à un groupe d'hôtes. Ce n'est pas une bonne pratique, parce qu'on utilise plutôt des dossiers en parallèle, ce qui permet de découpler la liste des hosts des variables qui leurs sont associées.

On crée donc le fichier hosts.ini avec :

machine1 ansible_host=192.168.121.217

Maintenant, on peut tester la connexion (il fait plus qu'un ping) avec la VM. Il faut spécifier dans la commande, le fichier d'inventaire que l'on vient de créer :

ansible -m ping all -i hosts.ini

Mais ansible doit connaître les infos de connexions (clé privée et login). Ca nous donne donc au final :

ansible all -m ping -i hosts.ini -u vagrant --private-key ~/Documents/Ateliers-Ansible/Vagrant-test/.vagrant/machines/default/libvirt/private_key

Ca marche… mais c'est long (et on est fainéants). On peut raccourcir en ajoutant la private key au ssh agent.

On vérifie que le ssh-agent tourne:

ssh-add -l

S'il ne tourne pas, on le lance:

eval $(ssh-agent)

Et on ajoute la clé privée de la machine:

ssh-add ~/Documents/Ateliers-Ansible/Vagrant-test/.vagrant/machines/default/libvirt/private_key

On peut retester la connexion sans le paramètre --private-key

ansible all -m ping -i hosts.ini -u vagrant 

Pour le user, il y a trois solutions, soit on prod, on a configuré son fichier de config ssh. L'autre solution est le -u. Une troisième solution est de rajouter une variable :

Il y a une préséance des variables: https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#understanding-variable-precedence La doc n'est pas toujours clair, mais on apprend progressivement par essais-erreur en s'arrachant les cheveux 🙂

On modifie le fichier hosts.ini pour ajouter une variable pour le user :

machine1 ansible_host=192.168.121.217 ansible_ssh_user=vagrant

Les variables qui commencent par ansible sont des variables d'ansible. Ansible appelle ça des facts car c'est tout ce qui touche à l'environnement ansible. Ca permet de voir toutes les variables qui définissent la machine.

ansible -m gather_facts all -i hosts.ini

On peut définir un inventaire par défaut pour ansible. Pour cela, on doit créer un fichier de config pour Ansible. Par défaut, Ansible va regarder s'il y a un fichier ansible.cfg qui existe dans le répertoire en cours. On crée ce fichier avec:

[defaults]
inventory = hosts.ini

On peut tester sans préciser l'inventaire:

ansible -m ping all

Maintenant, on va créer une deuxième machine avec vagrant. On fait un vagrant destroy puis on rajoute ceci dans le Vagrantfile:

 config.vm.define :ubuntu1 do |ubuntu1|
      ubuntu1.vm.box = "generic/ubuntu2204"
      ubuntu1.vm.hostname = "ubuntu1"
 end
 
 config.vm.define :debian1 do |debian1|
      debian1.vm.box = "debian/bullseye64"
      debian1.vm.hostname = "debian1"
 end

On enleve aussi la partie de config config.vm.box = "debian/bullseye64".

Dans le fichier inventaire, on va créer deux groupes, un groupe Ubuntu et un autre Debian :

[debian]
debian1 ansible_host=192.168.121.26

[ubuntu]
ubuntu1 ansible_host=192.168.121.103

Une fois les machines installées (le téléchargement de l'image ubuntu prend du temps…), on peut s'y connecter en SSH. Il faut préciser le nom de la machine à laquelle on veut se connecter :

vagrant ssh debian1
vagrant ssh ubuntu1

Pour utiliser ansible, on rajoute les clés privées des deux machines avec ssh-add, comme fait précédemment, et on fait un test de connexion:

ssh-add ~/Documents/Ateliers-Ansible/Vagrant-test/.vagrant/machines/debian1/libvirt/private_key
ssh-add ~/Documents/Ateliers-Ansible/Vagrant-test/.vagrant/machines/ubuntu1/libvirt/private_key
ansible -m ping all -u vagrant

On peut pinger les machines individuellement mais aussi en mentionnant le goupe :

ansible -m ping debian -u vagrant
ansible -m ping ubuntu -u vagrant

Ici, nos groupes ne sont pas très intéressant, mais en prod ça peut-être intéressant, avec par exemple un groupe postgresql…

On va créer un groupe “labo”, en rajoutant ceci dans l'inventaire:

[labo]
debian1
ubuntu1

On n'a pas besoin de remettre leurs IP car elles sont déjà déclarées.

Comme d'hab, on teste sur ce nouveau groupe:

ansible -m ping labo -u vagrant

On va aller plus loin en définissant une hiérarchie des groupes. À la place de ce qu'on venait de rajouter, on met:

[labo:children]
debian
ubuntu

Ici, on dit que les “enfants” du groupe labo sont les groupes debian et ubuntu (qui pourraient contenir plusieurs machines). On ne spécifie plus les machines elles-mêmes.

On voit qu'on peut commencer à hiérarchiser. Chez Neutrinet, on a par exemple les machines de LouiseDC, les enfants sont les machines baremetal, le réseau et le cluster patata.

Certains vont tirer les machines par datacenter, mettre les proxmox ensembles…

Mettre les variables dans l'inventaire n'est pas une très bonne pratique.

On va créer un dossier d'inventaire qui contiendra notre fichier d'inventaire et toutes les variables pour chaque host:

mkdir inventories
mv hosts.ini inventories/hosts.ini
mkdir inventories/group_vars inventories/host_vars

On modifie le fichier de config ansible.cfg pour avoir:

inventory = inventories/hosts.ini

Ansible fonctionne avec une structure prédéfinie.

L'intérêt de faire un dossier inventories est de séparer les variables de l'inventaire des variables des playbook (qui seront dans un dossier séparé après). Cela permet aussi d'avoir une vue claire d'où se trouvent le ou les inventaires, et les playbooks, etc.

Ansible va aller regarder s'il y a des variables définies dans le dossier host_vars qui se trouve au même niveau que le fichier d'inventaire hosts.ini. S'il ne trouve pas une variable, il va aller chercher dans le dossier group_vars, etc. Cela nous ramène à la notion de préséance des variables.

Cela permet d'avoir une structure. Molécule va aussi utiliser une structure.

On va créer deux fichiers inventories/host_vars/debian1.yml et inventories/host_vars/ubuntu1.yml avec:

ansible_host: <ip>

Et on enlève la variable ansible_host du hosts.ini pour ces deux machines.

On peut à présent tester la connexion:

ansible -m ping all -u vagrant

Attention de bien lancer la commande depuis la racine du projet (là où se trouve le fichier ansible.cfg).

On crée enfin un fichier inventories/group_vars/all.yml dans lequel on va mettre:

ansible_ssh_user: vagrant

Quand on teste la connexion, on ne doit plus mettre le nom d'utilisateur:

ansible -m ping all

On est parvenu à séparer les variables ansible de l'inventaire des hosts, ce qui permet d'avoir une vue clair sur la liste des machines et aussi sur la liste des variables pour telle machine ou groupe de machines.

TODO: création d'un playbook de test pour les VMs

Molecule

pip + virtualenv

plugin molecule-vagrant

Labo

Création d'une VM via molecule

Playbook vs rôle

Keycloak

Prochaine réunion

Prochain atelier Ansible : 24/09 à 14h

Lieu : Chez Célo (métro Crainhem)

Météo de fin

Moment informel durant lequel on exprime en peu de mots comment, à titre personnel, la réunion a été vécue que ce soit positif ou négatif. Si une ou plusieurs tension est née durant la réunion, il est peut-être nécessaire d'envisager l'une ou l'autre réunion pour y remédier.