diff --git a/docs/FAMILY_VIEW_DASHBOARD.md b/docs/FAMILY_VIEW_DASHBOARD.md new file mode 100644 index 0000000..4c9bcec --- /dev/null +++ b/docs/FAMILY_VIEW_DASHBOARD.md @@ -0,0 +1,194 @@ +# Family-View Dashboard - Spezifikation + +Status: **Spezifikation (Doku-only)**, kein Grafana-JSON in diesem Schritt. +Audit-Bezug: `docs/AUDIT_2026-05-25.md` Finding **F-08** (Alerts/Sichtbarkeit) und das Sprint-3-TODO "Family-View Dashboard definieren" aus `docs/AUDIT_2026-05-25_TODO.md`. + +## Zweck + +Ein Grafana-Dashboard, das beim Morgen-Check in unter 30 Sekunden zeigt, ob das Homelab gesund ist. Zielgruppe ist primaer der Operator. Wenn die Familie es zufaellig anschaut, soll niemand erschrecken: ueberall gruene Felder bedeuten "alles in Ordnung", ohne dass man die Technik dahinter verstehen muss. + +Das Dashboard ist die Konsolidierung des morgendlichen Pruefablaufs: + +- Sind die wichtigsten Apps erreichbar? +- Hat das Backup gestern Nacht funktioniert? +- Wann laufen die Zertifikate aus? +- Sind die Disks ausreichend frei? +- Laufen die kritischen Container? + +## Abgrenzung + +Diese Datei beschreibt nur Layout, Datenquellen und PromQL-Queries. Die JSON-Datei `monitoring/grafana/dashboards/family-view.json` wird **bewusst noch nicht** angelegt, weil: + +- Es noch keine Live-Pruefung gegen die echte Grafana-Instanz gab. +- Die Alert-Regeln (Borg-Stale, Cert-Expiry, Container-Down) sind laut `docs/AUDIT_2026-05-25_TODO.md` Sprint 3 selbst noch im "in Arbeit (Regeln vorbereitet)"-Status. +- Bei einem halbgaren Dashboard-JSON entstehen mehr Wartungsfragen als Klarheit. + +Die JSON-Datei wird angelegt, sobald (a) die genannten Metriken stabil verfuegbar sind und (b) ein erster manueller Build im Grafana-UI das Layout bestaetigt hat. + +## Datenquellen + +Authoritativ ist `monitoring/grafana/provisioning/datasources/datasources.yml`. Das Dashboard nutzt nur die schon provisionierten Datasources: + +- `Prometheus` - Blackbox, node-exporter, cAdvisor, Traefik-Metrics +- optional `Loki` - Log-Volume-Spike als Zusatz-Panel +- bewusst nicht: `InfluxDB 3 Core` (das ist Home-Assistant-/Ecowitt-Sicht, nicht Homelab-Health) + +## Layout (4x4 Grid, mobile-vertraeglich) + +| Zeile | Panel | Breite (Grafana w) | Hoehe (Grafana h) | +|---|---|---:|---:| +| 1 | Endpoints up (Stat, gross gruen/rot) | 12 | 5 | +| 1 | Backup heute Nacht (Stat) | 6 | 5 | +| 1 | Naechster Cert-Ablauf (Stat, Tage) | 6 | 5 | +| 2 | Kritische Container running (Stat-Liste) | 12 | 6 | +| 2 | Disk-Fuellung (Bargraph, je Mountpoint) | 12 | 6 | +| 3 | Endpoint-Tabelle (Table: Host, Status, Latenz) | 24 | 8 | +| 4 | Cert-Tage-Tabelle (Table: Host, Tage bis Ablauf) | 12 | 6 | +| 4 | Container-Status-Tabelle (Table: kritischer Container, Running, letztes Restart) | 12 | 6 | + +Dashboard-Metadaten: + +- UID: `homelab-family-view` +- Title: `Homelab / Family View` +- Tags: `homelab`, `family-view`, `morning-check` +- Refresh: `30s` +- Default-Zeitfenster: `now-24h` bis `now` +- Folder: `Homelab` + +## Panel-Spezifikation + +### Panel 1: Endpoints up + +- Type: `stat` +- Title: `Apps online` +- Query: `sum(probe_success{job="blackbox-http"})` +- Anzeige: gruene Zahl bei Gesamtzahl, Wechsel auf rot wenn `< Soll-Anzahl`. +- Threshold: Soll-Anzahl wird aus `monitoring/blackbox/blackbox.yml` und Prometheus-Scrape-Liste abgeleitet (zum Doku-Zeitpunkt 19 HTTPS-Ziele laut `docs/MIGRATION_LOG.md` 2026-05-25-Monitoring-Konsolidierung). +- Subtitel im Panel: `von erreichbar`. (Soll-Wert wird beim Bau aus dem aktuellen Target-Count gesetzt; nicht hartcoden.) + +### Panel 2: Backup heute Nacht + +- Type: `stat` +- Title: `Borg-Lauf` +- Query (sobald Borg-Stale-Metrik im Textfile-Collector live ist): + ```promql + (time() - homelab_borg_last_completed_timestamp_seconds) / 3600 + ``` +- Einheit: `h` +- Threshold: + - 0-26 h gruen + - 26-30 h gelb + - >30 h rot +- Subtitel im Panel: `Stunden seit letztem completed-Lauf`. +- Fallback bis Metrik live: Panel zeigt `n/a`, Doku-Hinweis in der Beschreibung. + +### Panel 3: Naechster Cert-Ablauf + +- Type: `stat` +- Title: `Cert laeuft in` +- Query: + ```promql + min((probe_ssl_earliest_cert_expiry{job="blackbox-http"} - time()) / 86400) + ``` +- Einheit: `d` (Tage) +- Threshold: + - >14 gruen + - 7-14 gelb + - <7 rot +- Subtitel: `Tage bis kleinste Restlaufzeit aller geprueften Hosts`. + +### Panel 4: Kritische Container running + +- Type: `stat` +- Title: `Kritische Container` +- Query (sobald Container-Up-Metrik live ist): + ```promql + sum(homelab_critical_container_running) + ``` +- Threshold: erwartete Anzahl gruen, jeder fehlende Container rot. +- Subtitel: `von erwartet`. Erwartete Liste pflegen wir in `services/posture-check` / Textfile-Exporter (siehe `docs/AUDIT_2026-05-25_TODO.md` Sprint 3 "Container-Down-Alert"). +- Fallback: cAdvisor-Query als Naeherung, solange Textfile-Metrik noch nicht produktiv ist: + ```promql + count(rate(container_last_seen{name=~"traefik|authelia|postgresql17|Redis|gitea|komodo-core|komodo-mongo|komodo-periphery|monitoring-prometheus|monitoring-grafana|monitoring-loki|monitoring-alertmanager"}[5m]) > 0) + ``` + +### Panel 5: Disk-Fuellung + +- Type: `bargauge` +- Title: `Disk-Fuellung` +- Query: + ```promql + 100 * (1 - node_filesystem_avail_bytes{fstype!~"tmpfs|overlay"} / node_filesystem_size_bytes{fstype!~"tmpfs|overlay"}) + ``` +- Anzeige: pro Mountpoint, sortiert absteigend. +- Threshold: + - <70 gruen + - 70-85 gelb + - >85 rot +- Subtitel: `Prozent belegt`. + +### Panel 6: Endpoint-Tabelle + +- Type: `table` +- Title: `Endpoint-Status` +- Spalten: + - Host (Instance) + - Status (`UP` / `DOWN`) + - Antwortzeit (probe_duration_seconds) +- Queries: + - `probe_success{job="blackbox-http"}` -> Mapping: `1` -> `UP` (gruen), `0` -> `DOWN` (rot) + - `probe_duration_seconds{job="blackbox-http"}` -> Sekunden +- Sortierung: DOWN oben, dann nach Antwortzeit absteigend. + +### Panel 7: Cert-Tage-Tabelle + +- Type: `table` +- Title: `Cert-Tage bis Ablauf` +- Spalten: Host, Tage +- Query: + ```promql + (probe_ssl_earliest_cert_expiry{job="blackbox-http"} - time()) / 86400 + ``` +- Sortierung: aufsteigend (am ehesten ablaufende oben). +- Color-Mapping wie Panel 3. + +### Panel 8: Container-Status-Tabelle + +- Type: `table` +- Title: `Kritische Container` +- Spalten: Container, Running (1/0), letztes Restart (Sekunden seit Start) +- Queries: + - sobald Textfile-Metrik live: `homelab_critical_container_running` (Label `name`) + - Fallback aus cAdvisor: `container_start_time_seconds{name=~""}` +- Sortierung: nicht-running zuerst. + +## Spaeter ergaenzbar (nicht Teil der ersten Version) + +- Loki-Log-Volume-Spike-Panel +- node_exporter Memory-Saturation-Panel +- Plex-Sessions (nur wenn Plex-Exporter eingerichtet ist; aktuell nicht geplant) +- Immich Asset-Wachstum (eigenes Dashboard, nicht Family-View) + +## Build-Reihenfolge fuer den spaeteren JSON + +Wenn das JSON gebaut wird, bitte in dieser Reihenfolge: + +1. Sicherstellen, dass alle Metriken aus den Queries oben in Prometheus auffindbar sind (`/graph` Smoke-Test). +2. Dashboard in Grafana-UI manuell zusammenklicken; Layout an dieser Spezifikation entlang. +3. JSON exportieren, in `monitoring/grafana/dashboards/family-view.json` ablegen. +4. Provisioning-Provider laesst die Datei automatisch laden (siehe `monitoring/grafana/provisioning/dashboards/dashboards.yml`). +5. Bei jeder Schema-Aenderung Doku hier nachziehen, damit Spec und JSON nicht driften. + +## Smoke-Test nach Aktivierung + +- Dashboard laedt unter `https://monitoring.kaleschke.info/d/homelab-family-view/`. +- Alle 8 Panels rendern ohne `No data`. +- Im Normalbetrieb erscheinen Panel 1-5 vollstaendig gruen. +- Ein bewusster Test-Stale-Borg oder ein Container-Stop laesst die zugehoerigen Panels auf gelb/rot wechseln. + +## Was das Dashboard NICHT ersetzt + +- ntfy-Alerts: das Dashboard ist passiv (Pull), ntfy ist aktiv (Push). Beide sind notwendig. +- DR-Doku: `docs/DISASTER_RECOVERY.md` bleibt die Recovery-Quelle. +- Restore-Tests: `docs/RESTORE_DRILL_ROUTINE.md` ist die Kadenz, die das Dashboard nicht ersetzt. +- Familien-Onboarding: `docs/FAMILY_ONBOARDING.md` ist die Doku fuer die Familie, dieses Dashboard ist Operator-Tool.