mirror of
https://github.com/Alkatrazz24/Funk-lab.git
synced 2026-07-08 23:54:43 +02:00
BrokenPipeError corrigé : send_response(200) envoyé avant subprocess. ask-agent lancé en threading.Thread daemon — AlertManager n'attend plus. NO_COLOR=1 + TERM=dumb pour éviter les codes ANSI dans les logs. docs: alertmanager-webhook.md — architecture, flux, test, dépannage Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
418 lines
15 KiB
Markdown
418 lines
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 |
|