k8s/ is now a faithful port of the mentor's Ansible Grafana stack (Prometheus, Alertmanager, Loki, Tempo, Alloy, Grafana, node-exporter) for the ledgerline-observability namespace on falken: same versions and configs, one *.vione.cloud ingress per tool behind Traefik basic auth, secrets generated by create-secrets.sh into gitignored files. Two fixes over the Ansible role, both verified live: Tempo's metrics-generator needs traces_storage or it runs no processors, and Prometheus needs exemplar-storage to keep exemplars. Ledgerline stays on czy; an Alloy agent in the Traefik override ships its container logs to Loki. Roadmap and CLAUDE.md now point at k8s/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4.5 KiB
Phase 6 — Dashboards and alerts as code
Part of docs/roadmap/ — see intro.md for platform/background context and summary.md for how every phase fits together. Prev: phase-5.md · Next: phase-7.md
Status: Not started
Message: Depends on Phases 3–5 producing traces, logs and metrics worth dashboarding.
Time-box: 2 evenings. This is an explicit requirement from Babak: destroy and recreate in seconds.
Concepts
- Three levels of "as code", and you should know when each applies:
- File provisioning — YAML in
provisioning/datasources/andprovisioning/dashboards/, plus dashboard JSON files mounted into the container. Grafana loads them at startup. Simplest, no state, no API tokens, perfect for a Compose demo. This alone satisfies the requirement. - Grafana Foundation SDK — typed libraries (Go, Python, TypeScript, Java,
PHP) that generate the dashboard JSON programmatically. Now a dashboard is a
function:
red_dashboard("ledger"), called seven times in a loop. Reviewable diffs, reusable panels, no 2000-line JSON blobs. Grafana now points here instead of Grafonnet (which is legacy and unofficial). - Terraform provider / Grafana Operator / Git Sync — for state-managed, multi-environment, or GitOps setups. Overkill here; the right answer later, when this lands on your Kubernetes cluster.
- File provisioning — YAML in
- Dashboards should answer questions, not display data. A good service dashboard answers, in order: is it up, is it serving, is it erroring, is it slow, and where is the pressure. Panels that nobody looks at during an incident are cost, not value.
- Alerts as code too. Grafana alert rules, contact points and notification policies all provision from YAML. And keep the alert definition next to the dashboard that explains it.
Resources
- Grafana docs → Observability as code (the index page — it lays out Foundation SDK, file provisioning, Git Sync and Terraform, and tells you which to use when).
- Grafana Foundation SDK repo → the Python examples, especially the
red-methodexample. - Udemy backbone: dashboard design sections (panel types, transformations, variables, dashboard best practice).
Build
Your platform provisions datasources only — there is no dashboards provider at
all. So this phase is entirely yours, and the right home for it is inside the
platform's own manifests (k8s/grafana/, the k3s port of the Ansible role),
using the same mechanism as everything else.
- Extend
k8s/grafana/. Add a file-based dashboard provider (aprovisioning/dashboards/YAML pointing at a mounted directory) and the dashboard JSON. Ship both throughconfigMapGenerator, exactly asdatasources.ymlis shipped today, and mount them indeployment.yaml. Two k8s-specific things to work out:- A ConfigMap is capped at about 1 MiB, and client-side
kubectl applyalso stores a copy in an annotation. Large generated dashboards push against that. - The configMap hash suffix rolls the Grafana pod on every change. Decide whether that's what you want.
- A ConfigMap is capped at about 1 MiB, and client-side
- Generate the JSON from code, don't hand-write it. Use the Grafana
Foundation SDK (Python): one function producing a RED dashboard for a given
service name, looped over the seven services; a second producing a system
overview (service graph, stream lag, pool saturation, error-budget burn). Commit
the generator and its output;
k8s/grafana/ships the output. - The demo is now
kubectl apply -k k8s/. Delete every dashboard through the Grafana UI, re-apply (or go further:kubectl delete -k k8s/and rebuild the whole platform), and watch them all come back. That is a strictly stronger answer to Babak's "destroy and recreate in seconds" than a local Makefile target, because it's the same command that builds the platform. - While you're in there, review the provisioned datasources and add what's missing:
tracesToMetricson Tempo (pointing at the Tempo-generated span metrics) is a worthwhile addition, and the existingtracesToLogsV2has notagsmapping — understand whyfilterByTraceID: truealone is sufficient here, and when it wouldn't be. - Add alert rules — see Phase 8 for the Alertmanager-vs-Grafana-alerting choice.
Done when: kubectl apply -k k8s/ restores every dashboard from scratch, and no
dashboard has ever been saved through the Grafana UI.