❯ monitoring · Running

Observabilité avec VictoriaMetrics + Grafana Observability with VictoriaMetrics + Grafana

Construction d'une stack d'observabilité légère mais complète pour le cluster k3s — VictoriaMetrics pour le stockage des métriques, Grafana pour les dashboards, et alerting avec VMAlert. Building a lightweight but complete observability stack for the k3s cluster — VictoriaMetrics for metrics storage, Grafana for dashboards, and alerting with VMAlert.

· 6 min de lecture 6 min read ·
k3s-clustermonitoringgrafanavictoriametricsobservability
🇬🇧 Read this article in English

Mise à jour 2026-06 : cette stack a bien évolué depuis — logs centralisés, alerting réellement actif et annotations automatiques. L’évolution est retracée en fin d’article, et la mise en œuvre détaillée dans Observabilité v2.

Pourquoi VictoriaMetrics plutôt que Prometheus ?

Le premier Prometheus que j’ai déployé sur un master Pi 4 8 Go a fini par grignoter jusqu’à 2 Go de RAM — pour deux semaines de rétention et à peine une cinquantaine de cibles. Sur un nœud qui fait déjà tourner l’API server et etcd, c’est un quart de la capacité disponible réquisitionné par un seul composant de monitoring. Il fallait un outil pensé pour tourner à l’étroit.

VictoriaMetrics offre un ratio performances/ressources bien supérieur :

  • Compression de données 5-10x meilleure que Prometheus
  • Consommation mémoire ~70% inférieure à configuration équivalente
  • Compatibilité totale avec les dashboards Grafana Prometheus
  • API PromQL identique — zéro changement dans les dashboards existants

Pour un homelab sur ARM64, c’est le meilleur choix.

Architecture de la stack

vmagent (collecte)
    │  scrape des cibles (pods, nodes, k8s API)
    ▼
VictoriaMetrics single-node (stockage)
    │  TSDB compressée, rétention 30j
    ├──► Grafana (dashboards)
    └──► VMAlert (règles d'alerte)
              │
              ▼
        Alertmanager (notifications)

vmagent est l’équivalent léger de Prometheus scrape — il collecte les métriques des cibles et les pousse vers VictoriaMetrics via le remote write protocol. Il consomme ~50 Mo de RAM.

Déploiement

VictoriaMetrics

apiVersion: apps/v1
kind: Deployment
metadata:
  name: victoriametrics
  namespace: monitoring
spec:
  template:
    spec:
      containers:
      - name: victoriametrics
        image: victoriametrics/victoria-metrics:stable
        args:
        - -storageDataPath=/storage
        - -retentionPeriod=30d
        - -httpListenAddr=:8428
        resources:
          requests:
            memory: 256Mi
          limits:
            memory: 1Gi
        volumeMounts:
        - name: storage
          mountPath: /storage

30 jours de rétention avec ~50 cibles génère environ 3-4 Go de données compressées. Un PVC de 10 Go est largement suffisant.

vmagent — scraping Kubernetes

La configuration de scraping suit le format Prometheus. Les targets clés :

# ConfigMap vmagent-config
scrape_configs:
  # Métriques des nœuds via node-exporter
  - job_name: 'node-exporter'
    kubernetes_sd_configs:
    - role: node
    relabel_configs:
    - source_labels: [__address__]
      regex: '(.*):.*'
      target_label: __address__
      replacement: '${1}:9100'

  # Métriques kubelet
  - job_name: 'kubelet'
    kubernetes_sd_configs:
    - role: node
    scheme: https
    tls_config:
      insecure_skip_verify: true
    bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token

  # Pods avec annotation prometheus.io/scrape
  - job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
    - role: pod
    relabel_configs:
    - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
      action: keep
      regex: true

Grafana

Grafana se connecte à VictoriaMetrics comme datasource Prometheus standard :

apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-datasources
  namespace: monitoring
data:
  datasources.yaml: |
    apiVersion: 1
    datasources:
    - name: VictoriaMetrics
      type: prometheus
      url: http://victoriametrics.monitoring.svc.cluster.local:8428
      isDefault: true

Les dashboards Kubernetes standard (IDs Grafana : 315, 6417, 13332) fonctionnent sans modification.

Dashboards clés

Cluster Overview (Grafana 315) : vue globale CPU/RAM/réseau par nœud. Premier dashboard à consulter pour un état général.

Kubernetes Pods (Grafana 6417) : métriques par pod — idéal pour identifier les pods OOM-killés ou les containers qui consomment trop.

Longhorn : dashboard officiel Longhorn pour l’état des volumes et l’utilisation du stockage.

Node Exporter : métriques système détaillées par nœud (température CPU, I/O disque, saturation mémoire).

Alerting

⚠️ Ces règles existaient sur le papier — l’alerting réel (Alertmanager → Telegram) n’a été activé que des mois plus tard. Détails dans la mise à jour 2026-06.

VMAlert évalue les règles d’alerte et envoie les notifications via Alertmanager. Les alertes critiques configurées :

groups:
- name: homelab-critical
  rules:
  - alert: NodeMemoryHigh
    expr: (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) < 0.1
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "Node {{ $labels.instance }} — mémoire disponible < 10%"

  - alert: LonghornVolumeDegraded
    expr: longhorn_volume_robustness != 2
    for: 10m
    labels:
      severity: critical
    annotations:
      summary: "Volume Longhorn {{ $labels.volume }} dégradé"

  - alert: PodCrashLooping
    expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} en CrashLoop"

