Déploiement Grafana/Prometheus
Déploiement Prometheus + Grafana + Exporters - Guide Complet
Date : 28 Novembre 2025
Environnement : AlmaLinux 9, Podman rootless
Infrastructure : VPS OVH avec monitoring complet des services
Table des matières
- Architecture finale
- Stack de monitoring déployée
- Déploiement Prometheus & Grafana
- Configuration des exporters
- Configuration Nginx reverse proxy
- Dashboard Grafana
- Problèmes rencontrés et solutions
- Commandes de maintenance
- Ressources
Architecture finale
┌─────────────────────────────────────────────────────────┐
│ VPS AlmaLinux 9 │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Conteneurs Podman (rootless) │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Prometheus │◄───│ Grafana │ │ │
│ │ │ :9091 │ │ :3000 │ │ │
│ │ └──────┬───────┘ └──────────────┘ │ │
│ │ │ │ │
│ │ ├──► Node Exporter (:9100) │ │
│ │ └──► Autres conteneurs │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Services système (hôte) │ │
│ │ │ │
│ │ • Podman Exporter (:9882) │ │
│ │ • Podman API (socket rootless) │ │
│ │ • Nginx reverse proxy │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Accès externe HTTPS │ │
│ │ │ │
│ │ grafana.sanjyasz.pro → Nginx → Grafana │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
Stack de monitoring déployée
Services déployés
| Service | Type | Port | Description |
|---|---|---|---|
| Prometheus | Conteneur | 9091 | Collecte et stockage des métriques |
| Grafana | Conteneur | 3000 | Visualisation et dashboards |
| Node Exporter | Conteneur | 9100 | Métriques système (CPU, RAM, disque) |
| Podman Exporter | Systemd user | 9882 | Métriques des conteneurs Podman |
| Podman API | Systemd user | Socket | API Podman pour l'exporter |
| Nginx | Système | 80/443 | Reverse proxy HTTPS |
Services monitorés
- Jenkins (conteneur)
- HashiCorp Vault (conteneur)
- BookStack (conteneur)
- Prometheus (auto-monitoring)
- Grafana (auto-monitoring)
- Système hôte (via Node Exporter)
Déploiement Prometheus & Grafana
Structure des fichiers
~/monitoring/
├── docker-compose.yml
├── prometheus/
│ ├── config/
│ │ └── prometheus.yaml
│ └── data/ # Volume Podman
└── grafana/
├── data/ # Volume Podman
└── provisioning/
└── datasources/
└── prometheus.yml
Création de la structure
mkdir -p ~/monitoring/{prometheus/config,grafana/provisioning/datasources}
cd ~/monitoring
Configuration Prometheus
Fichier prometheus/config/prometheus.yaml :
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
# Prometheus auto-monitoring
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# Node Exporter (système)
- job_name: 'node_exporter'
static_configs:
- targets: ['host.containers.internal:9100']
# Podman Exporter (conteneurs)
- job_name: 'podman_exporter'
static_configs:
- targets: ['host.containers.internal:9882']
# Nginx Exporter (optionnel)
- job_name: 'nginx'
static_configs:
- targets: ['host.containers.internal:9113']
Points clés :
- Utilisation de
host.containers.internalau lieu delocalhostpour accéder aux services de l'hôte depuis les conteneurs - Port 9091 exposé sur l'hôte (9090 interne au conteneur)
Configuration datasource Grafana
Fichier grafana/provisioning/datasources/prometheus.yml :
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: false
Note : Utilisation de prometheus:9090 (résolution DNS interne Docker/Podman entre conteneurs du même réseau).
Docker Compose
Fichier docker-compose.yml :
version: '3.8'
services:
# Prometheus
prometheus:
image: docker.io/prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
extra_hosts:
- "host.containers.internal:host-gateway"
volumes:
- ./prometheus/config:/etc/prometheus:Z
- prometheus_data:/prometheus:Z
command:
- '--config.file=/etc/prometheus/prometheus.yaml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d'
ports:
- "9091:9090"
networks:
- monitoring
# Grafana
grafana:
image: docker.io/grafana/grafana:latest
container_name: grafana
restart: unless-stopped
user: "472"
volumes:
- grafana_data:/var/lib/grafana:Z
- ./grafana/provisioning:/etc/grafana/provisioning:Z
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=ChangeMe123!
- GF_SERVER_ROOT_URL=https://grafana.sanjyasz.pro
- GF_SERVER_DOMAIN=grafana.sanjyasz.pro
ports:
- "3000:3000"
networks:
- monitoring
depends_on:
- prometheus
# Node Exporter
node-exporter:
image: docker.io/prom/node-exporter:latest
container_name: node-exporter
restart: unless-stopped
command:
- '--path.rootfs=/host'
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
volumes:
- /:/host:ro,rslave
ports:
- "9100:9100"
networks:
- monitoring
volumes:
prometheus_data:
driver: local
grafana_data:
driver: local
networks:
monitoring:
driver: bridge
Points importants :
extra_hostspour permettre à Prometheus d'accéder aux services de l'hôte:Zsur les volumes pour SELinuxuser: "472"pour Grafana (UID grafana)- Port Prometheus changé en 9091 (9090 était utilisé par systemd)
Fix SELinux
# Appliquer les contextes SELinux
sudo chcon -R -t container_file_t ~/monitoring/
# Ou permanent
sudo semanage fcontext -a -t container_file_t "$HOME/monitoring(/.*)?"
sudo restorecon -Rv ~/monitoring/
Démarrage
cd ~/monitoring
podman-compose up -d
# Vérifier les logs
podman-compose logs -f
# Vérifier que tout tourne
podman ps
Accès
- Prometheus :
http://localhost:9091 - Grafana :
http://localhost:3000(admin / ChangeMe123!)
Configuration des exporters
Node Exporter (conteneur)
Déjà inclus dans docker-compose.yml.
Test :
curl http://localhost:9100/metrics | grep node_
Podman Exporter (service systemd user)
Pourquoi en service systemd et pas en conteneur ?
- Accès au socket Podman rootless difficile depuis un conteneur
- Plus simple et stable en service système
- Évite les problèmes de permissions complexes
Installation
# Installer depuis les dépôts AlmaLinux
sudo dnf install -y prometheus-podman-exporter
Service Podman API (prérequis)
Créer le service pour exposer l'API Podman :
mkdir -p ~/.config/systemd/user/
cat > ~/.config/systemd/user/podman-api.service << 'EOF'
[Unit]
Description=Podman API Service
Documentation=man:podman-system-service(1)
After=network.target
[Service]
Type=exec
KillMode=process
ExecStart=/usr/bin/podman system service --time=0
[Install]
WantedBy=default.target
EOF
# Activer et démarrer
systemctl --user daemon-reload
systemctl --user enable --now podman-api
# Vérifier
systemctl --user status podman-api
# Vérifier que le socket existe
ls -lah /run/user/$(id -u)/podman/podman.sock
Service Podman Exporter
cat > ~/.config/systemd/user/prometheus-podman-exporter.service << EOF
[Unit]
Description=Prometheus Podman Exporter
After=podman-api.service
Requires=podman-api.service
[Service]
Type=simple
Environment="CONTAINER_HOST=unix:///run/user/$(id -u)/podman/podman.sock"
ExecStart=/usr/bin/prometheus-podman-exporter
Restart=always
RestartSec=5
[Install]
WantedBy=default.target
EOF
# Activer et démarrer
systemctl --user daemon-reload
systemctl --user enable --now prometheus-podman-exporter
# Vérifier
systemctl --user status prometheus-podman-exporter
Test
# Métriques disponibles
curl http://localhost:9882/metrics | grep podman_container
# Vérifier dans Prometheus
curl -G http://localhost:9091/api/v1/query \
--data-urlencode 'query=podman_container_info' | jq
Configuration Nginx reverse proxy
DNS OVH
Ajouter l'enregistrement A :
Type Nom Valeur TTL
A grafana <IP_VPS> 3600
Certificat Let's Encrypt
Étendre le certificat existant :
sudo certbot certonly --nginx \
-d sanjyasz.pro \
-d www.sanjyasz.pro \
-d vault.sanjyasz.pro \
-d grafana.sanjyasz.pro \
--expand
Configuration Nginx
Fichier /etc/nginx/conf.d/grafana.conf :
upstream grafana_backend {
server 127.0.0.1:3000;
}
server {
listen 80;
server_name grafana.sanjyasz.pro;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name grafana.sanjyasz.pro;
# SSL
ssl_certificate /etc/letsencrypt/live/sanjyasz.pro/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/sanjyasz.pro/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Headers sécurité
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Logs
access_log /var/log/nginx/grafana-access.log;
error_log /var/log/nginx/grafana-error.log;
# Proxy vers Grafana
location / {
proxy_pass http://grafana_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
Test et activation
# Tester la configuration
sudo nginx -t
# Recharger Nginx
sudo systemctl reload nginx
# Fix SELinux si nécessaire
sudo setsebool -P httpd_can_network_connect 1
# Vérifier
curl -I https://grafana.sanjyasz.pro
Accès final
https://grafana.sanjyasz.pro
Dashboard Grafana
Connexion
- URL :
https://grafana.sanjyasz.pro - Login :
admin - Password :
ChangeMe123!
Dashboard "Services Status" (custom)
Création manuelle :
Query :
podman_container_info{name=~"jenkins|vault|bookstack"} * on(id) group_left(name) podman_container_state
Configuration :
- Visualization type : Stat
- Legend :
{{name}} - Color mode : Background
- Text mode : Value and name
Value mappings :
1→ Text:UP ✓→ Color: Green0→ Text:DOWN ✗→ Color: Red
Résultat : Briques vertes/rouges indiquant l'état de chaque service.
Dashboards communautaires (tentative)
IDs testés mais non fonctionnels :
- 1860 : Node Exporter Full (problème de compatibilité)
- 15798 : Podman Container Metrics (problème de labels)
- 19972 : Podman Exporter (problème de compatibilité)
Raison : Les dashboards communautaires attendent des labels ou métriques spécifiques qui ne correspondent pas exactement à notre configuration.
Solution : Création de dashboards custom adaptés aux métriques disponibles.
Problèmes rencontrés et solutions
Problème 1 : Port 9090 déjà utilisé
Erreur :
Error: unable to start container: rootlessport listen tcp 0.0.0.0:9090: bind: address already in use
Cause : Le port 9090 était utilisé par systemd (service websm).
Solution :
# Vérifier qui utilise le port
sudo lsof -i :9090
# Changer le port de Prometheus dans docker-compose.yml
ports:
- "9091:9090" # Port hôte 9091, conteneur 9090
Problème 2 : Erreur .containerenv manquant
Erreur :
crun: open `/home/almalinux/.local/share/containers/storage/overlay/.../merged/run/.containerenv`:
No such file or directory
Cause : Problème avec le storage overlay de Podman après des suppressions brutales de conteneurs.
Solutions testées :
- Ajout de tmpfs (non retenu - contournement) :
tmpfs:
- /run
- Reset du storage Podman :
podman system prune -a --volumes
- Reboot du serveur (solution finale) :
sudo reboot
Explication : Le reboot nettoie les mounts overlay résiduels et recrée un état propre.
Problème 3 : Prometheus ne trouve pas sa config
Erreur :
Error loading config: open /etc/prometheus/prometheus.yml: no such file or directory
Cause :
- Fichier créé avec extension
.yamlau lieu de.yml - Le volume mount ne fonctionnait pas correctement
Solutions :
# Vérifier que le fichier existe
ls -lah ~/monitoring/prometheus/config/prometheus.yaml
# Fix SELinux
sudo chcon -R -t container_file_t ~/monitoring/prometheus/
# Utiliser chemin absolu dans docker-compose.yml si nécessaire
volumes:
- /home/almalinux/monitoring/prometheus/config:/etc/prometheus:Z
# Ou renommer en .yml
mv prometheus.yaml prometheus.yml
Problème 4 : Podman Exporter - Permission denied sur socket
Erreur :
unable to connect to Podman socket: dial unix /run/user/1000/podman/podman.sock:
connect: permission denied
Causes multiples :
- Socket n'existe pas
- Service en conteneur ne peut pas accéder au socket rootless
- Plusieurs instances en conflit
Diagnostic :
# Vérifier que le socket existe
ls -lah /run/user/$(id -u)/podman/podman.sock
# Vérifier les process en cours
ps aux | grep prometheus-podman-exporter
# Vérifier qui utilise le port
sudo lsof -i :9882
Solution finale : Service systemd user
# Désactiver toutes les instances
sudo systemctl disable --now prometheus-podman-exporter
systemctl --user disable --now prometheus-podman-exporter
sudo pkill -9 -f prometheus-podman-exporter
# Créer le service Podman API
systemctl --user enable --now podman-api
# Créer le service exporter
systemctl --user enable --now prometheus-podman-exporter
# Vérifier qu'il n'y a qu'une instance
ps aux | grep prometheus-podman-exporter | grep -v grep
Pourquoi cette solution ?
- Le service user a accès direct au socket rootless
- Pas de problème de mapping UID/permissions
- Plus stable que l'approche conteneur pour rootless
Problème 5 : Prometheus ne peut pas scraper localhost depuis le conteneur
Erreur : Targets podman_exporter et node_exporter en état "down".
Cause : Prometheus (conteneur) ne peut pas atteindre localhost:9882 (hôte).
Diagnostic :
# Depuis l'hôte, ça marche
curl http://localhost:9882/metrics
# Mais Prometheus (conteneur) ne voit pas localhost de l'hôte
Solution : Utiliser host.containers.internal
# Dans prometheus.yaml
- job_name: 'podman_exporter'
static_configs:
- targets: ['host.containers.internal:9882']
# Dans docker-compose.yml
prometheus:
extra_hosts:
- "host.containers.internal:host-gateway"
Résultat : Tous les targets passent à "up" ✅
Problème 6 : Labels name manquants dans podman_container_state
Observation :
podman_container_state{id="abc123", ...} 1
Pas de label name !
Cause : Podman Exporter ne retourne pas toujours tous les labels dans toutes les métriques.
Solution : Utiliser podman_container_info avec join
# Query qui combine les deux métriques
podman_container_info * on(id) group_left(name) podman_container_state
Résultat :
{name="jenkins", id="...", state="running"} 1
{name="vault", id="...", state="running"} 1
Problème 7 : Dashboards communautaires vides
Cause : Les dashboards attendent des métriques ou labels spécifiques qui ne correspondent pas exactement.
Solution : Créer des dashboards custom adaptés aux métriques réellement disponibles.
Méthode :
- Tester les queries dans Explore
- Identifier les labels disponibles
- Créer les panels manuellement
- Adapter les value mappings
Commandes de maintenance
Gestion des services
# Status de tous les services
podman ps
systemctl --user status podman-api
systemctl --user status prometheus-podman-exporter
# Restart complet du stack
cd ~/monitoring
podman-compose restart
# Logs
podman-compose logs -f prometheus
podman-compose logs -f grafana
journalctl --user -u prometheus-podman-exporter -f
Vérification des métriques
# Node Exporter
curl http://localhost:9100/metrics | grep node_cpu
# Podman Exporter
curl http://localhost:9882/metrics | grep podman_container
# Prometheus targets
curl http://localhost:9091/api/v1/targets | jq '.data.activeTargets[] | {job: .labels.job, health: .health}'
# Query spécifique
curl -G http://localhost:9091/api/v1/query \
--data-urlencode 'query=podman_container_info' | jq
Reload configuration Prometheus
# Sans restart
podman exec prometheus kill -HUP 1
# Ou restart complet
podman restart prometheus
Backup
# Backup volumes
podman volume export prometheus_data > prometheus-backup.tar
podman volume export grafana_data > grafana-backup.tar
# Backup configs
tar -czf monitoring-config.tar.gz ~/monitoring/
Restore
# Restore volume
podman volume create prometheus_data
cat prometheus-backup.tar | podman volume import prometheus_data -
Ressources
Documentation
- Prometheus : https://prometheus.io/docs/
- Grafana : https://grafana.com/docs/
- Node Exporter : https://github.com/prometheus/node_exporter
- Podman Exporter : https://github.com/containers/prometheus-podman-exporter
Queries PromQL utiles
Nombre de conteneurs :
count(podman_container_info)
Conteneurs par état :
sum by (state) (podman_container_state)
CPU usage par conteneur :
rate(podman_container_cpu_seconds_total[5m])
Memory usage :
podman_container_mem_usage_bytes
System CPU usage :
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
System memory usage :
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
Disk usage :
100 - ((node_filesystem_avail_bytes{mountpoint="/",fstype!="rootfs"} / node_filesystem_size_bytes{mountpoint="/",fstype!="rootfs"}) * 100)
Dashboards Grafana recommandés
Pour création manuelle :
- System Overview : CPU, RAM, Disk, Network
- Container Status : État des conteneurs critiques
- Container Resources : CPU/RAM par conteneur
- Alerts : Services down, seuils dépassés
Configuration réseau complète
Ports utilisés :
80 HTTP (redirect HTTPS)
443 HTTPS (Nginx)
3000 Grafana (localhost)
8080 Nginx status (localhost)
9091 Prometheus (localhost)
9100 Node Exporter (localhost)
9113 Nginx Exporter (localhost, optionnel)
9882 Podman Exporter (localhost)
Firewall nftables :
# Monitoring (localhost only)
tcp dport { 3000, 9091, 9100, 9882 } ip saddr 127.0.0.1 accept
# HTTPS public
tcp dport { 80, 443 } accept
Checklist de vérification
- Tous les conteneurs UP :
podman ps - Services systemd actifs :
systemctl --user status podman-api prometheus-podman-exporter - Socket Podman existe :
ls /run/user/$(id -u)/podman/podman.sock - Prometheus targets UP :
curl http://localhost:9091/api/v1/targets | jq - Grafana accessible :
https://grafana.sanjyasz.pro - Dashboard services fonctionne
- Certificat SSL valide
- Logs propres :
podman-compose logs --tail=50
Conclusion
Infrastructure monitoring complète déployée avec succès.
Points clés :
- ✅ Prometheus + Grafana en conteneurs Podman rootless
- ✅ Node Exporter pour métriques système
- ✅ Podman Exporter en service systemd user (solution stable pour rootless)
- ✅ Reverse proxy Nginx avec SSL Let's Encrypt
- ✅ Dashboard custom pour monitoring des services critiques
- ✅ Accès sécurisé via
https://grafana.sanjyasz.pro
Stack complètement opérationnelle et prête pour la production.
Prochaines étapes possibles :
- Ajouter Alertmanager pour les notifications
- Configurer des alertes (services down, seuils CPU/RAM)
- Ajouter Nginx Exporter pour monitoring web
- Créer des dashboards additionnels
- Automatiser les backups Grafana/Prometheus