Spec: Serverpuls (Server-Monitoring fuer Admins)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6323b830d7
commit
3eb67bba81
1 file changed
+198
@@ -0,0 +1,198 @@
|
|||||||
|
# Serverpuls – Server-Monitoring in WOMS
|
||||||
|
|
||||||
|
Stand: 2026-09-30 · Status: Design vom Nutzer im Chat freigegeben, Spec zur Prüfung
|
||||||
|
|
||||||
|
## Ziel
|
||||||
|
|
||||||
|
Ein neuer Navbar-Eintrag **Serverpuls** in WOMS (ticket.webklar.com) zeigt Admins die
|
||||||
|
Auslastung des Servers und das Wachstum der einzelnen Projekte über Tage, Wochen und
|
||||||
|
Monate – als Verlaufskurven „wie bei Aktien“. Feste Warnregeln (keine KI) melden
|
||||||
|
Probleme per E-Mail in drei Stufen.
|
||||||
|
|
||||||
|
Ausdrücklich **nicht KI-gesteuert**: Messung, Auswertung und Mails laufen
|
||||||
|
deterministisch in einem kleinen Dienst ohne Claude.
|
||||||
|
|
||||||
|
## Rahmenbedingungen
|
||||||
|
|
||||||
|
- Server: 8 Kerne, 15 GB RAM, 4 GB Swap (am 2026-09-30 voll), Platte 150 GB (76 % belegt),
|
||||||
|
~61 Container. Der Dienst selbst muss sehr sparsam sein (Limit 128 MB RAM, 0,5 CPU).
|
||||||
|
- WOMS ist eine statische React/Vite-SPA auf Appwrite; Server-Logik liegt in kleinen
|
||||||
|
Node-Diensten hinter dem WOMS-nginx (Vorbild: `ticket-integrations`,
|
||||||
|
`/api/integrations/`, Auth über Appwrite-JWT).
|
||||||
|
- Admin in WOMS = Appwrite-Konto mit Label `admin` (`AuthContext.jsx`).
|
||||||
|
- Der Deploy-Webhook von WOMS deployt jeden gepushten Branch; live geht nur per Push
|
||||||
|
des Nutzers nach `test`.
|
||||||
|
|
||||||
|
## Entscheidungen
|
||||||
|
|
||||||
|
| Frage | Entscheidung |
|
||||||
|
|---|---|
|
||||||
|
| Name | Serverpuls |
|
||||||
|
| Rolle der KI | keine; feste Warnregeln |
|
||||||
|
| „Projekt“ | jede App / jeder Container (Previews als eigene Apps) mit CPU, RAM, Speicher |
|
||||||
|
| Warnkanal | E-Mail in drei Stufen Low / Medium / Critical |
|
||||||
|
| Mail-Takt | Critical sofort, Medium Sammelmail täglich 07:00, Low Wochenübersicht Mo 07:00 |
|
||||||
|
| Mail-Adresse | `server@webklar.com` ist Absender **und** einziger Empfänger |
|
||||||
|
| Umsetzung | eigener schlanker Dienst (kein Beszel, kein Netdata) |
|
||||||
|
| Sichtbarkeit | nur Admins (Navbar und API) |
|
||||||
|
|
||||||
|
## Architektur
|
||||||
|
|
||||||
|
```
|
||||||
|
Host /proc (ro) ──┐
|
||||||
|
docker-socket-proxy (nur GET containers/stats) ──┼──> serverpuls (Node 22, SQLite) ──> SMTP server@
|
||||||
|
/home/webklar/apps + Docker-Volumes (ro) ──┘ │
|
||||||
|
└── HTTP :8091 <── WOMS-nginx /api/serverpuls/ <── ServerpulsPage (uPlot)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Einheiten
|
||||||
|
|
||||||
|
1. **Collector** (`collect/`): liest Messwerte, gibt reine Datenobjekte zurück.
|
||||||
|
- `host.js` – `/proc/stat` (CPU % aus Differenz), `/proc/meminfo` (RAM verfügbar,
|
||||||
|
Swap), `/proc/loadavg`, Zombies = Prozesse mit State `Z` in `/proc/*/stat`.
|
||||||
|
Plattenbelegung `/` per `statfs` auf `/host/apps` (gleiches Dateisystem `/dev/sda1`).
|
||||||
|
- `docker.js` – über den Socket-Proxy: Container-Liste (Name, Compose-Projekt/-Service,
|
||||||
|
Status, Health, RestartCount) und `stats?stream=false` (CPU %, RAM).
|
||||||
|
- `sizes.js` – Größe pro App: Ordner unter `/home/webklar/apps/<app>` plus Docker-
|
||||||
|
Volumes der Container dieser App (`du -sb`, unter `nice -n 19 ionice -c3`).
|
||||||
|
Außerdem „Aufräumbar“: ungenutzte Images, Build-Cache, gestoppte Container
|
||||||
|
(`/system/df` über den Proxy).
|
||||||
|
2. **App-Zuordnung** (`apps.js`): Container → App über das Label
|
||||||
|
`com.docker.compose.service` bzw. den Containernamen; eine kleine Mapping-Datei
|
||||||
|
`config/apps.json` erlaubt Umbenennen/Zusammenfassen (z. B. `webklar-ticket-*` →
|
||||||
|
„Ticketsystem“) und Ordnerzuordnung. Previews erscheinen einzeln.
|
||||||
|
3. **Speicher** (`store.js`): SQLite (`node:sqlite`) im Volume `serverpuls-data`.
|
||||||
|
- Tabellen: `host_samples`, `app_samples`, `app_sizes`, `cleanup_samples`,
|
||||||
|
`alerts` (Zustand), `mail_queue`.
|
||||||
|
- Aufbewahrung: Rohwerte (5 min) 7 Tage → Stundenmittel/-max 90 Tage →
|
||||||
|
Tageswerte unbegrenzt. Verdichtung täglich 03:30.
|
||||||
|
- Erwartete Größe < 100 MB/Jahr.
|
||||||
|
4. **Regeln** (`rules.js`): reine Funktion `(aktuelle + historische Werte) → Liste aktiver
|
||||||
|
Befunde {id, level, titel, wert, seit}`. Schwellen in `config/rules.json`.
|
||||||
|
5. **Alarmierung** (`alerts.js`): vergleicht Befunde mit gespeichertem Zustand, erzeugt
|
||||||
|
Mails (sofort/täglich/wöchentlich), Wiederholungssperre 24 h, „✅ behoben“-Mail beim
|
||||||
|
Verschwinden. Zustand in SQLite → Neustart erzeugt keine Doppel-Mails.
|
||||||
|
6. **Mailer** (`mail.js`): nodemailer, `mail.your-server.de:587` STARTTLS, Login
|
||||||
|
`server@webklar.com`. Fehlgeschlagene Mails bleiben in `mail_queue` und werden
|
||||||
|
alle 5 min erneut versucht (max. 24 h).
|
||||||
|
7. **API** (`api.js`): HTTP-Server Port 8091, Basis `/api/serverpuls`. Jede Anfrage
|
||||||
|
(außer `/health`) braucht `X-Appwrite-JWT`; der Dienst fragt `/account` bei Appwrite
|
||||||
|
ab (Cache 60 s) und verlangt Label `admin`, sonst 401/403.
|
||||||
|
8. **Scheduler** (`main.js`): 5-min-Takt, nächtliche Größenmessung 03:00, Verdichtung
|
||||||
|
03:30, Medium-Digest täglich 07:00, Low-Wochenübersicht Mo 07:00 (Europe/Berlin).
|
||||||
|
|
||||||
|
### Messung
|
||||||
|
|
||||||
|
- Alle 5 Minuten: Server gesamt (CPU %, Load 1/5/15, RAM verfügbar, Swap belegt,
|
||||||
|
Platte `/`, Zombies) und pro Container (CPU %, RAM, Status, Neustarts).
|
||||||
|
- Täglich 03:00: Größe pro App und „Aufräumbar“-Werte.
|
||||||
|
- Fällt eine Messung aus, wird **kein** Wert geschrieben (Lücke statt falscher Null).
|
||||||
|
|
||||||
|
## Warnregeln
|
||||||
|
|
||||||
|
Wichtige Container („kritisch“): Traefik, Appwrite, Kundenportal, Ticketsystem,
|
||||||
|
ticket-integrations, Gitea, InvoiceNinja, n8n (Liste in `config/rules.json`).
|
||||||
|
|
||||||
|
**🔴 Critical – sofortige Mail**
|
||||||
|
- Platte `/` ≥ 90 %
|
||||||
|
- RAM verfügbar < 500 MB **und** Swap ≥ 90 %, 10 min lang
|
||||||
|
- Load (5 min) > 24, 15 min lang
|
||||||
|
- kritischer Container läuft nicht oder > 3 Neustarts pro Stunde
|
||||||
|
- Zombies ≥ 100
|
||||||
|
|
||||||
|
**🟠 Medium – Sammelmail täglich 07:00**
|
||||||
|
- Platte `/` ≥ 80 %
|
||||||
|
- Swap ≥ 75 % oder RAM verfügbar < 1,5 GB, jeweils 1 h lang
|
||||||
|
- Zombies ≥ 20
|
||||||
|
- App wächst in 7 Tagen um > 20 % oder > 2 GB
|
||||||
|
- Load (5 min) > 8, 1 h lang
|
||||||
|
- nicht-kritischer Container abgestürzt (exited ≠ 0 oder Restart-Schleife)
|
||||||
|
|
||||||
|
**🟡 Low – Wochenübersicht Mo 07:00**
|
||||||
|
- ungenutzte Docker-Images > 10 GB oder Build-Cache > 3 GB
|
||||||
|
- App wächst in der Woche um > 10 %
|
||||||
|
- Zombies (≥ 1) halten sich > 24 h
|
||||||
|
- immer enthalten: Top 5 größte Apps, Top 5 schnellstes Wachstum, Top 5 RAM
|
||||||
|
|
||||||
|
**Allgemein**
|
||||||
|
- Dieselbe Warnung (gleiche `id`) höchstens 1× pro 24 h erneut gemeldet.
|
||||||
|
- Beim Verschwinden: Mail „✅ behoben“ (in derselben Taktung wie die Stufe; bei
|
||||||
|
Critical sofort).
|
||||||
|
- Alle aktiven Warnungen stehen jederzeit auf der Seite.
|
||||||
|
- Wird eine Warnung höher eingestuft (Medium → Critical), gilt sofort die höhere Stufe.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
Alle Antworten JSON, Zeitstempel ISO/UTC.
|
||||||
|
|
||||||
|
| Methode + Pfad | Inhalt |
|
||||||
|
|---|---|
|
||||||
|
| `GET /health` | ohne Auth; `{ok, lastSample}` |
|
||||||
|
| `GET /overview` | aktuelle Serverwerte, 24-h-Minikurven, Trend ggü. gestern, aktive Warnungen, Zeitpunkt der letzten Messung |
|
||||||
|
| `GET /host?metric=cpu\|ram\|swap\|load\|disk\|zombies&range=24h\|7d\|30d\|90d\|1y` | Zeitreihe; Auflösung abhängig vom Zeitraum |
|
||||||
|
| `GET /apps` | Tabelle: Name, CPU %, RAM, Speicher, Δ 7 T, Δ 30 T, Speicher-Minikurve (30 T) |
|
||||||
|
| `GET /apps/:id?range=…` | Zeitreihen Speicher, RAM, CPU für eine App |
|
||||||
|
| `GET /cleanup` | ungenutzte Images, Build-Cache, gestoppte Container mit Größe |
|
||||||
|
|
||||||
|
Nur lesend. Es gibt **keine** Endpunkte zum Löschen, Stoppen oder Ändern.
|
||||||
|
|
||||||
|
## WOMS-Seite „Serverpuls“
|
||||||
|
|
||||||
|
- Navbar-Eintrag mit Pulssymbol, nur für `isAdmin`; roter/oranger Punkt bei aktiver
|
||||||
|
Critical/Medium-Warnung (aus `/overview`, beim Laden der App und alle 5 min).
|
||||||
|
- Route `/serverpuls`; Nicht-Admins werden umgeleitet.
|
||||||
|
- Aufbau:
|
||||||
|
1. **Kopf mit Ampel**: 🟢 Alles ok / 🟠 n Warnungen / 🔴 Critical, darunter die
|
||||||
|
aktiven Warnungen (Stufe, seit wann, Wert). Hinweis „letzte Messung vor X Min.“;
|
||||||
|
gelb, wenn älter als 15 min.
|
||||||
|
2. **Kacheln**: CPU, Load, RAM, Swap, Platte, Zombies – aktueller Wert, 24-h-Minikurve,
|
||||||
|
Trend ggü. gestern.
|
||||||
|
3. **Großes Diagramm** (uPlot): Zeitraum 24 h / 7 T / 30 T / 90 T / 1 J, Wert
|
||||||
|
wählbar, Tooltip mit exaktem Wert.
|
||||||
|
4. **Tabelle „Projekte“**: sortierbar, Suchfeld, Δ-Spalten grün/rot mit Pfeil,
|
||||||
|
Minikurve; Klick öffnet Detaildiagramm der App.
|
||||||
|
5. **„Aufräumbar“**: reine Anzeige.
|
||||||
|
- Aktualisiert sich jede Minute, solange sichtbar. Handy: Kacheln untereinander.
|
||||||
|
- Neue Abhängigkeit: `uplot`. Lockfile wie bisher in einem Wegwerf-`node:20-alpine`
|
||||||
|
erzeugen (Deploy-Image).
|
||||||
|
|
||||||
|
## Betrieb / Deploy
|
||||||
|
|
||||||
|
- Code: `/home/webklar/apps/serverpuls/` (eigenes Git-Repo, nur lokal).
|
||||||
|
- `docker-compose.yml` (Backup vorher): Dienste `serverpuls` (`node:22-alpine`,
|
||||||
|
`mem_limit: 128m`, `cpus: 0.5`, `init: true`; Host-`/proc` wird als `/host/proc:ro` gemountet,
|
||||||
|
kein `pid: host`) und `serverpuls-docker-proxy`
|
||||||
|
(`tecnativa/docker-socket-proxy`, nur lesend: `CONTAINERS=1`, `IMAGES=1`, `SYSTEM=1`
|
||||||
|
für `/system/df`, `POST=0`), beide im `webklar-network`, ohne Traefik-Route.
|
||||||
|
- Mounts serverpuls: `/proc:/host/proc:ro`, `/home/webklar/apps:/host/apps:ro`,
|
||||||
|
`/var/lib/docker/volumes:/host/volumes:ro`, `serverpuls-data:/data`.
|
||||||
|
- `.env`: `SERVERPULS_SMTP_USER=server@webklar.com`, `SERVERPULS_SMTP_PASS` – vom
|
||||||
|
Nutzer über eine Erweiterung von `scripts/mail-zugang.py` gesetzt (Claude sieht das
|
||||||
|
Passwort nicht).
|
||||||
|
- WOMS-nginx: `location /api/serverpuls/` → `http://serverpuls:8091` (wie
|
||||||
|
`/api/integrations/`).
|
||||||
|
- WOMS-Code auf `feature/serverpuls` (Worktree `dev-serverpuls`, Basis `origin/test`).
|
||||||
|
Live erst nach Push des Nutzers.
|
||||||
|
|
||||||
|
## Fehlerbehandlung
|
||||||
|
|
||||||
|
- Collector-Teilfehler (z. B. Proxy nicht erreichbar) → betroffene Werte fehlen, Rest
|
||||||
|
wird gespeichert; Fehler im Log.
|
||||||
|
- Größenmessung hat Zeitlimit (30 min); bricht sie ab, bleiben die Vortageswerte stehen
|
||||||
|
und die Seite zeigt deren Datum.
|
||||||
|
- SMTP-Fehler → `mail_queue`, erneuter Versuch alle 5 min bis 24 h.
|
||||||
|
- Appwrite nicht erreichbar → API 502, Seite zeigt Fehlerhinweis.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
- Unit: Regeln (Schwellen, Dauerbedingungen, Hoch-/Herabstufung, Wiederholungssperre,
|
||||||
|
behoben), Verdichtung, Wachstumsberechnung, `/proc`-Parser mit Beispieldateien.
|
||||||
|
- API: gemocktes Appwrite (Admin → 200, ohne Label → 403, ohne JWT → 401).
|
||||||
|
- UI: bewährtes esbuild-/Playwright-Harness unter RAM-Limit mit API-Fixtures.
|
||||||
|
- Live-Check: Container-RAM < 128 MB, erste Messwerte, Test-Mail an server@.
|
||||||
|
|
||||||
|
## Nicht enthalten
|
||||||
|
|
||||||
|
- Keine Aktionen (Löschen, Neustarten) aus der Seite heraus.
|
||||||
|
- Keine KI-Auswertung.
|
||||||
|
- Keine automatischen Tickets (Warnungen nur per Mail und auf der Seite).
|
||||||
Reference in new issue
Block a user