proxMon/proxmox-monitor-konzept.md

418 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Proxmox Monitor — Konzept
Eine Agent-Server-Anwendung zum Monitoring von Proxmox-Hosts mit Fokus auf ZFS-Gesundheit und VM-Übersicht. Implementiert in Elixir/OTP.
## Designprinzipien
- **KISS**: Jede Entscheidung zugunsten der einfacheren Lösung, solange sie funktioniert.
- **YAGNI**: Features werden erst gebaut, wenn sie konkret gebraucht werden — nicht prophylaktisch.
- **Read-only**: Der Agent führt keine verändernden Commands auf dem Proxmox-Host aus.
- **Push-Architektur**: Agents initiieren die Verbindung zum Server (NAT-freundlich).
---
## Architektur-Übersicht
```
┌──────────────────┐ ┌────────────────────┐
│ Proxmox-Host 1 │ │ Server (LXC) │
│ ┌────────────┐ │ │ im Rechenzentrum │
│ │ Agent │──┼──WSS──┐ │ │
│ └────────────┘ │ │ │ ┌──────────────┐ │
└──────────────────┘ └──▶│ │ Caddy │ │
│ │ Reverse Proxy│ │
┌──────────────────┐ │ └──────┬───────┘ │
│ Proxmox-Host 2 │ │ │ │
│ ┌────────────┐ │ │ ┌──────▼───────┐ │
│ │ Agent │──┼──WSS─────▶│ │ Phoenix │ │
│ └────────────┘ │ │ │ LiveView │ │
└──────────────────┘ │ └──────┬───────┘ │
... │ │ │
│ ┌──────▼───────┐ │
┌──────────────────┐ │ │ SQLite │ │
│ Proxmox-Host N │ │ └──────────────┘ │
│ ┌────────────┐ │ │ │
│ │ Agent │──┼──WSS─────▶│ │
│ └────────────┘ │ │ │
└──────────────────┘ └────────────────────┘
```
## Technologie-Stack
| Komponente | Technologie | Begründung |
|------------|-------------|------------|
| Agent | Elixir + Burrito | Eigenständige Binary, keine Erlang-Installation auf Proxmox nötig |
| Server | Phoenix + LiveView | Realtime-UI ohne separates Frontend |
| Transport | Phoenix Channels (WSS) | Persistente Verbindung, Auto-Reconnect, Offline-Detection gratis |
| Datenbank | SQLite + Ecto | Für ~20 Hosts vollkommen ausreichend, keine separate DB-Instanz |
| TLS / Reverse Proxy | Caddy (bereits vorhanden) | Let's Encrypt automatisch |
| Deployment (Server) | LXC-Container auf Proxmox im RZ | Geringer Overhead, saubere Isolation |
| Deployment (Agent) | systemd-Service | Standard auf Debian/Proxmox |
## Systemanforderungen
- **Proxmox-Hosts**: Proxmox VE 8.3+ mit OpenZFS 2.3+ (für `-j` JSON-Output)
- **Server**: LXC oder VM mit ausreichend RAM (1 GB reicht), Debian/Ubuntu
- **Netzwerk**: Server muss öffentlich über HTTPS erreichbar sein (via Caddy)
---
## Agent
### Verantwortlichkeiten
Der Agent läuft auf jedem Proxmox-Host, sammelt in festen Intervallen Metriken und schickt sie an den Server. Er hält eine persistente WebSocket-Verbindung zum Server über Phoenix Channels.
### Sammlungs-Intervalle
Nicht alles muss gleich häufig gesammelt werden. Daten mit hoher Änderungsrate werden öfter abgefragt, statische Informationen seltener.
| Intervall | Daten |
|-----------|-------|
| 30 Sekunden | Host-Metriken, VM-Runtime-Status, ZFS-Pool-Status, Storage-Auslastung |
| 5 Minuten | Snapshots, Dataset-Liste, VM-Config, Guest-Agent-IPs |
| 30 Minuten | Proxmox-Version, pending APT-Updates, ZFS-Version |
### Zu sammelnde Daten
**Host-Metriken** (aus `/proc` und `uptime`)
- CPU-Auslastung (%), Load-Average (1/5/15)
- RAM: used, total, available
- Uptime in Sekunden
- Root-Filesystem: used, total
- Hostname, Kernel-Version
**Proxmox-Storage** (`pvesh get /nodes/<node>/storage --output-format json`)
- Alle konfigurierten Storages (ZFS, NFS, Local, etc.)
- Typ, Status (active/inactive), used, total
- Content-Typen (images, backup, iso, ...)
**ZFS-Pools** (`zpool status -j --json-flat-vdevs --json-int`, `zpool list -j --json-int`)
- Rohes JSON (vollständig gespeichert für spätere Analyse)
- Zusätzlich extrahiertes Summary:
- Pool-Name, Health-State
- Size, Allocated, Free (Bytes)
- Fragmentation (%), Capacity (%)
- Error-Counter (read/write/checksum)
- Scrub-Status, letzter erfolgreicher Scrub
- Anzahl vdevs, Anzahl degraded vdevs
**ZFS-Datasets & Snapshots** (`zfs list -j --json-int`)
- Rohes JSON (Datasets und Snapshots)
- Snapshot-Summary pro Dataset:
- Anzahl Snapshots
- Alter des ältesten / neuesten Snapshots
- Gesamt-Speicher (usedbysnapshots)
**Virtuelle Maschinen & LXC-Container** (`pvesh`)
- Statisch (aus Config): VMID, Name, Type (qemu/lxc), Cores, RAM (max), Disks (mit Storage-Backend), Tags, Autostart
- Dynamisch (aus Runtime-Status): Status, Uptime, CPU-Auslastung, RAM-Verbrauch, Disk-I/O, Netzwerk-I/O
- Via Guest-Agent (QEMU, optional): IP-Adressen aller Interfaces, OS-Info, Disk-Usage im Guest
- LXC: IPs direkt aus Container-Config
**System-Info**
- Proxmox-Version (`pveversion`)
- Anzahl verfügbarer Updates (`apt list --upgradable 2>/dev/null | wc -l`)
- ZFS-Version
- Agent-Version (fest einkompiliert)
### Konfiguration
Datei: `/etc/proxmox-monitor/agent.toml`, Zugriffsrechte `0600`, Eigentümer `root`.
```toml
server_url = "wss://monitor.example.com/socket/websocket"
token = "agent_abc123xyz..."
host_id = "pve-host-01" # optional, sonst Hostname
[intervals]
fast_seconds = 30
medium_seconds = 300
slow_seconds = 1800
```
### Deployment
```bash
# Einmaliges Setup auf einem Proxmox-Host
scp proxmox-monitor-agent root@pve-host-01:/usr/local/bin/
scp agent.toml root@pve-host-01:/etc/proxmox-monitor/
scp proxmox-monitor-agent.service root@pve-host-01:/etc/systemd/system/
ssh root@pve-host-01 "systemctl enable --now proxmox-monitor-agent"
```
Der Agent muss als root laufen, da `zpool status` bei Degraded-Pools und einigen ZFS-Details Root-Rechte benötigt. Das ist akzeptabel, weil der Agent ausschließlich lesende Commands ausführt und keine eingehenden Verbindungen akzeptiert.
---
## Server
### Verantwortlichkeiten
- Nimmt Verbindungen von Agents entgegen, authentifiziert sie
- Speichert Metriken in SQLite
- Stellt LiveView-Dashboard bereit
- Cleanup alter Metriken (Retention)
### Datenmodell
```elixir
# hosts
- id (PK)
- name (unique)
- token_hash (bcrypt)
- created_at
- last_seen_at
- agent_version (nullable)
- proxmox_version (nullable)
- zfs_version (nullable)
- status ("online" | "offline" | "never_connected")
# metrics (Zeitreihe, ein Eintrag pro Sample)
- id (PK)
- host_id (FK)
- collected_at (indexed)
- interval_type ("fast" | "medium" | "slow")
- payload (JSON) # Gesamter Sample-Inhalt
# Indexiert wegen Dashboard-Queries:
# - (host_id, collected_at DESC) für "letzte N Samples eines Hosts"
# - (collected_at) für Retention-Cleanup
```
**Begründung für JSON-Payload statt normalisierter Spalten:**
Die Daten werden fast immer vollständig pro Host/Zeitpunkt gelesen (für die Detail-View). Separate Spalten pro Metrik würden die Schema-Evolution aufwendig machen. SQLite unterstützt JSON-Operatoren (`->>`) für Queries, falls wir mal einzelne Felder filtern müssen.
### Phoenix Channels — Protokoll
**Join** (Agent → Server):
```json
{
"topic": "host:pve-host-01",
"payload": {
"token": "agent_abc123xyz...",
"agent_version": "0.1.0"
}
}
```
Server validiert Token gegen `hosts.token_hash`. Bei Erfolg: Assign der `host_id` an den Socket, `last_seen_at` aktualisieren, Status auf `online` setzen.
**Events** (Agent → Server):
```json
// Event: "metric:fast" | "metric:medium" | "metric:slow"
{
"collected_at": "2026-04-21T12:34:56Z",
"data": { /* je nach Intervall-Typ */ }
}
```
**Disconnect-Handling** (Server):
Wenn der Channel-Prozess stirbt (Terminate-Callback), wird der Host sofort als `offline` markiert. Kein Polling, keine Timeouts nötig.
### LiveView-Seiten
**1. Übersicht (`/`)**
- Eine Karte pro Host, Grid-Layout
- Status-Ampel: Grün (alles ok) / Gelb (Warnung) / Rot (kritisch) / Grau (offline)
- Kritisch = Pool DEGRADED/FAULTED, Capacity > 90%, Agent offline
- Warnung = Capacity 80-90%, alte Snapshots, pending Updates, Scrub überfällig
- Pro Karte sichtbar: Host-Name, CPU, RAM, Uptime, Pool-Status (Zusammenfassung), VM-Anzahl
- Sortier-/Filter-Optionen: nach Status, nach Host-Name
**2. Host-Detail (`/hosts/:name`)**
- Header mit Proxmox-Version, Uptime, letzter Kontakt
- Tabs oder Sektionen:
- **Metriken**: CPU/RAM/Load-Graphen über 24h (simple Line-Charts mit Chart.js oder Contex)
- **ZFS-Pools**: Pro Pool eine Box mit Health, Capacity-Bar, Fragmentation, Error-Counters, Scrub-Info, vdev-Liste
- **Snapshots**: Tabelle pro Dataset mit Anzahl, Alter des ältesten/neuesten Snapshots
- **Storage**: Tabelle aller Proxmox-Storages mit Auslastung
- **VMs/LXCs**: Tabelle mit Name, Type, Status, CPU/RAM-Auslastung, IPs, Tags
**3. VM-Suche (`/vms`)**
- Globale Suche über alle Hosts
- Input: Name oder IP
- Ergebnis: Tabelle mit VM-Name, Host, Status, IP, Ressourcen
- Killer-Feature bei 20 Hosts: "Wo läuft `nginx-proxy`?"
**4. Host-Verwaltung (`/admin/hosts`)**
- Liste aller Hosts mit Status, letzter Kontakt, Agent-Version
- Button "Neuen Host hinzufügen": Formular mit Name → generiert Token, zeigt Install-Anleitung
- Pro Host: "Token revoken" (setzt neuen Token), "Host löschen"
### Authentifizierung
**Web-UI:**
- Single-User, Login via Passwort (Argon2-Hash in Environment-Variable oder Secret-Config)
- Minimal-Setup mit einer einzigen Session/Cookie
- Kein User-Management-UI nötig
**Agents:**
- Shared Token pro Agent
- Token-Generierung: 32 Bytes Random, Base64-URL-encoded
- In DB als Bcrypt-Hash gespeichert
- Revoke durch Token-Neugenerierung im Admin-UI
### Retention / Cleanup
Ein GenServer läuft stündlich und löscht:
- `metrics` älter als 48 Stunden
Das reicht für "was ist in den letzten 2 Tagen passiert"-Analysen. Bei 20 Hosts × 30s-Samples × 48h = ca. 230.000 Rows. Völlig unproblematisch für SQLite.
---
## Projekt-Struktur
Monorepo mit zwei getrennten Mix-Projekten:
```
proxmox_monitor/
├── README.md
├── konzept.md # Dieses Dokument
├── agent/ # Mix-Projekt → Burrito-Binary
│ ├── mix.exs
│ ├── config/
│ │ ├── config.exs
│ │ └── runtime.exs
│ ├── lib/
│ │ ├── agent.ex
│ │ ├── agent/
│ │ │ ├── application.ex
│ │ │ ├── config.ex
│ │ │ ├── reporter.ex # Phoenix Channel Client
│ │ │ ├── supervisor.ex
│ │ │ └── collectors/
│ │ │ ├── host.ex
│ │ │ ├── storage.ex
│ │ │ ├── zfs.ex
│ │ │ ├── vms.ex
│ │ │ └── system_info.ex
│ │ └── agent/schema/ # Structs für Samples
│ │ ├── sample.ex
│ │ ├── pool_summary.ex
│ │ └── ...
│ ├── rel/
│ │ └── burrito.exs
│ └── test/
└── server/ # Phoenix-Projekt
├── mix.exs
├── config/
├── lib/
│ ├── server/
│ │ ├── application.ex
│ │ ├── hosts.ex # Kontext: Host-Verwaltung
│ │ ├── metrics.ex # Kontext: Metrik-Speicherung
│ │ ├── retention.ex # GenServer: Cleanup
│ │ └── schema/
│ │ ├── host.ex
│ │ └── metric.ex
│ └── server_web/
│ ├── router.ex
│ ├── endpoint.ex
│ ├── channels/
│ │ ├── agent_socket.ex
│ │ └── host_channel.ex
│ ├── live/
│ │ ├── overview_live.ex
│ │ ├── host_detail_live.ex
│ │ ├── vm_search_live.ex
│ │ └── admin_hosts_live.ex
│ └── controllers/
│ └── auth_controller.ex
├── priv/
│ └── repo/migrations/
└── test/
```
---
## Sicherheit
- **TLS erzwungen** über Caddy, Agent verifiziert Server-Zertifikat
- **Token niemals in Plaintext** in DB (Bcrypt) oder Logs
- **Agent-Config mit Rechten `0600`, Root**
- **Rate-Limiting** auf Channel-Join via Hammer-Library (z.B. 5 Versuche/Minute pro IP)
- **Keine eingehenden Verbindungen zum Agent** — Agent initiiert immer
- **Server-URL im Agent fest gepinnt** (keine Redirects)
- **Read-only auf dem Proxmox-Host** — Agent führt keine verändernden Commands aus
---
## MVP-Scope — Was ist drin, was nicht
### ✅ Im MVP
- Agent sammelt Host-, ZFS-, VM-, Storage-, System-Metriken
- Persistente WebSocket-Verbindung via Phoenix Channels
- SQLite-Speicherung mit 48h Retention
- Dashboard: Übersicht, Host-Detail, VM-Suche, Admin
- Single-User-Auth für Web-UI
- Token-basierte Agent-Auth
- Burrito-Binary für Agent
- LXC-Deployment für Server
### ❌ Bewusst nicht im MVP (YAGNI)
- **Alerts per E-Mail/Telegram/Gotify** — Dashboard zeigt farbig, reicht erstmal
- **SMART-Werte der Disks** — eigenes Unterprojekt, später
- **Backup-Task-Historie** — aufwendiges Parsing, später
- **Cluster-Support** — nicht benötigt
- **Agent-Self-Update** — manuell via scp reicht bei 20 Hosts
- **Remote-Actions** (VM starten, Scrub triggern) — Read-only bleibt Read-only
- **Multi-User, RBAC** — Single-User reicht
- **Langzeit-Historie (>48h)** — separates Konzept (Downsampling) wenn gebraucht
- **Mobile App** — Web-UI responsive reicht
---
## Roadmap
### Phase 1 — Grundgerüst (Woche 1-2)
- Agent-Skeleton: Ein Collector (Host-Metriken), Output auf Console
- Server-Skeleton: Phoenix-Projekt, Host-Schema, einfacher Channel, der Daten empfängt und loggt
- Ende: Agent verbindet sich lokal zum Server und pusht CPU-Metriken
### Phase 2 — ZFS & VMs (Woche 3-4)
- ZFS-Collector (pool status, list, snapshots)
- VM-Collector (pvesh)
- Storage-Collector
- Server speichert in SQLite, simple Anzeige-Route
### Phase 3 — LiveView-Dashboard (Woche 5-6)
- Übersichtsseite mit Status-Ampeln
- Host-Detail mit allen Sektionen
- VM-Suche
- Auth für Web-UI
### Phase 4 — Admin & Deployment (Woche 7)
- Admin-UI für Host-Verwaltung
- Retention-GenServer
- Burrito-Build für Agent
- LXC-Deployment-Dokumentation
### Phase 5 — Produktiv-Rollout (Woche 8)
- Deployment auf Server im RZ
- Caddy-Konfiguration
- Agent auf 2-3 Test-Hosts ausrollen
- Nach erfolgreichem Test: Rollout auf alle 20 Hosts
### Nach dem MVP — ggf. als nächstes
1. **Alerts** (wenn klar ist, welche Thresholds sinnvoll sind)
2. **SMART-Monitoring** (für ZFS-Vorhersage)
3. **Backup-Task-Tracking**
4. **Langzeit-Historie** mit Downsampling
---
## Offene Entscheidungen / Vormerkungen
Diese Punkte können beim Implementieren entschieden werden, sind aber fürs Konzept nicht kritisch:
- **Chart-Library**: Chart.js (JS, via LiveView-Hook) vs. Contex (SVG, pure Elixir). Contex ist "eleganter", Chart.js bietet mehr Optionen out-of-the-box.
- **Shared Structs zwischen Agent/Server**: Anfangs duplizieren. Falls das wehtut, später in eine geteilte Lib extrahieren.
- **Exception-Handling bei pvesh/zpool-Fehlern**: Ausfall einer Datenquelle darf nicht das ganze Sample verwerfen. Teil-Samples mit Fehler-Flag akzeptieren.
- **Logs**: Strukturiert (JSON) vs. Text. Vorschlag: Server strukturiert für spätere Auswertung, Agent textbasiert für einfaches journalctl-Debugging.