mirror of
https://github.com/Alkatrazz24/Funk-lab.git
synced 2026-07-08 23:44:41 +02:00
Ajout d'une section '## État vérifié' à chaque doc admin/ basée sur l'inspection read-only du cluster (systemctl, kubectl, nft, etc.). Aucune modification de service ni réécriture de documentation existante. Co-Authored-By: Hermes <hermes@funk.lab>
424 lines
No EOL
15 KiB
Markdown
424 lines
No EOL
15 KiB
Markdown
# Monitoring — Grafana + Prometheus + AlertManager
|
||
|
||
Stack déployée via ArgoCD dans le namespace `monitoring`.
|
||
Chart : `kube-prometheus-stack` v85.0.2 (Prometheus Operator).
|
||
Dashboards et alertes custom versionnés dans `k8s/infra/monitoring/`.
|
||
|
||
---
|
||
|
||
## Accès et comptes
|
||
|
||
| Service | URL | Login | Mot de passe |
|
||
|---|---|---|---|
|
||
| **Grafana** | http://grafana.lab.local | `admin` | `funk-grafana` |
|
||
| **Prometheus** | http://prometheus.lab.local | — | — |
|
||
| **AlertManager** | http://alertmanager.lab.local | — | — |
|
||
|
||
> DNS `*.lab.local` ne résout que depuis le LAN cluster (192.168.10.0/24).
|
||
> Depuis le poste perso (192.168.1.x), ajouter dans `/etc/hosts` :
|
||
> ```
|
||
> 192.168.10.200 grafana.lab.local argocd.lab.local prometheus.lab.local alertmanager.lab.local
|
||
> ```
|
||
|
||
---
|
||
|
||
## Architecture
|
||
|
||
```
|
||
Prometheus (k8s, namespace monitoring)
|
||
├── kube-state-metrics → ressources k8s (pods, deployments, nodes)
|
||
├── node-exporter (DaemonSet) → métriques système compute-01/02/03
|
||
├── scrape storage-01:9100 → métriques système storage-01 (job=storage-01)
|
||
├── scrape gpu-01:9100 → métriques système + ROCm gpu-01 (job=gpu-01-node)
|
||
│ └── textfile_collector → rocm_gpu_temperature/utilization/vram (rocm_scraper.timer)
|
||
├── scrape gpu-01:1234 → llama-server GPU Qwen3-8B (job=llama-server-gpu)
|
||
├── scrape gpu-01:1236 → llama-server CPU system (job=llama-server-system)
|
||
└── scrape gpu-01:1237 → llama-server CPU monitor (job=llama-server-monitor)
|
||
↓
|
||
AlertManager
|
||
├── receiver "null" → alerte Watchdog (heartbeat interne — silencé)
|
||
└── receiver "hermes-monitor" → webhook storage-01:9093 → ask-agent monitor → Hermes
|
||
```
|
||
|
||
---
|
||
|
||
## Dashboards Grafana
|
||
|
||
Trois dashboards custom auto-importés via ConfigMaps (label `grafana_dashboard: "1"`).
|
||
Versionnés dans `k8s/infra/monitoring/dashboards/`.
|
||
|
||
| Dashboard | UID | Contenu |
|
||
|---|---|---|
|
||
| **Funk — Kubernetes** | `funk-k8s1` | Cluster overview, CPU/RAM par nœud, pod restarts, PVCs |
|
||
| **Funk — Infrastructure** | `funk-inf1` | Hardware compute/storage-01/gpu-01 — CPU, RAM, disque, réseau, GPU ROCm |
|
||
| **Funk — IA / Hermes** | `funk-ai01` | Tokens/s llama-server (GPU+CPU), requêtes, VRAM, température GPU |
|
||
|
||
URLs directes :
|
||
- http://grafana.lab.local/d/funk-k8s1
|
||
- http://grafana.lab.local/d/funk-inf1
|
||
- http://grafana.lab.local/d/funk-ai01
|
||
|
||
### Ajouter un dashboard custom (GitOps)
|
||
|
||
1. Créer `k8s/infra/monitoring/dashboards/mon-dashboard.yaml` :
|
||
|
||
```yaml
|
||
apiVersion: v1
|
||
kind: ConfigMap
|
||
metadata:
|
||
name: grafana-dashboard-mon-dashboard
|
||
namespace: monitoring
|
||
labels:
|
||
grafana_dashboard: "1"
|
||
data:
|
||
mon-dashboard.json: |
|
||
{ ... JSON exporté depuis Grafana ... }
|
||
```
|
||
|
||
2. `git add` + `git push` → ArgoCD applique → sidecar Grafana importe automatiquement.
|
||
|
||
Pour exporter un dashboard depuis Grafana : **Dashboard → Share → Export → Save to file**.
|
||
|
||
---
|
||
|
||
## Règles d'alerte
|
||
|
||
Versionnées dans `k8s/infra/monitoring/alerts/` comme `PrometheusRule`.
|
||
Déclenchent AlertManager → webhook storage-01:9093 → profil `monitor` Hermes.
|
||
|
||
### Nœuds (`alerts-node.yaml`)
|
||
|
||
| Alerte | Seuil | Sévérité |
|
||
|---|---|---|
|
||
| `NodeHighCPU` | CPU > 90% pendant 5 min | warning |
|
||
| `NodeCriticalCPU` | CPU > 98% pendant 2 min | critical |
|
||
| `NodeHighMemory` | RAM > 90% pendant 5 min | warning |
|
||
| `NodeCriticalMemory` | RAM > 97% pendant 2 min | critical |
|
||
| `NodeDiskSpaceLow` | Disque > 80% pendant 10 min | warning |
|
||
| `NodeDiskSpaceCritical` | Disque > 93% pendant 5 min | critical |
|
||
| `Storage01Down` | `up{job="storage-01"} == 0` pendant 2 min | critical |
|
||
| `Gpu01Down` | `up{job="gpu-01-node"} == 0` pendant 2 min | critical |
|
||
| `NodeHighLoad` | Load5 > 8 pendant 10 min | warning |
|
||
|
||
### Kubernetes (`alerts-k8s.yaml`)
|
||
|
||
| Alerte | Seuil | Sévérité |
|
||
|---|---|---|
|
||
| `KubeNodeNotReady` | Nœud NotReady pendant 5 min | critical |
|
||
| `PodCrashLooping` | > 3 restarts en 15 min | warning |
|
||
| `PodFailedLong` | Pod Failed/Unknown pendant 10 min | warning |
|
||
| `PodPendingLong` | Pod Pending pendant 15 min | warning |
|
||
| `DeploymentUnavailable` | Réplicas < spec pendant 5 min | warning |
|
||
| `PVCNotBound` | PVC non Bound pendant 10 min | warning |
|
||
| `ArgoCDAppOutOfSync` | App ArgoCD OutOfSync pendant 30 min | warning |
|
||
|
||
### IA / LLM (`alerts-ai.yaml`)
|
||
|
||
| Alerte | Seuil | Sévérité |
|
||
|---|---|---|
|
||
| `LlamaServerGPUDown` | `up{job="llama-server-gpu"} == 0` pendant 2 min | critical |
|
||
| `LlamaServerSystemDown` | `up{job="llama-server-system"} == 0` pendant 2 min | warning |
|
||
| `LlamaServerMonitorDown` | `up{job="llama-server-monitor"} == 0` pendant 2 min | warning |
|
||
| `GPUTemperatureHigh` | Temp GPU > 80°C pendant 5 min | warning |
|
||
| `GPUTemperatureCritical` | Temp GPU > 90°C pendant 2 min | critical |
|
||
| `GPUVRAMAlmostFull` | VRAM > 95% pendant 5 min | warning |
|
||
| `LlamaServerHighQueueGPU` | Requêtes deferred > 5 pendant 2 min | warning |
|
||
|
||
### Ajouter une règle d'alerte
|
||
|
||
```yaml
|
||
# k8s/infra/monitoring/alerts/ma-regle.yaml
|
||
apiVersion: monitoring.coreos.com/v1
|
||
kind: PrometheusRule
|
||
metadata:
|
||
name: funk-ma-regle
|
||
namespace: monitoring
|
||
labels:
|
||
release: kube-prometheus-stack # obligatoire — sélecteur du Prometheus operator
|
||
spec:
|
||
groups:
|
||
- name: funk.custom
|
||
rules:
|
||
- alert: MonAlerte
|
||
expr: ma_metrique > seuil
|
||
for: 5m
|
||
labels:
|
||
severity: warning
|
||
annotations:
|
||
summary: "Description courte"
|
||
description: "Détail avec {{ $value }}"
|
||
```
|
||
|
||
---
|
||
|
||
## Sources de métriques
|
||
|
||
### Nœuds hors cluster (storage-01, gpu-01)
|
||
|
||
`node_exporter` v1.9.1 déployé via Ansible (rôle `node_exporter`).
|
||
Prérequis firewall : port 9100 ouvert depuis 192.168.10.0/24 (nftables storage-01, firewalld gpu-01).
|
||
|
||
```bash
|
||
# État node_exporter
|
||
ssh storage-01 "systemctl status node_exporter"
|
||
ssh gpu-01 "systemctl status node_exporter"
|
||
|
||
# Vérifier les métriques directement
|
||
curl -s http://192.168.10.1:9100/metrics | grep node_memory_MemAvailable
|
||
curl -s http://192.168.10.20:9100/metrics | grep node_memory_MemAvailable
|
||
|
||
# Vérifier le statut des targets dans Prometheus
|
||
curl -s http://prometheus.lab.local/api/v1/targets | python3 -c "
|
||
import sys, json
|
||
d = json.load(sys.stdin)
|
||
for t in d['data']['activeTargets']:
|
||
job = t['labels'].get('job','?')
|
||
print(t['health'], job, t.get('lastError','')[:60])
|
||
" | grep -E "storage-01|gpu-01|llama"
|
||
```
|
||
|
||
### Métriques GPU AMD (gpu-01) — ROCm via textfile_collector
|
||
|
||
Collecteur sysfs `rocm_scraper.timer` (toutes les 30s) écrit dans `/var/lib/node_exporter/textfile_collector/rocm.prom`.
|
||
Servi par node_exporter sur port 9100 — **pas de port séparé**.
|
||
Lit `/sys/class/drm/card0/device/` directement (rocm-smi retiré dans ROCm 7.x).
|
||
|
||
```bash
|
||
# Vérifier les métriques ROCm
|
||
ssh gpu-01 "cat /var/lib/node_exporter/textfile_collector/rocm.prom"
|
||
|
||
# Forcer une collecte manuelle
|
||
ssh gpu-01 "sudo systemctl start rocm_scraper"
|
||
|
||
# Vérifier le timer
|
||
ssh gpu-01 "systemctl status rocm_scraper.timer"
|
||
```
|
||
|
||
Métriques disponibles (labels : `gpu="0"`, `model="gfx1031"`) :
|
||
|
||
| Métrique | Description | Valeur typique |
|
||
|---|---|---|
|
||
| `rocm_gpu_temperature_celsius` | Température (°C) | 40–85 |
|
||
| `rocm_gpu_utilization_percent` | Utilisation GPU (%) | 0–100 |
|
||
| `rocm_vram_used_bytes` | VRAM utilisée | ~10 GB (modèle chargé) |
|
||
| `rocm_vram_total_bytes` | VRAM totale | 12.9 GB (RX 6700XT) |
|
||
|
||
### llama-server /metrics
|
||
|
||
llama.cpp expose ses métriques sur `/metrics` — activé via `--metrics` dans le service systemd.
|
||
|
||
| Métrique | Description |
|
||
|---|---|
|
||
| `llamacpp:prompt_tokens_seconds` | Débit prompt moyen (tokens/s) |
|
||
| `llamacpp:predicted_tokens_seconds` | Débit génération moyen (tokens/s) |
|
||
| `llamacpp:prompt_tokens_total` | Tokens prompt totaux (compteur) |
|
||
| `llamacpp:tokens_predicted_total` | Tokens générés totaux (compteur) |
|
||
| `llamacpp:requests_processing` | Requêtes en cours de traitement |
|
||
| `llamacpp:requests_deferred` | Requêtes en file d'attente |
|
||
| `llamacpp:n_decode_total` | Nombre total d'appels llama_decode() |
|
||
|
||
```bash
|
||
# Métriques GPU (Qwen3-8B)
|
||
curl -s http://192.168.10.20:1234/metrics | grep llamacpp
|
||
|
||
# Métriques CPU system
|
||
curl -s http://192.168.10.20:1236/metrics | grep llamacpp
|
||
|
||
# Métriques CPU monitor
|
||
curl -s http://192.168.10.20:1237/metrics | grep llamacpp
|
||
```
|
||
|
||
---
|
||
|
||
## AlertManager → Hermes monitor
|
||
|
||
AlertManager envoie les alertes en POST vers `http://192.168.10.1:9093/webhook`.
|
||
Le service `alertmanager-webhook` (Python, storage-01) reçoit les alertes, répond 200 immédiatement,
|
||
et lance `ask-agent monitor` en arrière-plan. Hermes (profil `monitor`) analyse et répond dans ses logs.
|
||
|
||
**Documentation complète** : [admin/ia/alertmanager-webhook.md](../ia/alertmanager-webhook.md)
|
||
|
||
```bash
|
||
# Vérifier le webhook
|
||
ssh storage-01 "systemctl status alertmanager-webhook"
|
||
ssh storage-01 "journalctl -u alertmanager-webhook -n 20 --no-pager --output=cat"
|
||
|
||
# Tester manuellement
|
||
curl -s -X POST http://192.168.10.1:9093/webhook \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"alerts":[{"status":"firing","labels":{"alertname":"TestAlert","severity":"warning","instance":"storage-01"},"annotations":{"summary":"Test alerte","description":"Vérification du webhook"}}]}'
|
||
|
||
# Voir les alertes actives dans AlertManager
|
||
curl -s http://alertmanager.lab.local/api/v2/alerts | python3 -m json.tool
|
||
```
|
||
|
||
---
|
||
|
||
## Persistance des données
|
||
|
||
Prometheus et Grafana stockent leurs données sur le RAID5 de storage-01 via NFS.
|
||
Provisioner : `nfs-subdir-external-provisioner`, StorageClass `nfs`.
|
||
|
||
| Service | PVC | Taille | Rétention |
|
||
|---|---|---|---|
|
||
| Prometheus | `prometheus-...-db-prometheus-...-0` | 20 Gi | 15 jours / 8 GB |
|
||
| Grafana | `kube-prometheus-stack-grafana` | 2 Gi | — |
|
||
|
||
```bash
|
||
# Espace consommé sur le RAID5
|
||
ssh storage-01 "du -sh /srv/data/nfs/k8s/*"
|
||
|
||
# État des PVCs
|
||
kubectl get pvc -n monitoring
|
||
```
|
||
|
||
---
|
||
|
||
## Administration Grafana
|
||
|
||
### Changer le mot de passe admin
|
||
|
||
Via API :
|
||
```bash
|
||
curl -X PUT http://admin:funk-grafana@grafana.lab.local/api/user/password \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"oldPassword":"funk-grafana","newPassword":"<nouveau>","confirmNew":"<nouveau>"}'
|
||
```
|
||
|
||
Ou modifier `k8s/infra/monitoring/values.yaml` → `grafana.adminPassword` → `git push`.
|
||
|
||
### Datasources
|
||
|
||
| Nom | Type | URL (interne cluster) |
|
||
|---|---|---|
|
||
| Prometheus | prometheus | `http://kube-prometheus-stack-prometheus:9090` |
|
||
| Alertmanager | alertmanager | `http://kube-prometheus-stack-alertmanager:9093` |
|
||
|
||
Configurées automatiquement au démarrage — ne pas modifier depuis l'UI (écrasé à chaque redémarrage).
|
||
|
||
### Lister les dashboards disponibles
|
||
|
||
```bash
|
||
curl -s http://admin:funk-grafana@grafana.lab.local/api/search?type=dash-db \
|
||
| python3 -c "import sys,json; [print(d['title'],'—',d['uid']) for d in json.load(sys.stdin)]"
|
||
```
|
||
|
||
---
|
||
|
||
## Administration Prometheus
|
||
|
||
### Vérifier les targets de scrape
|
||
|
||
```bash
|
||
# Depuis l'UI
|
||
open http://prometheus.lab.local/targets
|
||
|
||
# Via API
|
||
curl -s http://prometheus.lab.local/api/v1/targets | python3 -c "
|
||
import sys, json
|
||
d = json.load(sys.stdin)
|
||
for t in d['data']['activeTargets']:
|
||
print(t['health'], t['labels'].get('job','?'), t.get('lastError','')[:60])
|
||
" | sort
|
||
```
|
||
|
||
### Ajouter un scrape config
|
||
|
||
Modifier `k8s/infra/monitoring/values.yaml` → section `additionalScrapeConfigs` → `git push`.
|
||
|
||
```yaml
|
||
prometheus:
|
||
prometheusSpec:
|
||
additionalScrapeConfigs:
|
||
- job_name: mon-service
|
||
static_configs:
|
||
- targets: ['192.168.10.x:9xxx']
|
||
labels:
|
||
instance: mon-instance
|
||
```
|
||
|
||
### Vérifier les règles d'alerte
|
||
|
||
```bash
|
||
# Depuis l'UI
|
||
open http://prometheus.lab.local/alerts
|
||
|
||
# Via API — alertes actuellement firing
|
||
curl -s http://prometheus.lab.local/api/v1/alerts | python3 -c "
|
||
import sys, json
|
||
d = json.load(sys.stdin)
|
||
for a in d['data']['alerts']:
|
||
if a['state'] == 'firing':
|
||
print(a['labels']['alertname'], a['labels'].get('instance',''), a['state'])
|
||
"
|
||
```
|
||
|
||
---
|
||
|
||
## Commandes d'administration
|
||
|
||
```bash
|
||
# État de tous les pods monitoring
|
||
kubectl get pods -n monitoring
|
||
|
||
# Logs Prometheus
|
||
kubectl logs -n monitoring prometheus-kube-prometheus-stack-prometheus-0 -c prometheus --tail=50
|
||
|
||
# Logs Grafana
|
||
kubectl logs -n monitoring -l app.kubernetes.io/name=grafana --tail=50
|
||
|
||
# Logs AlertManager
|
||
kubectl logs -n monitoring alertmanager-kube-prometheus-stack-alertmanager-0 --tail=50
|
||
|
||
# Redémarrer Grafana (recharge les ConfigMaps dashboards)
|
||
kubectl rollout restart deployment kube-prometheus-stack-grafana -n monitoring
|
||
|
||
# Forcer re-sync ArgoCD (dashboards, alertes, config)
|
||
kubectl -n argocd annotate application monitoring argocd.argoproj.io/refresh=hard --overwrite
|
||
kubectl -n argocd annotate application kube-prometheus-stack argocd.argoproj.io/refresh=hard --overwrite
|
||
```
|
||
|
||
---
|
||
|
||
## Structure des fichiers
|
||
|
||
```
|
||
k8s/infra/monitoring/
|
||
├── helmrelease.yaml # Application ArgoCD (multi-source Helm kube-prometheus-stack)
|
||
├── namespace.yaml # Namespace monitoring (labels PodSecurity privileged)
|
||
├── values.yaml # Valeurs Helm : ressources, ingress, scrape configs, alertmanager
|
||
├── dashboards/
|
||
│ ├── dashboard-kubernetes.yaml # ConfigMap — Funk Kubernetes (uid: funk-k8s1)
|
||
│ ├── dashboard-infrastructure.yaml # ConfigMap — Funk Infrastructure (uid: funk-inf1)
|
||
│ └── dashboard-ai.yaml # ConfigMap — Funk IA/Hermes (uid: funk-ai01)
|
||
└── alerts/
|
||
├── alerts-node.yaml # PrometheusRule — CPU, RAM, disque, nodes hors cluster
|
||
├── alerts-k8s.yaml # PrometheusRule — k8s : pods, nodes, PVCs, ArgoCD
|
||
└── alerts-ai.yaml # PrometheusRule — llama-server, GPU temp, VRAM
|
||
```
|
||
|
||
> ArgoCD Application `monitoring` utilise `directory.recurse: true` pour scanner
|
||
> récursivement le répertoire et ses sous-dossiers.
|
||
|
||
---
|
||
|
||
## Points d'attention
|
||
|
||
| Sujet | Détail |
|
||
|---|---|
|
||
| Persistance | **Active** — Prometheus 20 Gi + Grafana 2 Gi sur RAID5 NFS storage-01 |
|
||
| Dashboards GitOps | ConfigMaps dans `dashboards/` → importés automatiquement par le sidecar Grafana |
|
||
| Alertes GitOps | PrometheusRules dans `alerts/` → label `release: kube-prometheus-stack` obligatoire |
|
||
| Receiver `null` | Doit exister dans la config AlertManager — requis par les sous-routes internes du chart (Watchdog) |
|
||
| nftables storage-01 | Port 9100 doit être avant la règle `drop` dans la chaîne `input` (sinon context deadline) |
|
||
| ROCm port | Métriques GPU via textfile_collector sur port **9100** — pas de port séparé 9101 |
|
||
| llama-server `--metrics` | Flag requis dans le service systemd pour activer l'endpoint `/metrics` |
|
||
| Talos — composants désactivés | `kubeEtcd`, `kubeScheduler`, `kubeControllerManager`, `kubeProxy` désactivés (non accessibles) |
|
||
| PodSecurity privileged | Namespace `monitoring` doit être `privileged` pour node-exporter (hostNetwork/hostPID/hostPath) |
|
||
| nfs-provisioner | StorageClass peut disparaître si ArgoCD prune l'app — forcer sync si PVC Pending |
|
||
|
||
|
||
---
|
||
|
||
## État vérifié — 2026-06-05
|
||
Tous les services monitoring (alertmanager, grafana, prometheus) sont actifs et en cours d'exécution. Aucune divergence notable avec la documentation. Aucun élément opérationnel critique détecté (pods crashés, configurations manquantes). L'état du cluster correspond entièrement aux spécifications. |