Monitor Multi-Servers with Ansible

Deploy a Prometheus/Grafana monitoring stack across 3 VMs using Ansible.

Introduction

30 Second Summary

Imagine gerer plusieurs serveurs en production sans savoir lequel est en surcharge ou plante silencieusement. C'est le quotidien des sysadmins sans monitoring centralise.

Dans ce projet, tu vas deployer une infrastructure de monitoring complete sur 3 serveurs Rocky Linux avec Ansible, Prometheus et Grafana. Un seul playbook automatise tout, de l'installation des serveurs web a la visualisation des metriques en temps reel.

What You'll Build

Tu lanceras une seule commande ansible-playbook et en moins de 2 minutes, tes 3 serveurs seront monitores avec des graphiques CPU, RAM et disque qui s'actualisent en direct dans un dashboard Grafana.

A la fin de ce projet, tu auras :

  • Un dashboard Grafana affichant les metriques systeme (CPU, RAM, disque, reseau) de 3 serveurs en temps reel.
  • Un playbook Ansible qui deploie toute l'infrastructure de monitoring en une seule commande sur N serveurs.
  • Deux serveurs web Nginx monitores automatiquement par Prometheus via node_exporter.
  • Secret Mission: Simuler une panne serveur et observer Prometheus detecter l'incident en temps reel.

Y a-t-il des prerequis ?

Ce projet s'adresse aux apprenants ayant des connaissances RHCSA et RHCE (administration Linux et Ansible). Tu dois aussi avoir VMware Workstation installe sur Windows.

Before We Start

Avant de creer des machines virtuelles et d'ecrire des playbooks, prends un moment pour formuler clairement ce que tu vas construire dans ce projet et pourquoi. C'est le moment de verrouiller ta comprehension avant de plonger dans la technique.

Preparer l'Environnement Multi-Serveurs

Pour deployer une infrastructure de monitoring, tu as besoin de plusieurs serveurs qui communiquent entre eux. Sans cet environnement multi-serveurs, impossible de pratiquer la gestion d'infrastructure a l'echelle.

Dans cette etape, tu vas creer 3 machines virtuelles Rocky Linux 9.8 dans VMware Workstation, installer Ansible sur ta machine de controle, et configurer la connectivite SSH entre tous les serveurs.

Dans cette etape, prepare-toi a :
  • Creer 3 VMs Rocky Linux 9.8 avec un reseau inter-VM fonctionnel.
  • Installer Ansible et configurer l'authentification SSH par cle.
  • Creer l'inventaire Ansible et verifier la connectivite vers tous les hotes.
Creer les 3 VMs Rocky Linux 9.8 dans VMware Workstation

Trois VMs simulent un environnement de production realiste. La VM control sera ta machine de gestion Ansible. Les VMs web1 et web2 seront tes serveurs web cibles.

  • Telecharge l'ISO Rocky Linux 9.8 minimal depuis le site officiel. Le fichier s'appelle Rocky-9-latest-x86_64-minimal.iso.
  • Dans VMware Workstation, ouvre le Virtual Network Editor (menu Edit > Virtual Network Editor).
  • Selectionne le reseau NAT (VMnet8) et modifie le sous-reseau en 192.168.100.0 avec le masque 255.255.255.0. Clique Apply.
  • Cree la premiere VM avec ces parametres : nom control, 2 Go RAM, 20 Go disque, adaptateur reseau sur NAT.
  • Cree la deuxieme VM : nom web1, 1 Go RAM, 15 Go disque, adaptateur reseau sur NAT.
  • Cree la troisieme VM : nom web2, 1 Go RAM, 15 Go disque, adaptateur reseau sur NAT.
  • Installe Rocky Linux 9.8 en mode minimal sur chaque VM. Pendant l'installation, cree un utilisateur sysadmin avec les privileges administrateur.

Pourquoi le mode NAT ?

Le mode NAT permet a tes VMs d'acceder a Internet pour telecharger des paquets, tout en communiquant entre elles sur le reseau 192.168.100.0/24.

Une fois les 3 VMs demarrees et connectees en tant que sysadmin, configure une IP statique sur chaque VM. Sur la VM control, identifie d'abord le nom de ta connexion reseau :

  • Affiche les connexions disponibles en executant :
nmcli connection show

Tu verras le nom de ta connexion active (souvent ens160 ou ens33). Note ce nom pour les commandes suivantes.

  • Configure l'IP statique sur la VM control en executant ces commandes (remplace ens160 par le nom de ta connexion) :
sudo nmcli connection modify ens160 ipv4.method manual ipv4.addresses 192.168.100.10/24 ipv4.gateway 192.168.100.2 ipv4.dns 8.8.8.8
sudo nmcli connection down ens160 && sudo nmcli connection up ens160
  • Sur web1, configure l'adresse 192.168.100.11 avec les memes commandes en ajustant l'IP.
  • Sur web2, configure l'adresse 192.168.100.12.
  • Verifie la connectivite depuis control en executant :
ping -c 2 192.168.100.11
ping -c 2 192.168.100.12

Tu devrais voir des reponses de chaque VM, confirmant que le reseau fonctionne entre les 3 machines.

Pas de reponse au ping ?

Verifie que les 3 VMs utilisent bien le meme reseau NAT (VMnet8) dans les parametres de chaque VM. Confirme aussi que le sous-reseau dans le Virtual Network Editor correspond a 192.168.100.0.

Si les pings echouent toujours, verifie que le firewall ne bloque pas ICMP : sudo firewall-cmd --add-protocol=icmp --permanent && sudo firewall-cmd --reload.

Besoin d'aide supplementaire ? Aide-moi a diagnostiquer la connectivite reseau entre mes VMs VMware.

Installer Ansible et configurer l'authentification SSH

Ansible a besoin d'une connexion SSH sans mot de passe pour gerer les serveurs distants. Tu vas installer Ansible via le depot EPEL, puis distribuer une cle SSH a tes 2 serveurs web.

  • Sur la VM control, installe le depot EPEL puis Ansible en executant :
sudo dnf install epel-release -y
sudo dnf install ansible -y
  • Verifie l'installation en executant :
ansible --version

Tu devrais voir le numero de version d'Ansible et le chemin de sa configuration.

Erreur lors de l'installation ?

