← Homelab

Monitoring als code: alerts met tests en een runbook per alert

De monitoring van mijn homelab staat volledig in Git: Prometheus-regels met unittests, Alertmanager-config, een runbook per alert dat in CI wordt afgedwongen, en een merge naar main die automatisch uitrolt.

De stack

Een aparte monitoring-VM met Prometheus, Alertmanager, Grafana, Loki, blackbox-exporter en een meldingsketen (ntfy en Keep). Op alle hosts draait Grafana Alloy, dat metrics en logs doorstuurt. De configuratie staat als Ansible-rollen in Git.

Alles in Git

De repository bevat de Prometheus-config, de alertregels per categorie, de scrape-targets, de Alertmanager-config, tests en scripts. Een pull request start de validatie; een merge naar main rolt via Gitea Actions over SSH uit naar de monitoring-VM.

De validatie draait:

  • promtool check config en check rules;
  • unittests op alertregels: bij deze reeks waarden moet deze alert na deze tijd afgaan, met deze labels;
  • amtool check-config;
  • shellcheck op alle scripts;
  • een runbook-controle.

Een runbook per alert, afgedwongen

Elke alert heeft een runbook_url naar een pagina in dezelfde repository. De validatie faalt bij een alert zonder pagina, en bij een pagina zonder alert. Zo kan een runbook niet verouderen of ontbreken.

Het formaat is bewust machineleesbaar:

  • PromQL-blokken met labelvariabelen ($instance, $mountpoint), zodat je ze direct kunt plakken;
  • oorzaken met "Herken je aan …", om er één voor één af te strepen.

Dat is voorbereiding op een volgende stap: een lokaal taalmodel dat bij een alert het runbook leest, de queries uitvoert en een eerste analyse maakt.

Wat het schrijven van runbooks opleverde

Bij het schrijven van een runbook per alert kom je vanzelf de alerts tegen die niet kloppen:

  • twee alerts konden nooit afgaan, omdat ze op een job-label filterden dat niet bestond (de agents leveren onder een andere naam) of op een metric die de agent niet verzamelde;
  • een "exporter down"-alert dekte daardoor maar één exporter;
  • een blackbox-probe controleerde een endpoint dat al lang niet meer bestond.

Elke alert die een runbook krijgt, wordt dus ook getest op de vraag of hij wel kan afgaan.

Melding ≠ incident

Alertmanager groepeert op alertnaam, instance en severity, en herhaalt elke vier uur. Daardoor gaven dertien targets die tegelijk uitvielen dertien losse kritieke meldingen. De echte oorzaak was één DNS-probleem. Zie De storing die geen storing was. Een aggregatieregel met een inhibit maakt daar één melding van.