
Sur le lab du site, dsoxlab provision s'occupe de tout, vous n'avez rien à faire. Cette page n'est pas un how-to où vous lancez quoi que ce soit : c'est l'ouverture du capot. On regarde ce qui se passe quand le provisionnement enchaîne Terraform, cloud-init et le playbook de préparation, on apprend les 5 prérequis techniques qu'un managed node doit fournir à Ansible, et on décortique ligne par ligne le playbook qui les pose. Objectif : sortir de cette page capable de reproduire la préparation sur n'importe quelle fleet existante, hors du lab.
C'est aussi le moment où vous découvrez le principe « Ansible se prépare lui-même » : cloud-init pose le strict minimum (utilisateur + clé SSH), et Ansible converge le reste via un playbook idempotent. Cette approche reste valable partout : à condition d'avoir SSH + Python 3, un seul ansible-playbook aligne tout.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Identifier les 5 prérequis techniques d'un managed node Ansible et savoir les vérifier ;
- Comprendre ce que
dsoxlab provisionjoue derrière le rideau, et donc ce que vous devrez faire à la main sur une fleet existante ; - Bootstrapper un host sans Python via le module
raw(cas RHEL minimal, Alpine, équipements réseau) ; - Lire le playbook
bootstrap/prepare-managed-nodes/playbook.ymlligne par ligne et comprendre chaque module utilisé ; - Vérifier l'idempotence avec un second
ansible-playbookqui doit afficherchanged=0.
Prérequis
Section intitulée « Prérequis »Cette page suppose que :
- Le lab KVM est provisionné (cf. Préparer le lab KVM) ;
- Vous avez un inventaire fonctionnel sous
inventory/hosts.ymlavec un groupe parentrhce_labregroupantwebserversetdbservers; - L'utilisateur
ansibleexiste sur les managed nodes avec sudo NOPASSWD et la clé SSH publique enauthorized_keys.
Les 5 prérequis d'un managed node
Section intitulée « Les 5 prérequis d'un managed node »| Prérequis | Pourquoi | Comment vérifier |
|---|---|---|
| Python 3 (3.6+) | Tous les modules Ansible (sauf raw) sont du Python exécuté côté cible | ssh ansible@web1.lab python3 --version |
| Accès SSH par clé (compte non-root) | Modèle agentless, pas de mot de passe interactif | ssh -i ssh/id_ed25519 ansible@web1.lab doit passer |
| Sudo NOPASSWD sur ce compte | become: true doit fonctionner sans saisie interactive | ssh ansible@web1.lab sudo -n true doit retourner 0 |
| Firewall ouvert sur SSH | Le moteur push s'appuie sur tcp/22 | nc -z web1.lab 22 doit réussir |
| Horloge synchronisée | Les modules de fichiers (mtime), les certificats, les logs corrélés | chronyc tracking côté managed node |
Quatre des cinq sont déjà couverts par le cloud-init du lab. Le firewall, la synchro NTP, SELinux en mode enforcing, et l'inscription dans /etc/hosts sont posés par le playbook de préparation.
Le playbook de préparation, ligne par ligne
Section intitulée « Le playbook de préparation, ligne par ligne »Le lab fournit ce playbook de préparation. Voici sa structure complète, commentée.
---- name: Préparation des managed nodes (chrony, paquets utiles, /etc/hosts) hosts: rhce_lab # cible le groupe parent (webservers + dbservers) gather_facts: true # récupère ansible_distribution, ansible_default_ipv4... become: true # toutes les tâches s'exécutent en root via sudo
vars: lab_packages: - python3-libselinux # indispensable quand SELinux est enforcing - firewalld # firewall standard RHEL (objectif RHCE) - python3-firewall # binding Python pour ansible.posix.firewalld - chrony # synchro horaire entre managed nodes - bash-completion - vim-enhanced - tar - rsync
lab_hosts_entries: - { ip: 10.10.20.10, name: control-node } - { ip: 10.10.20.21, name: web1 } - { ip: 10.10.20.22, name: web2 } - { ip: 10.10.20.31, name: db1 }Le bloc vars regroupe toutes les valeurs du playbook au même endroit, ajouter un paquet ou un hôte ne demande qu'une ligne.
Tâche 1, Installer les paquets de base
Section intitulée « Tâche 1, Installer les paquets de base » - name: Installer les paquets de base ansible.builtin.dnf: name: "{{ lab_packages }}" state: presentLe module dnf est idempotent : il vérifie chaque paquet, n'installe que ceux qui manquent. Au second run, changed=0.
Tâches 2-3, Activer chrony et firewalld
Section intitulée « Tâches 2-3, Activer chrony et firewalld » - name: Activer et démarrer chronyd ansible.builtin.systemd: name: chronyd enabled: true # démarrage au boot state: started # démarré maintenant
- name: Activer et démarrer firewalld ansible.builtin.systemd: name: firewalld enabled: true state: startedLe module systemd combine enable + start en une seule tâche. Sans enabled: true, le service redémarrerait pas après reboot.
Tâche 4, Ouvrir SSH dans firewalld
Section intitulée « Tâche 4, Ouvrir SSH dans firewalld » - name: Autoriser le service SSH dans firewalld (zone publique par défaut) ansible.posix.firewalld: service: ssh permanent: true # écrit dans /etc/firewalld/zones/public.xml immediate: true # applique aussi à la session runtime en cours state: enabledpermanent: true + immediate: true est le combo standard : la règle survit au reboot et s'applique tout de suite. Sans immediate: true, il faudrait recharger firewalld manuellement.
Tâche 5, Inscrire les hôtes dans /etc/hosts
Section intitulée « Tâche 5, Inscrire les hôtes dans /etc/hosts » - name: Inscrire chaque hôte du lab dans /etc/hosts ansible.builtin.lineinfile: path: /etc/hosts line: "{{ item.ip }} {{ item.name }}.lab {{ item.name }}" regexp: "^{{ item.ip | regex_escape }}\\s" state: present owner: root group: root mode: "0644" loop: "{{ lab_hosts_entries }}" loop_control: label: "{{ item.name }}"lineinfile ajoute la ligne si elle n'existe pas, ou la remplace si une ligne match regexp: (utile quand les IPs changent). Le loop_control.label: rend la sortie plus lisible (affiche web1 au lieu du dict complet).
Tâches 6-7, SELinux et timezone
Section intitulée « Tâches 6-7, SELinux et timezone » - name: Vérifier que SELinux est en mode enforcing ansible.posix.selinux: state: enforcing policy: targeted
- name: Régler la timezone du lab (Europe/Paris) community.general.timezone: name: Europe/ParisSELinux enforcing est attendu en production RHEL et sur l'examen RHCE. Le module détecte l'état courant et n'agit que si nécessaire.
Rejouer le playbook pour observer (optionnel)
Section intitulée « Rejouer le playbook pour observer (optionnel) »dsoxlab provision a déjà joué ce playbook une fois pour vous. Le rejouer manuellement n'est pas obligatoire, c'est un exercice utile pour observer l'idempotence en action et se familiariser avec la sortie d'ansible-playbook avant de basculer sur les guides suivants.
Depuis la racine du repo ansible-training :
ansible-playbook labs/bootstrap/prepare-managed-nodes/playbook.yml(L'option -i inventory/hosts.yml est implicite : ansible.cfg à la racine du repo pointe déjà sur ce fichier.)
Au second passage sur un lab déjà préparé, vous devez voir le PLAY RECAP suivant :
PLAY RECAP *********************************************************************db1.lab : ok=8 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0web1.lab : ok=8 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0web2.lab : ok=8 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0changed=0 est la preuve d'idempotence : tous les modules ont vérifié l'état réel, l'ont trouvé conforme à l'état désiré, et n'ont rien modifié. Au premier run sur un lab fraîchement provisionné, vous verrez plutôt changed=6 ou changed=7 car les paquets, services, règles firewall et lignes /etc/hosts doivent être ajoutés.
Cas particulier, Bootstrap d'un host sans Python 3
Section intitulée « Cas particulier, Bootstrap d'un host sans Python 3 »Sur certaines images minimales (RHEL UBI, Alpine, équipements réseau), Python 3 n'est pas installé. Vous ne pouvez pas faire un ansible all -m ping directement, vous devez passer par le module raw qui exécute du shell brut via SSH, sans Python côté cible.
---- name: Bootstrap Python 3 sur les managed nodes minimaux hosts: rhce_lab gather_facts: false # désactivé, sinon Ansible essaie d'utiliser setup et plante become: true
tasks: - name: Vérifier la présence de Python 3 ansible.builtin.raw: which python3 || dnf install -y python3 register: python_check changed_when: "'Installed' in python_check.stdout"Une fois Python 3 installé via raw, vous bascule sur les modules normaux (dnf, systemd, etc.) dans les plays suivants. Le gather_facts: false est obligatoire dans le play de bootstrap, sinon Ansible essaie d'exécuter le module setup qui demande Python.
À retenir
Section intitulée « À retenir »- Un managed node doit fournir : Python 3, SSH par clé, sudo NOPASSWD, firewall ouvert, horloge synchronisée.
- Le cloud-init du lab ne fait que poser l'utilisateur et la clé SSH ; Ansible converge le reste via le playbook de préparation.
- Le module
dnfest idempotent ;systemdcombine enable + start ;firewalldavecpermanent: true+immediate: trueest le combo standard. - Sans Python 3 côté cible, on bootstrappe via le module
raw(avecgather_facts: false). changed=0au second passage est la preuve d'idempotence, le critère de qualité d'un playbook bien écrit.
Mettre en pratique
Section intitulée « Mettre en pratique »Connaître les cinq prérequis ne dit pas encore s'ils tiennent sur une machine réelle. Le lab vous fait converger les trois managed nodes avec le playbook de bootstrap décortiqué ci-dessus : paquets de base, chronyd actif, entrées dans /etc/hosts, SELinux en enforcing et fuseau horaire. Les tests interrogent chaque nœud plutôt que votre YAML, et le second passage doit rendre changed=0, seule preuve recevable d'idempotence.
En cas de blocage sur l'environnement lui-même, dsoxlab doctor contrôle Python, pytest et les runtimes détectés. Pour les erreurs venant du playbook, la section Troubleshooting Ansible couvre la verbosité, le debugger et l'idempotence cassée.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Première connexion SSH : la clé et le fichier
~/.ssh/configqui rendent ces cinq prérequis exploitables. - Commandes ad-hoc : vérifier en une commande que chaque prérequis tient vraiment sur le parc.
- Debug des premières erreurs :
Permission deniedousudo: a password is requiredtrahissent presque toujours un prérequis manquant.