Si dnf ne trouve pas le paquet ansible, verifie que epel-release est bien installe avec dnf repolist. Le depot EPEL doit apparaitre dans la liste.

Besoin d'aide ? Aide-moi a installer Ansible sur Rocky Linux 9.

  • Genere une paire de cles SSH en executant :
ssh-keygen -t rsa -b 4096

Que fait cette commande ?

Cette commande cree une cle privee (~/.ssh/id_rsa) et une cle publique (~/.ssh/id_rsa.pub). Appuie sur Entree a chaque invite pour accepter les valeurs par defaut (pas de passphrase).

  • Distribue la cle publique a web1 en executant :
ssh-copy-id sysadmin@192.168.100.11
  • Distribue la cle publique a web2 en executant :
ssh-copy-id sysadmin@192.168.100.12

Chaque commande te demandera le mot de passe de sysadmin sur le serveur distant. C'est la derniere fois que tu auras besoin de ce mot de passe.

  • Teste la connexion SSH sans mot de passe en executant :
ssh web1 hostname

Cette commande devrait echouer avec une erreur de resolution de nom. C'est normal, tu n'as pas encore configure /etc/hosts.

  • Configure la resolution de noms sur les 3 VMs. Sur chaque VM (control, web1, web2), ajoute ces lignes a /etc/hosts en executant :
echo '192.168.100.10 control' | sudo tee -a /etc/hosts
echo '192.168.100.11 web1' | sudo tee -a /etc/hosts
echo '192.168.100.12 web2' | sudo tee -a /etc/hosts

Pourquoi configurer /etc/hosts ?

Sans serveur DNS sur ce reseau, les VMs ne peuvent pas resoudre les noms control, web1, web2 en adresses IP. Le fichier /etc/hosts fournit cette resolution localement sur chaque machine.

  • De retour sur control, verifie que la connexion SSH par nom fonctionne :
ssh web1 hostname
ssh web2 hostname

Chaque commande doit retourner le hostname du serveur distant (web1 ou web2) sans demander de mot de passe.

SSH demande encore un mot de passe ?

Verifie que ssh-copy-id a bien copie la cle en regardant ~/.ssh/authorized_keys sur le serveur distant. Le fichier doit contenir ta cle publique.

Verifie aussi les permissions : ~/.ssh doit etre en 700 et authorized_keys en 600. Des permissions trop ouvertes font que SSH refuse la cle.

Besoin d'aide ? Aide-moi a corriger l'authentification SSH par cle.

Creer l'inventaire Ansible et tester la connectivite

L'inventaire Ansible definit les groupes de serveurs et les parametres de connexion. C'est le fichier qui dit a Ansible quels serveurs gerer et comment s'y connecter.

  • Cree le repertoire du projet sur la VM control en executant :
mkdir -p ~/monitoring
cd ~/monitoring
  • Cree le fichier d'inventaire en executant :
cat > inventory.ini << 'EOF'
[control]
control ansible_connection=local

[webservers]
web1 ansible_host=192.168.100.11
web2 ansible_host=192.168.100.12

[monitoring]
control ansible_connection=local

[all:vars]
ansible_user=sysadmin
ansible_ssh_private_key_file=~/.ssh/id_rsa
EOF

Que contient cet inventaire ?

  • Le groupe [control] et [monitoring] utilisent ansible_connection=local car Ansible s'execute directement sur cette machine.
  • Le groupe [webservers] liste tes serveurs web avec leurs adresses IP.
  • La section [all:vars] definit l'utilisateur SSH et la cle privee pour toutes les connexions distantes.

Avant de tester, a ton avis, est-ce que la commande ansible all -m ping va reussir sur les 3 hotes ? Pourquoi ?

  • Teste la connectivite Ansible vers tous les hotes en executant :
ansible all -i inventory.ini -m ping

Tu devrais voir un resultat SUCCESS en vert pour chaque hote : control, web1, et web2.

Que fait ansible -m ping ?

Le module ping d'Ansible ne fait pas un ping ICMP. Il se connecte en SSH, verifie que Python est disponible sur le serveur distant, et retourne pong si tout fonctionne. C'est le test de sante de base pour valider ta configuration Ansible.

Un hote retourne UNREACHABLE ?

Verifie que le SSH sans mot de passe fonctionne manuellement avec ssh sysadmin@web1. Si ca marche, verifie que python3 est installe sur le serveur distant (which python3).

Si l'erreur mentionne la cle SSH, verifie que le chemin ~/.ssh/id_rsa dans ton inventaire correspond bien a ta cle reelle.

Besoin d'aide ? Aide-moi a diagnostiquer pourquoi ansible ping echoue.

✔️ Tout est en vert, parfait !

Excellent. Tes 3 serveurs repondent. Verifie bien que tu as sauvegarde ton fichier inventory.ini dans ~/monitoring/.

ⓧ Je voudrais verifier le code complet

Voici le contenu exact de ton fichier inventory.ini :

[control]
control ansible_connection=local

[webservers]
web1 ansible_host=192.168.100.11
web2 ansible_host=192.168.100.12

[monitoring]
control ansible_connection=local

[all:vars]
ansible_user=sysadmin
ansible_ssh_private_key_file=~/.ssh/id_rsa

Ton environnement multi-serveurs est pret. Tu as 3 VMs qui communiquent entre elles et un Ansible fonctionnel capable de les gerer toutes en une commande. Dans la prochaine etape, tu vas deployer Nginx manuellement sur un serveur pour ressentir la douleur de la repetition.

Deployer Nginx Manuellement et Constater le Probleme

Dans l'etape precedente, tu as prepare un environnement multi-serveurs avec 3 VMs connectees entre elles. Tu peux maintenant deployer un service web pour donner un role concret a tes serveurs.

Mais comment deployer un serveur web sur plusieurs machines ? Dans cette etape, tu vas installer Nginx manuellement sur un seul serveur pour ressentir la douleur de la repetition. C'est cette limitation qui justifie l'automatisation dans l'etape suivante.

In this step, get ready to:
  • Installer et configurer Nginx sur web1 en SSH.
  • Constater les limites de l'approche manuelle pour la gestion multi-serveurs.
Se connecter a web1 et installer Nginx

Ton serveur web1 est vide pour l'instant. Tu vas t'y connecter en SSH depuis la VM control et installer Nginx manuellement, comme un sysadmin le ferait sur un seul serveur.

  • Connecte-toi a web1 depuis la VM control en executant cette commande :