Mise à jour 2026-05-12 — Deadlock RWO sur Grafana

Grafana stocke sa base SQLite (dashboards custom, utilisateurs, plugins) sur un PVC Longhorn en accès RWO (ReadWriteOnce). Le Deployment par défaut utilise la stratégie RollingUpdate avec 1 surge et 0 unavailable. Lors d’un rolling update, le nouveau pod tentait de monter le même PVC RWO que l’ancien pod toujours en Terminating → AttachVolume bloqué → le rollout traînait 5+ minutes, parfois jusqu’au timeout.

Deux fixes appliqués (commits 4c02c9c + 917823a) :

1. Stratégie Recreate

spec:
  strategy:
    type: Recreate

Le nouveau pod attend que l’ancien soit complètement terminé avant de démarrer. Cela introduit une brève indisponibilité de Grafana (~30s), en échange d’un rollout déterministe et sans blocage. Pour un dashboard interne de homelab, c’est un compromis évident.

2. Memory limit 256Mi → 512Mi

Grafana avec defaultDashboardsEnabled: true (Helm chart) charge les dashboards en mémoire au démarrage et maintient les plugins actifs. À 256Mi de limit, des OOMKilled aléatoires survenaient lors de l’ouverture du dashboard k8s-cluster (30+ panels simultanés). Bump à 512Mi → stable.

resources:
  requests:
    memory: 100Mi
  limits:
    memory: 512Mi

Le requests reste à 100Mi pour ne pas sur-réserver sur les nœuds.

La leçon transposable : tout workload stateful sur PVC RWO doit avoir strategy: Recreate (ou un PVC RWX si le storage le permet). Le combo “RollingUpdate + RWO” est un piège silencieux — ça marche les jours où on a de la chance, et bloque les autres.

Leçons apprises

node-exporter sur chaque nœud : déployer node-exporter via DaemonSet (pas Deployment) pour garantir une instance sur chaque nœud, y compris les futurs. Sans node-exporter, les métriques système sont absentes — seules les métriques Kubernetes sont disponibles.

Rétention vs stockage : 30 jours de rétention est un bon équilibre. Au-delà, la croissance du PVC devient significative. Pour l’archivage long terme, configurer VictoriaMetrics pour écrire en remote write vers MinIO (format compatible S3).

insecure_skip_verify: true pour kubelet : le certificat kubelet est auto-signé. L’alternative correcte est de configurer une CA trust chain — acceptable pour un homelab, mais à corriger pour un environnement de production.

Mise à jour 2026-06-12 — La v2 : logs, alerting réel, annotations

Honnêteté rétrospective : la section « Alerting » ci-dessus décrivait la cible plus que la réalité. vmalert et Alertmanager sont restés inactifs pendant des mois — les règles existaient sur le papier, rien ne notifiait personne. Et la rétention réelle s’est stabilisée à 15 jours (pas 30) : suffisant pour le diagnostic, deux fois moins de stockage.

Ce qui a fini par tout débloquer : un pic de load inattribuable lors d’un audit — aucune métrique par pod, pas de logs centralisés, pas d’alerte. Les quatre manques ont été comblés en une journée :

Le détail de la mise en œuvre (VictoriaLogs + Vector, vmalert → Alertmanager → Telegram, annotations automatiques des graphes, et la leçon « pending n’est pas firing » sur les probes qui flappent) : Observabilité v2 — logs, alertes & annotations en une journée.

k3s-clustermonitoringgrafanavictoriametricsobservability