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>
15 KiB
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.localne 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)
- Créer
k8s/infra/monitoring/dashboards/mon-dashboard.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 ... }
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
# 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).
# É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).
# 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() |
# 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
# 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 | — |
# 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 :
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
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
# 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.
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
# 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
# É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
monitoringutilisedirectory.recurse: truepour 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.