# Déploiement Grafana/Prometheus

# Déploiement Prometheus + Grafana + Exporters - Guide Complet

**Date :** 28 Novembre 2025  
**Environnement :** AlmaLinux 10, Podman rootless  
**Infrastructure :** VPS OVH avec monitoring complet des services

---

## Table des matières

1. [Architecture finale](#architecture-finale)
2. [Stack de monitoring déployée](#stack-de-monitoring-déployée)
3. [Déploiement Prometheus & Grafana](#déploiement-prometheus--grafana)
4. [Configuration des exporters](#configuration-des-exporters)
5. [Configuration Nginx reverse proxy](#configuration-nginx-reverse-proxy)
6. [Dashboard Grafana](#dashboard-grafana)
7. [Problèmes rencontrés et solutions](#problèmes-rencontrés-et-solutions)
8. [Commandes de maintenance](#commandes-de-maintenance)
9. [Ressources](#ressources)

---

## Architecture finale
```
┌─────────────────────────────────────────────────────────┐
│                    VPS AlmaLinux 10                     │
│                                                          │
│  ┌──────────────────────────────────────────────────┐   │
│  │           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.sanjy.fr → 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
```bash
~/monitoring/
├── docker-compose.yml
├── prometheus/
│   ├── config/
│   │   └── prometheus.yaml
│   └── data/                    # Volume Podman
└── grafana/
    ├── data/                    # Volume Podman
    └── provisioning/
        └── datasources/
            └── prometheus.yml
```

### Création de la structure
```bash
mkdir -p ~/monitoring/{prometheus/config,grafana/provisioning/datasources}
cd ~/monitoring
```

### Configuration Prometheus

**Fichier `prometheus/config/prometheus.yaml` :**
```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.internal` au lieu de `localhost` pour 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` :**
```yaml
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` :**
```yaml
version: '3.8'

services:
  prometheus:
    image: docker.io/prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    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:
    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= ******
      - GF_SERVER_ROOT_URL=https://grafana.sanjy.fr
      - GF_SERVER_DOMAIN=grafana.sanjy.fr
    ports:
      - "3000:3000"
    networks:
      - monitoring
    depends_on:
      - prometheus

  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:
  grafana_data:

networks:
  monitoring:
    driver: bridge
```

**Points importants :**
- `:Z` sur les volumes pour SELinux
- `user: "472"` pour Grafana (UID grafana)
- Port Prometheus changé en 9091 (9090 était utilisé par systemd)

### Fix SELinux
```bash
# 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
```bash
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 :**
```bash
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

La vérité vraie: j'ai juste pas réussi à faire marcher l'image prometheus-podman-exporter :D Si quelqu'un a déjà réussi à la faire marcher qu'il me dise comment svp.

#### Installation
```bash
# 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 :**
```bash
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
```bash
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
```bash
# 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 :**
```bash
sudo certbot certonly --nginx \
  -d sanjy.fr \
  -d www.sanjy.fr \
  -d vault.sanjy.fr \
  -d grafana.sanjy.fr \
  --expand
```

### Configuration Nginx

**Fichier `/etc/nginx/conf.d/grafana.conf` :**
```nginx
server {
    listen 80;
    server_name grafana.sanjy.fr;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name grafana.sanjy.fr;

    # SSL
    ssl_certificate /etc/letsencrypt/live/sanjy.fr/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sanjy.fr/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://127.0.0.1:3000;
        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
```bash
# 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.sanjy.fr
```

### Accès final
```
https://grafana.sanjy.fr
```

---

## Dashboard Grafana

### Connexion

- **URL :** `https://grafana.sanjy.fr`
- **Login :** `admin`
- **Password :** `password_de_zinzin!`

### Dashboard "Services Status" (custom)

**Création manuelle :**

1. **Menu → Dashboards → New Dashboard**
2. **Add visualization**
3. **Select Prometheus datasource**

**Query :**
```promql
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: Green
- `0` → 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 à la configuration.

**Solution :** Création de dashboards custom adaptés aux métriques disponibles.

---

## 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.sanjy.fr`

**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