ssh web1
  • Installe Nginx en executant cette commande :
sudo dnf install nginx -y

Tu devrais voir une serie de lignes de telechargement suivies de Complete! a la fin.

L'installation echoue ?

Verifie que ta VM a un acces internet (mode NAT dans VMware). Teste avec ping 8.8.8.8. Si le ping echoue, verifie la configuration reseau de ta VM dans VMware.

Aide-moi a diagnostiquer mon probleme d'installation

  • Active et demarre Nginx, puis ouvre le port HTTP dans le firewall en executant ces commandes :
sudo systemctl enable --now nginx
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload

Que font ces commandes ?

  • enable --now nginx active Nginx au demarrage ET le lance immediatement.
  • firewall-cmd --permanent --add-service=http autorise le trafic HTTP (port 80) de facon permanente dans firewalld.
  • firewall-cmd --reload recharge les regles du firewall pour appliquer le changement.

Avant de verifier le resultat, a ton avis, que va afficher un curl vers web1 sur le port 80 ?

  • Verifie que Nginx repond en executant cette commande (toujours depuis web1) :
curl http://localhost

Tu devrais voir du HTML contenant "Welcome to nginx!" confirmant que le serveur web est operationnel.

  • Deconnecte-toi de web1 pour revenir sur la VM control :
exit
  • Depuis la VM control, confirme que la page est accessible a distance en executant :
curl http://192.168.100.11

Tu devrais voir le meme HTML "Welcome to nginx!" depuis la machine control. Ton serveur web1 sert des pages a travers le reseau.

Curl retourne connection refused ?

Verifie que le service nginx tourne avec ssh web1 'systemctl status nginx'. Si le statut est inactive, relance-le avec ssh web1 'sudo systemctl start nginx'.

Si nginx est actif mais que curl echoue, le firewall bloque probablement le trafic. Verifie avec ssh web1 'sudo firewall-cmd --list-services' et confirme que http apparait dans la liste.

Aide-moi a diagnostiquer pourquoi curl echoue

Constater le double probleme

web1 fonctionne. Maintenant, pense a ce qu'il faudrait faire pour web2.

  • Essaie d'acceder a web2 pour constater qu'il n'a rien :
curl http://192.168.100.12

Tu obtiens une erreur de connexion refusee. web2 n'a pas de serveur web. Pour le configurer, il faudrait refaire exactement les memes 4 commandes que sur web1.

Pourquoi c'est un probleme ?

Pour 2 serveurs, repeter les commandes est agacant. Pour 10 serveurs, c'est une perte de temps. Pour 100 serveurs, c'est tout simplement impossible a maintenir. Chaque etape manuelle est une opportunite d'erreur humaine.

Il y a un deuxieme probleme : tu n'as aucun moyen de savoir si web1 est en bonne sante sans te connecter en SSH. Pas de metriques CPU, pas de suivi memoire, pas d'alerte si le serveur tombe. C'est le double probleme que tu vas resoudre : la repetition manuelle ET le manque de visibilite.

Tu as un serveur web fonctionnel sur web1, mais zero visibilite sur sa sante, et web2 reste vide. Dans l'etape suivante, tu vas automatiser tout ca avec Ansible : un seul playbook deployera Nginx sur les deux serveurs en quelques secondes.

Automatiser le Deploiement avec Ansible

Dans l'etape precedente, tu as installe Nginx manuellement sur web1 et constate que web2 reste vide. Pour deployer la meme configuration sur web2, il faudrait refaire chaque commande a la main.

Ansible elimine cette repetition. Tu vas creer un playbook qui deploie Nginx ET un agent de monitoring sur tous tes serveurs en une seule commande. C'est exactement ce que les recruteurs attendent d'un candidat RHCE.

In this step, get ready to:
  • Creer la structure du projet Ansible avec un role webserver qui deploie Nginx.
  • Ajouter un role monitoring_agent qui installe node_exporter en service systemd.
  • Verifier que les metriques sont exposees sur chaque serveur.
Creer la structure Ansible et deployer Nginx

Un projet Ansible s'organise en roles. Chaque role regroupe les taches liees a une responsabilite (serveur web, agent de monitoring, etc.).

  • Depuis la VM control, cree la structure de repertoires du projet en executant cette commande :
cd ~/monitoring
mkdir -p roles/{webserver,monitoring_agent,prometheus,grafana}/tasks
  • Verifie que les repertoires existent en executant :
ls roles/

Tu devrais voir quatre repertoires : grafana, monitoring_agent, prometheus, et webserver.

  • Cree le fichier roles/webserver/tasks/main.yml en executant cette commande :
cat > roles/webserver/tasks/main.yml << 'EOF'
---
- name: Install Nginx
  ansible.builtin.dnf:
    name: nginx
    state: present

- name: Start and enable Nginx
  ansible.builtin.systemd:
    name: nginx
    state: started
    enabled: true

- name: Open HTTP in firewalld
  ansible.posix.firewalld:
    service: http
    permanent: true
    state: enabled
    immediate: true
EOF

Que fait ce role ?

  • La tache Install Nginx utilise le module dnf pour installer le package sur chaque serveur du groupe.
  • La tache Start and enable Nginx demarre le service et l'active au boot avec systemd.
  • La tache Open HTTP in firewalld ouvre le port 80 dans le firewall pour que les clients puissent acceder au serveur web.
  • Cree le fichier site.yml qui orchestre le deploiement en executant :
cat > site.yml << 'EOF'
---
- name: Deploy webservers
  hosts: webservers
  become: true
  roles:
    - webserver
EOF

Que fait site.yml ?

C'est le playbook principal. Il cible le groupe webservers defini dans ton inventaire (web1 et web2) et applique le role webserver sur chacun.

L'option become: true signifie qu'Ansible execute les taches en tant que root via sudo.

Avant de lancer le playbook, a ton avis, est-ce que web2 aura Nginx apres l'execution ?

  • Lance le playbook en executant :
ansible-playbook -i inventory.ini site.yml

Le playbook s'execute sur web1 et web2 simultanement. Tu devrais voir les taches passer en vert (ok) ou jaune (changed) pour chaque serveur.

  • Verifie que web2 sert maintenant Nginx en executant :
