9.9 KiB
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
- 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 StateZin/proc/*/stat. Plattenbelegung/perstatfsauf/host/apps(gleiches Dateisystem/dev/sda1).docker.js– über den Socket-Proxy: Container-Liste (Name, Compose-Projekt/-Service, Status, Health, RestartCount) undstats?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, unternice -n 19 ionice -c3). Außerdem „Aufräumbar“: ungenutzte Images, Build-Cache, gestoppte Container (/system/dfüber den Proxy).
- App-Zuordnung (
apps.js): Container → App über das Labelcom.docker.compose.servicebzw. den Containernamen; eine kleine Mapping-Dateiconfig/apps.jsonerlaubt Umbenennen/Zusammenfassen (z. B.webklar-ticket-*→ „Ticketsystem“) und Ordnerzuordnung. Previews erscheinen einzeln. - Speicher (
store.js): SQLite (node:sqlite) im Volumeserverpuls-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.
- Tabellen:
- Regeln (
rules.js): reine Funktion(aktuelle + historische Werte) → Liste aktiver Befunde {id, level, titel, wert, seit}. Schwellen inconfig/rules.json. - 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. - Mailer (
mail.js): nodemailer,mail.your-server.de:587STARTTLS, Loginserver@webklar.com. Fehlgeschlagene Mails bleiben inmail_queueund werden alle 5 min erneut versucht (max. 24 h). - API (
api.js): HTTP-Server Port 8091, Basis/api/serverpuls. Jede Anfrage (außer/health) brauchtX-Appwrite-JWT; der Dienst fragt/accountbei Appwrite ab (Cache 60 s) und verlangt Labeladmin, sonst 401/403. - 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:
- 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.
- Kacheln: CPU, Load, RAM, Swap, Platte, Zombies – aktueller Wert, 24-h-Minikurve, Trend ggü. gestern.
- Großes Diagramm (uPlot): Zeitraum 24 h / 7 T / 30 T / 90 T / 1 J, Wert wählbar, Tooltip mit exaktem Wert.
- Tabelle „Projekte“: sortierbar, Suchfeld, Δ-Spalten grün/rot mit Pfeil, Minikurve; Klick öffnet Detaildiagramm der App.
- „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-alpineerzeugen (Deploy-Image).
Betrieb / Deploy
- Code:
/home/webklar/apps/serverpuls/(eigenes Git-Repo, nur lokal). docker-compose.yml(Backup vorher): Diensteserverpuls(node:22-alpine,mem_limit: 128m,cpus: 0.5,init: true; Host-/procwird als/host/proc:rogemountet, keinpid: host) undserverpuls-docker-proxy(tecnativa/docker-socket-proxy, nur lesend:CONTAINERS=1,IMAGES=1,SYSTEM=1für/system/df,POST=0), beide imwebklar-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 vonscripts/mail-zugang.pygesetzt (Claude sieht das Passwort nicht).- WOMS-nginx:
location /api/serverpuls/→http://serverpuls:8091(wie/api/integrations/). - WOMS-Code auf
feature/serverpuls(Worktreedev-serverpuls, Basisorigin/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).