proxMon/proxmox-monitor-konzept.md

16 KiB
Raw Permalink Blame History

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.

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

# 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

# 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):

{
  "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):

// 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.