curl -s http://192.168.100.12 | head -5

Tu devrais voir les premieres lignes du HTML de la page par defaut Nginx. Une seule commande a configure les deux serveurs de maniere identique.

Le playbook echoue ?

  • Verifie que tu es bien dans le repertoire ~/monitoring avec pwd.
  • Confirme que la connectivite SSH fonctionne avec ansible all -m ping -i inventory.ini.
  • Si tu vois une erreur sur ansible.posix.firewalld, installe la collection manquante avec ansible-galaxy collection install ansible.posix.

Aide-moi a diagnostiquer cette erreur Ansible

Ajouter le role monitoring_agent

Nginx fonctionne sur les deux serveurs, mais tu n'as aucune visibilite sur leur sante. Le role monitoring_agent installe node_exporter, un agent qui expose les metriques systeme (CPU, RAM, disque, reseau) sur le port 9100.

  • Cree la premiere partie du fichier roles/monitoring_agent/tasks/main.yml en executant :
cat > roles/monitoring_agent/tasks/main.yml << 'EOF'
---
- name: Create node_exporter user
  ansible.builtin.user:
    name: node_exporter
    shell: /sbin/nologin
    system: true
    create_home: false

- name: Download node_exporter 1.12.1
  ansible.builtin.get_url:
    url: https://github.com/prometheus/node_exporter/releases/download/v1.12.1/node_exporter-1.12.1.linux-amd64.tar.gz
    dest: /tmp/node_exporter-1.12.1.linux-amd64.tar.gz
    mode: '0644'

- name: Extract node_exporter
  ansible.builtin.unarchive:
    src: /tmp/node_exporter-1.12.1.linux-amd64.tar.gz
    dest: /tmp/
    remote_src: true

- name: Copy node_exporter binary
  ansible.builtin.copy:
    src: /tmp/node_exporter-1.12.1.linux-amd64/node_exporter
    dest: /usr/local/bin/node_exporter
    owner: node_exporter
    group: node_exporter
    mode: '0755'
    remote_src: true
EOF

Que fait cette premiere partie ?

  • Un utilisateur systeme node_exporter est cree sans shell ni home directory, par securite.
  • Le binaire node_exporter version 1.12.1 est telecharge depuis GitHub, extrait, et copie dans /usr/local/bin/.
  • C'est l'installation recommandee par Prometheus pour les systemes RHEL, car il n'existe pas de RPM officiel.
  • Ajoute les taches restantes (service systemd et firewall) au fichier en executant :
cat >> roles/monitoring_agent/tasks/main.yml << 'EOF'

- name: Create node_exporter systemd service
  ansible.builtin.copy:
    dest: /etc/systemd/system/node_exporter.service
    content: |
      [Unit]
      Description=Prometheus Node Exporter
      After=network-online.target
      Wants=network-online.target

      [Service]
      User=node_exporter
      Group=node_exporter
      Type=simple
      ExecStart=/usr/local/bin/node_exporter

      [Install]
      WantedBy=multi-user.target
    mode: '0644'

- name: Start and enable node_exporter
  ansible.builtin.systemd:
    name: node_exporter
    state: started
    enabled: true
    daemon_reload: true

- name: Open port 9100 in firewalld
  ansible.posix.firewalld:
    port: 9100/tcp
    permanent: true
    state: enabled
    immediate: true
EOF

Que fait cette seconde partie ?

  • Un fichier unit systemd est cree pour que node_exporter demarre automatiquement au boot et redemarre en cas de crash.
  • Le service est demarre immediatement et active au boot.
  • Le port 9100 est ouvert dans firewalld pour que Prometheus puisse collecter les metriques plus tard.
  • Maintenant, mets a jour site.yml pour inclure le nouveau role et preparer la structure pour les prochaines etapes :
cat > site.yml << 'EOF'
---
- name: Deploy webservers
  hosts: webservers
  become: true
  roles:
    - webserver
    - monitoring_agent

- name: Deploy monitoring stack
  hosts: monitoring
  become: true
  roles:
    - monitoring_agent
    - prometheus
    - grafana
EOF

Pourquoi deux plays ?

Le premier play deploie Nginx et l'agent de monitoring sur les serveurs web. Le second play deploie l'agent de monitoring plus Prometheus et Grafana sur la machine control.

Les roles prometheus et grafana n'existent pas encore. Tu vas creer des fichiers placeholder pour qu'Ansible ne bloque pas en attendant de les remplir dans les prochaines etapes.

  • Cree les fichiers placeholder pour les roles prometheus et grafana en executant :
echo "---" > roles/prometheus/tasks/main.yml
echo "---" > roles/grafana/tasks/main.yml
  • Lance le playbook complet en executant :
ansible-playbook -i inventory.ini site.yml

Cette fois, tu devrais voir les taches de node_exporter s'executer en jaune (changed) sur web1 et web2. Le play monitoring stack s'execute aussi mais ne fait rien (roles vides).

Erreur sur le telechargement de node_exporter ?

  • Verifie que tes VMs ont acces a Internet (mode NAT dans VMware). Teste avec ansible webservers -m command -a 'curl -I https://github.com' -i inventory.ini.
  • Si tu vois une erreur de certificat SSL, verifie que ca-certificates est installe sur les VMs cibles.

Mon playbook echoue au telechargement de node_exporter

Verifier que les metriques sont exposees

Les serveurs web1 et web2 executent maintenant node_exporter. Cet agent expose des centaines de metriques systeme sur le port 9100, dans un format que Prometheus sait lire.

A ton avis, que va retourner un curl sur le port 9100 de web1 ?

  • Teste l'exposition des metriques en executant :
curl -s http://192.168.100.11:9100/metrics | head -20

Tu devrais voir des lignes commencant par node_ suivies de valeurs numeriques. Ce sont les metriques CPU, memoire, disque et reseau de web1.

  • Verifie aussi web2 en executant :
curl -s http://192.168.100.12:9100/metrics | head -20

Les deux serveurs exposent leurs metriques. Mais personne ne les collecte encore.

Curl ne repond pas ou connexion refusee ?

  • Verifie que node_exporter tourne sur le serveur cible : ssh web1 'systemctl status node_exporter'.
  • Verifie que le port 9100 est ouvert dans le firewall : ssh web1 'sudo firewall-cmd --list-ports'.
  • Si le service a echoue, consulte les logs avec ssh web1 'journalctl -u node_exporter --no-pager -n 20'.

node_exporter ne repond pas sur le port 9100

✔️ Awesome, I've got everything!

Parfait. Verifie que tu as bien sauvegarde tous les fichiers dans ~/monitoring/.

ⓧ I'd like to double check the full code

Voici le contenu complet de chaque fichier a ce stade.

roles/webserver/tasks/main.yml :

---
- name: Install Nginx
  ansible.builtin.dnf:
    name: nginx
    state: present

- name: Start and enable Nginx
  ansible.builtin.systemd:
    name: nginx
    state: started
    enabled: true

- name: Open HTTP in firewalld
  ansible.posix.firewalld:
    service: http
    permanent: true
    state: enabled
    immediate: true

site.yml :

---
- name: Deploy webservers
  hosts: webservers
  become: true
  roles:
    - webserver
    - monitoring_agent

- name: Deploy monitoring stack
  hosts: monitoring
  become: true
  roles:
    - monitoring_agent
    - prometheus
    - grafana

roles/monitoring_agent/tasks/main.yml : ce fichier contient 7 taches. Fais defiler vers le haut et confirme que tu as bien dans l'ordre :

  • Create node_exporter user (user)
  • Download node_exporter 1.12.1 (get_url)
  • Extract node_exporter (unarchive)
  • Copy node_exporter binary (copy)
  • Create node_exporter systemd service (copy avec le contenu du fichier unit)
  • Start and enable node_exporter (systemd)
  • Open port 9100 in firewalld (firewalld)

Tes serveurs exposent des metriques, mais personne ne les collecte. Dans la prochaine etape, tu vas deployer Prometheus pour centraliser la collecte et interroger les donnees de tous tes serveurs depuis un seul point.

Deployer le Serveur de Monitoring Prometheus

Tes serveurs web exposent des metriques sur le port 9100 grace a node_exporter. Le probleme : personne ne collecte ces donnees.

Sans systeme centralise, tu devrais te connecter a chaque serveur individuellement pour verifier son etat. Ca ne passe pas a l'echelle. Dans cette etape, tu vas deployer Prometheus sur la machine control pour collecter automatiquement les metriques de tous tes serveurs.

In this step, get ready to:
  • Creer un role Ansible qui installe et configure Prometheus 3.13.1 avec ses scrape targets.
  • Lancer le playbook complet et verifier que Prometheus collecte les metriques des 3 serveurs.
  • Tester une requete PromQL pour visualiser les metriques CPU.
Creer le role Ansible prometheus

Le role prometheus suit le meme pattern que monitoring_agent : telecharger le binaire, creer un utilisateur dedie, configurer le service systemd.

  • Cree la structure de repertoires pour le role prometheus en executant cette commande :
mkdir -p roles/prometheus/tasks
  • Cree egalement un placeholder pour le role grafana (qui sera complete a l'etape suivante) afin que le playbook ne bloque pas :
mkdir -p roles/grafana/tasks && echo "---" > roles/grafana/tasks/main.yml
  • Verifie que les repertoires existent en executant :
ls roles/

Tu devrais voir grafana, monitoring_agent, prometheus, et webserver dans la sortie.

  • Cree le fichier roles/prometheus/tasks/main.yml et commence par les taches de preparation (utilisateur, repertoires, telechargement) :
---
- name: Create prometheus user
  ansible.builtin.user:
    name: prometheus
    shell: /sbin/nologin
    system: true
    create_home: false

- name: Create prometheus directories
  ansible.builtin.file:
    path: "{{ item }}"
    state: directory
    owner: prometheus
    group: prometheus
    mode: '0755'
  loop:
    - /etc/prometheus
    - /var/lib/prometheus

- name: Download Prometheus 3.13.1
  ansible.builtin.get_url:
    url: https://github.com/prometheus/prometheus/releases/download/v3.13.1/prometheus-3.13.1.linux-amd64.tar.gz
    dest: /tmp/prometheus-3.13.1.linux-amd64.tar.gz
    mode: '0644'

- name: Extract Prometheus
  ansible.builtin.unarchive:
    src: /tmp/prometheus-3.13.1.linux-amd64.tar.gz
    dest: /tmp/
    remote_src: true

Que font ces taches ?

  • Un utilisateur systeme prometheus sans shell de login est cree pour faire tourner le service sans privileges root.
  • Deux repertoires sont crees : /etc/prometheus pour la configuration et /var/lib/prometheus pour le stockage des donnees.
  • L'archive officielle Prometheus 3.13.1 est telechargee et extraite dans /tmp/.
  • A la suite dans le meme fichier, ajoute les taches d'installation des binaires et des fichiers console :
- name: Copy Prometheus binaries
  ansible.builtin.copy:
    src: "/tmp/prometheus-3.13.1.linux-amd64/{{ item }}"
    dest: "/usr/local/bin/{{ item }}"
    owner: prometheus
    group: prometheus
    mode: '0755'
    remote_src: true
  loop:
    - prometheus
    - promtool

- name: Copy console files
  ansible.builtin.copy:
    src: "/tmp/prometheus-3.13.1.linux-amd64/{{ item }}"
    dest: "/etc/prometheus/{{ item }}"
    owner: prometheus
    group: prometheus
    remote_src: true
  loop:
    - consoles
    - console_libraries

Que font ces taches ?

Les binaires prometheus (le serveur) et promtool (outil de validation de config) sont copies dans /usr/local/bin/. Les fichiers console fournissent des templates web pour l'interface Prometheus.

  • Sauvegarde le fichier et verifie qu'il est bien ecrit en executant :
head -20 roles/prometheus/tasks/main.yml

Tu devrais voir les premieres taches YAML (Create prometheus user, Create prometheus directories) dans la sortie.

  • Rouvre roles/prometheus/tasks/main.yml et ajoute a la suite la configuration de Prometheus avec les scrape targets :
- name: Create Prometheus configuration
  ansible.builtin.copy:
    dest: /etc/prometheus/prometheus.yml
    owner: prometheus
    group: prometheus
    mode: '0644'
    content: |
      global:
        scrape_interval: 15s
        evaluation_interval: 15s

      scrape_configs:
        - job_name: 'prometheus'
          static_configs:
            - targets: ['localhost:9090']

        - job_name: 'node'
          static_configs:
            - targets:
                - 'localhost:9100'
                - '192.168.100.11:9100'
                - '192.168.100.12:9100'

Que definit cette configuration ?

Le fichier prometheus.yml definit deux jobs de scraping. Le job prometheus collecte les metriques internes de Prometheus lui-meme.

Le job node cible les 3 instances node_exporter : la machine control (localhost:9100), web1 (192.168.100.11:9100) et web2 (192.168.100.12:9100). Prometheus viendra scraper ces endpoints toutes les 15 secondes.

  • Toujours dans le meme fichier, ajoute la creation du service systemd :
- name: Create Prometheus systemd service
  ansible.builtin.copy:
    dest: /etc/systemd/system/prometheus.service
    mode: '0644'
    content: |
      [Unit]
      Description=Prometheus Monitoring System
      After=network-online.target
      Wants=network-online.target

      [Service]
      User=prometheus
      Group=prometheus
      Type=simple
      ExecStart=/usr/local/bin/prometheus \
        --config.file=/etc/prometheus/prometheus.yml \
        --storage.tsdb.path=/var/lib/prometheus/ \
        --web.console.templates=/etc/prometheus/consoles \
        --web.console.libraries=/etc/prometheus/console_libraries
      Restart=always

      [Install]
      WantedBy=multi-user.target

Que fait ce service systemd ?

Le service lance Prometheus avec l'utilisateur dedie prometheus. Les flags --config.file et --storage.tsdb.path pointent vers les repertoires crees plus haut.

La directive Restart=always garantit que Prometheus redemarre automatiquement en cas de crash.

  • Termine le fichier en ajoutant les taches de demarrage du service et d'ouverture du firewall :
- name: Start and enable Prometheus
  ansible.builtin.systemd:
    name: prometheus
    state: started
    enabled: true
    daemon_reload: true

- name: Open port 9090 in firewalld
  ansible.posix.firewalld:
    port: 9090/tcp
    permanent: true
    state: enabled
    immediate: true

Que font ces dernieres taches ?

Le service Prometheus est demarre et active au boot avec daemon_reload: true pour prendre en compte le nouveau fichier unit.

Le port 9090 est ouvert dans firewalld pour que l'interface web de Prometheus soit accessible.

  • Sauvegarde roles/prometheus/tasks/main.yml.
  • Verifie que le fichier est complet en executant :
wc -l roles/prometheus/tasks/main.yml

Tu devrais voir environ 95 lignes (le nombre exact peut varier selon les lignes vides).

✔️ Awesome, I've got everything!

Parfait. Verifie bien que tu as sauvegarde le fichier avant de continuer.

ⓧ I'd like to double check the full code

Le fichier complet contient les sections suivantes, dans cet ordre. Remonte dans les blocs de code ci-dessus et confirme que chacune est presente :

  • Create prometheus user (utilisateur systeme).
  • Create prometheus directories (repertoires /etc/prometheus et /var/lib/prometheus).
  • Download Prometheus 3.13.1 et Extract Prometheus (telechargement et extraction).
  • Copy Prometheus binaries et Copy console files (installation des binaires).
  • Create Prometheus configuration (scrape targets pour localhost, web1, web2).
  • Create Prometheus systemd service (fichier unit avec ExecStart).
  • Start and enable Prometheus et Open port 9090 in firewalld (demarrage et firewall).

Erreur de syntaxe YAML ?

Verifie l'indentation : chaque niveau dans YAML est de 2 espaces. Les taches commencent par - name: au meme niveau. La section content: | doit etre suivie de lignes indentees d'au moins 6 espaces supplementaires par rapport a content.

Tu peux valider la syntaxe avec ansible-playbook --syntax-check -i inventory.ini site.yml.

help me fix my YAML syntax in my Ansible prometheus role

Lancer le playbook et verifier dans Prometheus

Le role est pret. Il est temps de deployer Prometheus sur la machine control et de voir la collecte de metriques en action.

  • Avant de lancer le playbook, a ton avis : combien de targets Prometheus va-t-il trouver, et pourquoi ?
  • Lance le playbook complet en executant :
ansible-playbook -i inventory.ini site.yml

Le playbook deploie node_exporter et Prometheus sur control, puis Nginx et node_exporter sur web1 et web2. L'execution prend environ 1 a 2 minutes.

Que se passe-t-il pendant l'execution ?

Ansible execute les roles dans l'ordre defini dans site.yml. Le play Deploy monitoring stack applique d'abord monitoring_agent (node_exporter sur control), puis prometheus (le serveur de monitoring), puis grafana (le placeholder vide pour l'instant).

Le play Deploy webservers reapplique Nginx et node_exporter sur web1 et web2. Les taches deja appliquees s'affichent en vert (ok), pas en jaune (changed).

Le playbook echoue sur le role grafana ?

Verifie que tu as bien cree le placeholder avec echo "---" > roles/grafana/tasks/main.yml. Sans ce fichier, Ansible ne trouve pas le role et refuse de demarrer.

help me fix my Ansible playbook failing on a missing grafana role

  • Verifie que Prometheus tourne sur control en executant :
sudo systemctl status prometheus

Tu devrais voir active (running) dans la sortie.

  • Ouvre un navigateur et accede a l'interface Prometheus :
http://192.168.100.10:9090
  • Dans la barre de navigation en haut, clique sur **Status**.
  • Clique sur **Targets** dans le menu deroulant.

Tu devrais voir 3 targets dans le job node avec le statut UP en vert : localhost:9100, 192.168.100.11:9100, et 192.168.100.12:9100. Plus 1 target localhost:9090 pour le job prometheus.

Un target affiche DOWN en rouge ?

Verifie que node_exporter tourne sur le serveur concerne : ssh web1 sudo systemctl status node_exporter. Si le service est arrete, relance-le.

Verifie aussi que le port 9100 est ouvert dans le firewall : ssh web1 sudo firewall-cmd --list-ports.

help me troubleshoot why Prometheus shows a target as DOWN

  • Retourne sur la page principale de Prometheus (clique sur le logo ou sur **Graph** dans la navigation).
  • Dans le champ de requete en haut, tape la requete PromQL suivante et clique sur **Execute** :
node_cpu_seconds_total

Tu vois des dizaines de lignes de metriques CPU provenant des 3 serveurs. Chaque ligne indique le temps CPU cumule par mode (idle, user, system) et par instance.

Qu'est-ce que PromQL ?

PromQL est le langage de requete de Prometheus. Il permet de filtrer, agreger et transformer les metriques collectees. La requete node_cpu_seconds_total retourne toutes les series temporelles de type CPU, triees par instance et par mode.

Prometheus collecte desormais les metriques de toute ton infrastructure. Dans l'etape suivante, tu vas connecter Grafana pour transformer ces donnees brutes en dashboards visuels.

Visualiser avec Grafana et Creer un Dashboard

Prometheus collecte les metriques de tes 3 serveurs, mais son interface web reste rudimentaire. Tu peux lancer des requetes PromQL, mais impossible de construire un dashboard visuel exploitable en un coup d'oeil.

Grafana transforme ces metriques brutes en graphiques temps reel que tu peux montrer en entretien ou presenter a un manager. Dans cette etape, tu vas deployer Grafana via Ansible, connecter Prometheus comme source de donnees, et creer un dashboard complet avec de l'alerting.

In this step, get ready to:
  • Creer un role Ansible qui installe Grafana depuis le depot RPM officiel.
  • Connecter Prometheus comme data source et importer un dashboard communautaire.
  • Configurer une regle d'alerte qui detecte quand un serveur tombe.
Creer le role Ansible Grafana

Grafana n'a pas de RPM dans les depots Rocky Linux par defaut. Le role va ajouter le depot officiel Grafana, importer la cle GPG, puis installer et activer le service.

  • Cree le repertoire du role en executant cette commande :
mkdir -p ~/monitoring/roles/grafana/tasks
  • Confirme que le repertoire existe en executant :
ls ~/monitoring/roles/grafana/tasks

Tu devrais voir le dossier liste sans erreur.

  • Cree le fichier roles/grafana/tasks/main.yml avec la premiere partie du role (configuration du depot) en y ajoutant ce contenu :
---
- name: Add Grafana GPG key
  ansible.builtin.rpm_key:
    key: https://rpm.grafana.com/gpg.key
    state: present

- name: Add Grafana repository
  ansible.builtin.yum_repository:
    name: grafana
    description: Grafana OSS
    baseurl: https://rpm.grafana.com
    repo_gpgcheck: true
    enabled: true
    gpgcheck: true
    gpgkey: https://rpm.grafana.com/gpg.key
    sslverify: true
    sslcacert: /etc/pki/tls/certs/ca-bundle.crt

What does this code do?

  • rpm_key importe la cle GPG du depot Grafana pour que dnf puisse verifier l'authenticite des paquets.
  • yum_repository cree le fichier .repo dans /etc/yum.repos.d/ pointant vers https://rpm.grafana.com.
  • repo_gpgcheck: true verifie aussi la signature du fichier repodata, pas seulement des paquets individuels.
  • Ajoute la suite du role (installation et activation du service) directement en dessous dans le meme fichier :
- name: Install Grafana
  ansible.builtin.dnf:
    name: grafana
    state: present

- name: Start and enable Grafana
  ansible.builtin.systemd:
    name: grafana-server
    state: started
    enabled: true

- name: Open port 3000 in firewalld
  ansible.posix.firewalld:
    port: 3000/tcp
    permanent: true
    state: enabled
    immediate: true

What does this code do?

  • dnf installe le paquet grafana depuis le depot que tu viens de configurer.
  • Le service systemd s'appelle grafana-server (pas grafana). C'est un piege classique.
  • Le port 3000 est le port par defaut de Grafana, ouvert dans firewalld pour permettre l'acces depuis ton navigateur.
  • Sauvegarde le fichier roles/grafana/tasks/main.yml.

Erreur de syntaxe YAML?

Verifie que chaque tache commence par - name: avec exactement 2 espaces d'indentation.

Les attributs de chaque module doivent etre indentes de 4 espaces sous le nom du module.

Aide-moi a trouver mon erreur YAML

✔️ Awesome, I've got everything!

Parfait. Verifie que ton fichier est bien sauvegarde avant de lancer le playbook.

ⓧ I'd like to double check the full code

Ton fichier roles/grafana/tasks/main.yml doit contenir les deux blocs que tu as ajoutes, dans l'ordre :

  • Bloc 1 : les taches Add Grafana GPG key et Add Grafana repository.
  • Bloc 2 : les taches Install Grafana, Start and enable Grafana, et Open port 3000 in firewalld.

Remonte dans la page et compare chaque bloc avec ton fichier.

Ton fichier site.yml inclut deja le role grafana dans le play Deploy monitoring stack. A toi de deployer.

Avant de lancer le playbook, a ton avis, est-ce que Grafana sera accessible immediatement apres l'execution?

  • Deploie toute la stack en executant cette commande :
cd ~/monitoring && ansible-playbook -i inventory.ini site.yml

Le playbook s'execute sur les 3 serveurs. Les taches deja appliquees retournent ok (idempotence Ansible). Les nouvelles taches Grafana retournent changed.

  • Verifie que Grafana repond en executant :
curl -s http://localhost:3000/api/health

Tu devrais voir un JSON contenant "database": "ok". Grafana est operationnel.

Connection refused sur le port 3000?

  • Verifie que le service tourne : sudo systemctl status grafana-server.
  • Verifie que le port est ouvert : sudo firewall-cmd --list-ports. Tu dois voir 3000/tcp.

Aide-moi a diagnostiquer ce probleme

Connecter Prometheus et importer un dashboard

Grafana ne sait pas encore ou trouver tes metriques. Tu vas lui indiquer Prometheus comme source de donnees, puis importer un dashboard pre-construit par la communaute.

  • Ouvre ton navigateur et accede a http://192.168.100.10:3000.
  • Connecte-toi avec le login admin et le mot de passe admin.
  • Grafana te demande de changer le mot de passe. Clique sur Skip pour le moment (environnement de lab).

Pourquoi admin/admin?

Ce sont les identifiants par defaut de Grafana OSS a l'installation. En production, tu changerais immediatement ce mot de passe ou tu configurerais un provider d'authentification externe.

  • Dans le menu de gauche, clique sur Connections.
  • Clique sur Add new connection.
  • Dans la barre de recherche, tape Prometheus et selectionne Prometheus data source.
  • Clique sur Add new data source en haut a droite.
  • Dans le champ Prometheus server URL, entre http://localhost:9090.
  • Descends en bas de la page et clique sur Save & test.

Tu devrais voir un message vert confirmant que la connexion a Prometheus fonctionne.

Pourquoi localhost:9090?

Grafana tourne sur la meme machine (control) que Prometheus. Les deux services communiquent en local sans traverser le reseau.

  • Dans le menu de gauche, clique sur Dashboards.
  • Clique sur New puis selectionne Import dashboard.
  • Dans le champ d'import, entre l'ID 1860 et clique sur Load.
  • Dans le menu deroulant Prometheus en bas du formulaire, selectionne la data source que tu viens de creer.
  • Clique sur Import.

Le dashboard "Node Exporter Full" s'affiche avec les graphiques CPU, RAM, disque et reseau de tes 3 serveurs actualises en temps reel.

Qu'est-ce que le dashboard 1860?

C'est un dashboard communautaire tres populaire sur grafana.com, concu pour afficher toutes les metriques exposees par node_exporter. Il fonctionne immediatement sans configuration supplementaire.

Creer une regle d'alerte

Voir les metriques c'est bien. Etre averti automatiquement quand un serveur tombe, c'est encore mieux. Tu vas creer une alerte qui detecte quand un target Prometheus ne repond plus.

  • Dans le menu de gauche, clique sur Alerting (icone en forme de cloche).
  • Clique sur Alert rules.
  • Clique sur + New alert rule.
  • Dans le champ Rule name, entre InstanceDown.
  • Dans la section query, selectionne ta data source Prometheus et passe en mode code (bouton Code).
  • Entre la requete PromQL : up == 0.
  • Dans la section condition, configure le seuil : si la valeur retournee est superieure a 0 (un target est detecte comme down).
  • Dans Evaluate for, entre 30s pour que l'alerte se declenche apres 30 secondes de non-reponse.
  • Clique sur Save rule and exit.

Comment fonctionne cette alerte?

La metrique up vaut 1 quand Prometheus arrive a scraper un target, et 0 quand le target ne repond pas. La requete up == 0 retourne un resultat uniquement quand un serveur est en panne.

Le Evaluate for: 30s evite les faux positifs. L'alerte ne passe en "Firing" que si le serveur reste injoignable pendant 30 secondes consecutives.

Retourne a la page Alert rules. Tu devrais voir ta regle InstanceDown en etat Normal (vert). Tous tes serveurs repondent.

L'alerte est en etat Pending ou Firing?

  • Si elle est en Firing, un de tes node_exporter ne repond pas. Verifie avec curl http://web1:9100/metrics et curl http://web2:9100/metrics.
  • Si elle est en Pending, attends 30 secondes. Le delai d'evaluation doit s'ecouler avant que l'etat se stabilise.

Aide-moi a diagnostiquer mon alerte

Ton infrastructure de monitoring est complete. Tu as un dashboard Grafana qui affiche les metriques temps reel de tes 3 serveurs, et une alerte qui te previent si un serveur tombe.

Prochaine etape : tu vas tester cette alerte en simulant une vraie panne. C'est exactement ce qu'on te demanderait en entretien.

Secret mission

Simuler une Panne et Observer la Detection

En production, les pannes arrivent sans prevenir. Simule un incident sur ton infrastructure et observe ton systeme de monitoring detecter le probleme automatiquement, exactement comme lors d'une astreinte reelle.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally on VMware Workstation, so there are no ongoing cloud costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • 3 VMs Rocky Linux 9.8 dans VMware Workstation (control, web1, web2).
  • Ansible project files in ~/monitoring/ on the control VM.
  • Prometheus data stored in /var/lib/prometheus/ on the control VM.
  • Grafana dashboards and configuration on the control VM.

Keep everything running

No action needed. Choose this if you're still actively building or plan to use these VMs for follow-up projects (hardening, high availability, etc.).

  • Shut down the VMs when you're not using them to free up RAM. In VMware Workstation, right-click each VM and select Power > Shut Down Guest.
  • Your playbook can restart everything with a single ansible-playbook -i inventory.ini site.yml command whenever you power the VMs back on.
  • The VMs use approximately 50 Go of local disk space total (20 Go + 15 Go + 15 Go).

Pause - I'll come back to this later

Suspend your VMs to free up RAM while keeping their exact state intact. You can resume exactly where you left off.

  • In VMware Workstation, right-click each VM (control, web1, web2) and select Power > Suspend.
  • Suspended VMs conserve their running state. When you resume them, all services (Prometheus, Grafana, Nginx, node_exporter) will still be running.
  • Disk usage stays the same. The suspend files add a small amount per VM (equal to their RAM allocation).

Delete - I don't want to use this again

Remove all VMs and reclaim your disk space. Your Ansible playbook can rebuild everything from scratch if you change your mind later.

  • In VMware Workstation, right-click the control VM and select Manage > Delete from Disk.
  • Repeat for the web1 and web2 VMs.
  • Confirm each deletion when VMware asks. This permanently removes the VM files from your host machine.
  • Save your Ansible playbook files to a Git repository or a folder on your host machine before deleting the control VM. These files are your infrastructure-as-code and can recreate the entire environment.

Keep your playbook safe

Before deleting the control VM, copy the ~/monitoring/ directory to your host machine or push it to a Git remote. With that playbook, you can rebuild the entire monitoring stack on fresh VMs in under 2 minutes.

Nice Work!

Nice Work!

Tu viens de deployer une infrastructure de monitoring multi-serveurs complete, entierement automatisee par Ansible. C'est exactement le type de projet que les recruteurs recherchent pour un poste sysadmin senior.

Ce que tu as accompli :

  • Deploye 3 serveurs Rocky Linux et automatise l'installation de Nginx et node_exporter sur N serveurs avec un seul playbook Ansible.
  • Mis en place un monitoring centralise avec Prometheus qui collecte les metriques CPU, RAM, disque et reseau de tous tes serveurs automatiquement.
  • Cree un dashboard Grafana avec des visualisations temps reel et de l'alerting qui detecte les pannes en moins de 30 secondes.
  • Secret Mission : Simule une panne serveur et observe la detection automatique par Prometheus, exactement comme en production.

Ready to quiz yourself?