Proxmox VE im Unternehmensnetz: Cluster, HA, SDN & Backup ohne Internet
Ein einzelner Proxmox-Host ist schnell installiert. Ein Cluster, der in einem grossen Unternehmensnetz produktionsreif laeuft – mit echter Hochverfuegbarkeit, sauberer Netz-Segmentierung, verteiltem Storage, einer internen PKI und einem Backup, das im Ernstfall auch wirklich zurueckspielt – ist eine andere Hausnummer. Und wenn das Ganze in einem geschlossenen Netz ohne Internet-Anbindung stehen soll, faellt jede bequeme Abkuerzung weg: kein apt update aus dem Netz, kein Let’s-Encrypt, kein Cloud-NTP, kein schneller Blick ins offizielle Repo.
Dieser Post baut einen solchen Cluster Schritt fuer Schritt auf und erklaert bei jedem Aspekt das Warum – so, dass du die Entscheidungen nachvollziehen und im eigenen Netz reproduzieren kannst. Die Beispiele nutzen bewusst Unternehmens-Adressbereiche (mehrere 10.0.0.0/8-Segmente, VLAN-Trennung) statt eines typischen 192.168.1.0/24-Heimnetzes, und der rote Faden ist durchgehend der Air-Gap-Betrieb.
Dieser Post ist als Schritt-für-Schritt-Anleitung aufgebaut: Jedes Kapitel ist ein Schritt und liefert copy&paste-faehige Befehle, Configs und Skripte plus einen Verifikations-Block am Ende. Die Platzhalter (
corp.local,10.10.0.x, Interface-Namen, Disks) musst du an dein Netz anpassen – die Struktur der Schritte bleibt gleich.
So arbeitest du diesen Post durch
Die Schritte bauen aufeinander auf – arbeite sie in dieser Reihenfolge ab und pruefe nach jedem die Verifikation, bevor du weitergehst:
- Schritt 1 – Planung, Netzdesign, Adresskonzept (Papier vor Blech)
- Schritt 2 – Air-Gap-Basisdienste (Mirror, NTP, DNS, CA)
- Schritt 3 – Nodes installieren + Post-Install-Skript
- Schritt 4 – Cluster mit redundantem Corosync
- Schritt 5 – Ceph-Storage
- Schritt 6 – HA + Fencing
- Schritt 7–10 – SDN, Zertifikate, User/Rechte, Backup
- Schritt 11 – Betrieb, Monitoring, Updates
Voraussetzungen, die vor Schritt 1 stehen sollten:
- Hardware: mindestens 3 Nodes mit je 4–6 NICs, getrennte OS- und Daten-Disks, Hardware-Watchdog (IPMI/iDRAC/iLO).
- Netz: Switches mit VLAN-Trunking und Jumbo-Frames (MTU 9000) fuers Storage-Netz.
- Ein Einschleus-Host mit Internet, um Pakete/ISO einmalig zu spiegeln.
- Bestehende Firmen-Dienste erreichbar: AD/LDAP und eine interne CA.
- Konventionen: FQDNs (
pve-01.corp.local), festes IP-Schema (siehe Schritt 1).
Alle Befehle laufen als
root(oder mitsudo). Wo ein Befehl auf jedem Node oder nur auf pve-01 gehoert, steht es dabei. Copy&paste-Bloecke sind so gehalten, dass du nur die Platzhalter ersetzt.
Zielbild: Was bauen wir eigentlich?
Bevor eine einzige ISO gebrannt wird, muss das Zielbild stehen. Wir bauen einen 3-Node-Cluster (das Minimum fuer echtes Quorum und HA) mit verteiltem Ceph-Storage, angebunden an eine bestehende Unternehmens-Infrastruktur (AD, interne CA, interner Paket-Mirror, PBS-Backup). Alles ohne Weg nach draussen.
flowchart TB
subgraph MGMT["Management-Segment (VLAN 10 - 10.10.0.0/24)"]
A1["pve-01"]
A2["pve-02"]
A3["pve-03"]
end
subgraph CORO["Corosync-Ring 0/1 (VLAN 20/21 - dediziert)"]
A1 -. "totem" .- A2
A2 -. "totem" .- A3
A3 -. "totem" .- A1
end
subgraph CEPH["Ceph Cluster+Public (VLAN 30 - 10.30.0.0/24, MTU 9000)"]
A1 --- OSD["OSDs / MON / MGR"]
A2 --- OSD
A3 --- OSD
end
subgraph SVC["Interne Dienste"]
MIR["Paket-Mirror + APT-Repo"]
CA["Interne CA / step-ca (ACME)"]
AD["AD / LDAP"]
NTP["NTP (chrony)"]
PBS["Proxmox Backup Server"]
end
MGMT --> SVC
A1 & A2 & A3 --> PBS
Die vier tragenden Saeulen:
| Saeule | Umsetzung | Warum so |
|---|---|---|
| Quorum | 3 Nodes + optional QDevice | Ungerade Knotenzahl, damit bei Ausfall eines Nodes die Mehrheit steht |
| Storage | Ceph (RBD) | Shared Storage ist Voraussetzung fuer Live-Migration und HA-Recovery |
| Netz | mehrere getrennte NICs/VLANs | Corosync, Storage, VM-Traffic und Backup duerfen sich nicht gegenseitig aushungern |
| Backup | Proxmox Backup Server, getrennt | Ein Backup auf demselben Cluster ist kein Backup |
Schritt 1: Planung, Netzdesign und Adresskonzept
Node-Dimensionierung
Fuer einen produktiven Enterprise-Cluster gelten andere Massstaebe als im Heim-Setup. Als belastbare Ausgangswerte pro Node:
- CPU: 2 Sockel, genug Kerne fuer deine VM-Last + Reserve fuer den Ausfall eines Nodes (bei 3 Nodes muss jeder ~50 % Zusatzlast tragen koennen, wenn einer wegfaellt).
- RAM: VM-Bedarf + Ceph (Faustregel ~4 GB je OSD) + ZFS-ARC (falls lokales ZFS) + Reserve. RAM ist bei HA der limitierende Faktor, nicht CPU.
- Disks: getrennt fuer OS (2× SSD im ZFS-Mirror) und OSDs (Enterprise-SSD/NVMe mit Power-Loss-Protection). Ceph auf Consumer-SSDs ohne PLP ist quaelend langsam.
- NICs: mindestens 4 physische Ports, besser 6 (siehe unten).
Rechne bei HA immer mit n-1: Der Cluster muss den Verlust eines kompletten Nodes verkraften, ohne dass die verbleibenden ins Schwitzen kommen. Ein 3-Node-Cluster, der zu 80 % ausgelastet ist, hat kein HA – er hat nur noch nicht bemerkt, dass er keins hat.
Hardware-Spezifikationen im Ueberblick
Konkrete Anhaltswerte in drei Stufen. Mindestens = laeuft ueberhaupt; Best Practice = solide Produktion; Empfehlung = die Zielgroesse fuer den in diesem Guide beschriebenen Cluster.
Proxmox-VE-Node (je Node):
| Komponente | Mindestens | Best Practice | Empfehlung (dieser Guide) |
|---|---|---|---|
| Nodes im Cluster | 3 (oder 2 + QDevice) | 3–5, ungerade | 3+ ungerade, mit n-1-Reserve |
| CPU | 8 Kerne, 1 Sockel | 16+ Kerne, 2 Sockel, AVX2 | 2× 16 Kerne (32C/64T), Reserve fuer n-1 |
| RAM | 32 GB ECC | 128 GB ECC | 256 GB ECC (VM-Last + ~4 GB je OSD + ARC + Reserve) |
| Boot-/OS-Disks | 1× SSD | 2× SSD (ZFS-Mirror) | 2× 240 GB Enterprise-SSD, ZFS RAID1 |
| Ceph-OSD-Disks | 1× SSD | 3–4× SSD mit PLP | 4–8× Enterprise-NVMe mit Power-Loss-Protection |
| Netzwerk | 2× 1 GbE | 4 Ports (Corosync getrennt) | 6 Ports: 2×1G Corosync + 2×10G Mgmt/VM + 2×25G Ceph |
| Out-of-Band | – | IPMI/iDRAC/iLO | dediziertes OOB-Netz (Fencing + Remote Hands) |
Proxmox Backup Server (PBS):
| Komponente | Mindestens | Best Practice | Empfehlung (dieser Guide) |
|---|---|---|---|
| CPU | 4 Kerne | 8+ Kerne, AES-NI | 8–16 Kerne mit AES-NI (Dedup + Verify sind CPU-lastig) |
| RAM | 8 GB | 32 GB ECC | 64 GB ECC (skaliert mit Datastore-Groesse / Dedup-Index) |
| Boot-/OS-Disk | 1× SSD | 2× SSD (ZFS-Mirror) | 2× SSD, ZFS RAID1 |
| Datastore | HDD, einzeln | ZFS RAIDZ2 (HDD) | RAIDZ2 aus HDDs + NVMe-Special-Device fuer den Chunk-Index |
| Netzwerk | 1 GbE | 10 GbE | 10/25 GbE (kurzes Backup-Fenster) |
| Standort | – | anderer Brandabschnitt | getrennter Standort, dazu Offsite/Tape |
PBS-Besonderheiten: Der Dedup-Index lebt im RAM – mehr Datastore = mehr RAM. Und ein NVMe-Special-Device fuer die ZFS-Metadaten (bzw. den Chunk-Index) beschleunigt Verify und Garbage Collection auf HDD-Stores um Groessenordnungen. AES-NI in der CPU macht die client-seitige Verschluesselung praktisch kostenlos.
Warum mehrere NICs? (Das Netz-Design ist die halbe Miete)
Der haeufigste und teuerste Fehler beim Cluster-Bau: allen Traffic ueber eine einzige NIC (oder einen einzigen Bond) schicken. Im Labor mit drei Test-VMs faellt das nie auf – in Produktion unter Last kippt genau daran der Cluster. Um zu verstehen warum, muss man zwei grundverschiedene Anforderungen auseinanderhalten, die gern in einen Topf geworfen werden: Bandbreite und Latenz.
Bandbreite ist nicht Latenz
- Storage und Backup brauchen Bandbreite. Sie schaufeln grosse Datenmengen; ob ein einzelnes Paket 1 ms oder 5 ms braucht, ist egal – Hauptsache der Durchsatz stimmt.
- Corosync braucht Latenz. Es verschickt winzige Heartbeat-Pakete, aber die muessen konstant und schnell ankommen. Der Cluster erwartet Antworten im niedrigen Millisekunden-Bereich; wird die Leitung voll, staut sich der Puffer (Bufferbloat) und die Latenz springt von Bruchteilen einer Millisekunde auf zweistellige Werte.
Das ist der Kern des Problems: Ein voll ausgelastetes 10-GbE-Storage-Netz hat prima Bandbreite und trotzdem miserable Latenz. Legt man Corosync mit auf diese Leitung, sabotiert der Storage-Traffic den Cluster-Heartbeat – obwohl “genug Bandbreite” da waere. Bandbreite kann man kaufen; die Latenz eines gesaettigten Links nicht.
Der Kaskaden-Ausfall: was ohne Trennung wirklich passiert
Ein realistisches Szenario, wenn Corosync, Storage und Backup sich einen Bond teilen:
sequenceDiagram
participant BK as Backup-Job (nachts)
participant NIC as gemeinsame NIC / Bond
participant CO as Corosync
participant HA as HA-Manager
BK->>NIC: sichert 2 TB, NIC 100% ausgelastet
Note over NIC,CO: Latenz steigt 0.3ms auf 40ms (Bufferbloat)
Note over CO: Heartbeat-Timeout ueberschritten
CO->>HA: Node gilt als tot, Quorum-Neuwahl
HA->>HA: Watchdog fenced Node, VMs migrieren
Note over HA,NIC: Migrations-Traffic saettigt NIC zusaetzlich, naechster Node kippt
Ein einzelner Backup-Job loest also eine Kettenreaktion aus: Heartbeat-Timeout, Fencing, VM-Migration, dadurch noch mehr Last auf derselben Leitung, naechster Node kippt. Das ist kein hypothetisches Risiko, sondern der klassische “warum ist nachts der halbe Cluster neu gestartet?”-Vorfall – und er ist mit getrennten Netzen komplett vermeidbar.
Die Rollen im Detail
Deshalb trennen wir den Traffic physisch nach Rolle. Jede Zeile hat einen konkreten Grund, warum sie nicht mit den anderen geteilt werden darf:
| NIC-Rolle | Traffic | Bandbreite | Warum getrennt |
|---|---|---|---|
| Management / Web-UI | SSH, API, GUI | 1–10 GbE | Muss erreichbar bleiben, gerade wenn das Storage-Netz haengt – sonst kommst du im Stoerfall nicht mehr an den Cluster |
| Corosync Ring 0 | Cluster-Heartbeat | 1 GbE reicht | Latenz schlaegt Bandbreite; darf nie gesaettigt werden, sonst faelscht Bufferbloat den Cluster-Zustand |
| Corosync Ring 1 | Redundanter Heartbeat | 1 GbE | Zweiter, physisch getrennter Pfad, damit ein Switch-Ausfall den Cluster nicht kippt |
| Ceph Public | Client ↔ Ceph | 10/25 GbE | Storage-I/O saettigt schnell alles andere – gehoert weg von Corosync und Management |
| Ceph Cluster | OSD-Replikation | 10/25 GbE | Recovery nach einem Disk-/Node-Ausfall kann 10 GbE stundenlang dauerhaft fuellen |
| VM-Traffic | Gast-Netzwerke | 10 GbE, VLAN-Trunk | Mandanten-/Produktionstrennung; ein Gast darf die Infrastruktur-Netze nicht sehen |
Warum Ceph zwei eigene Netze (Public und Cluster)? Das Public-Netz traegt den Verkehr zwischen VMs und Ceph (Lesen/Schreiben). Das Cluster-Netz traegt die Replikation zwischen den OSDs. Faellt ein Node aus, muss Ceph tausende Objekte neu verteilen – dieser Recovery-Traffic ist massiv. Lage er auf dem Public-Netz, wuerde jedes Ceph-Recovery gleichzeitig die laufenden VMs ausbremsen. Getrennt merkt der Gast vom Recovery nichts.
Buendelung: Bonds, VLANs und Ringe
In der Praxis buendelt man diese Rollen auf 4–6 physische NICs. Das Beispiel unten zeigt einen Node mit 6 physischen NICs: zwei 10-GbE-Ports (eno3+eno4) werden per LACP zu bond0 (Management + VM-Trunk), zwei 25-GbE-Ports (ens1f0+ens1f1) zu bond1 (Ceph, Jumbo Frames), und zwei 1-GbE-Ports (eno1, eno2) gehen ungebuendelt direkt an die beiden Corosync-Ringe. Lies das Diagramm von links (physischer NIC) ueber die Buendelung (Mitte) nach rechts (Netz/Rolle):
flowchart LR
NIC3["eno3<br/>10GbE"] --> B0["bond0 · LACP"]
NIC4["eno4<br/>10GbE"] --> B0
NIC5["ens1f0<br/>25GbE"] --> B1["bond1 · LACP<br/>MTU 9000"]
NIC6["ens1f1<br/>25GbE"] --> B1
B0 -->|"Trunk, VLANs 2-4094"| VMBR0["vmbr0<br/>Management + VM-VLANs"]
B1 -->|"VLAN 30/31 · MTU 9000"| CEPHNET["Ceph<br/>Public + Cluster"]
NIC1["eno1<br/>1GbE"] -->|"VLAN 20"| RING0["Corosync<br/>Ring 0"]
NIC2["eno2<br/>1GbE"] -->|"VLAN 21"| RING1["Corosync<br/>Ring 1"]
classDef nic fill:#1f2a37,stroke:#e57000,stroke-width:2px,color:#fff;
classDef bond fill:#2a2140,stroke:#b07cff,stroke-width:2px,color:#fff;
classDef net fill:#0d1117,stroke:#8aa0b6,stroke-width:1.5px,color:#fff;
class NIC1,NIC2,NIC3,NIC4,NIC5,NIC6 nic;
class B0,B1 bond;
class VMBR0,CEPHNET,RING0,RING1 net;
Die sechs orangefarbenen Kaesten sind die tatsaechlich verbauten NICs, die zwei violetten die LACP-Bonds (je 2 NICs), rechts die Netze/Rollen. eno1/eno2 haben bewusst keinen Bond – warum, steht direkt darunter.
So sieht das konkret in /etc/network/interfaces aus (gekuerzt): ein LACP-Bond fuer Management + VM-Trunk, ein separater Bond mit Jumbo-Frames fuer Ceph, und Corosync bewusst ohne Bond auf einem dedizierten Port:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# Management + VM-Trunk (LACP-Bond)
auto bond0
iface bond0 inet manual
bond-slaves eno3 eno4
bond-mode 802.3ad
bond-miimon 100
bond-xmit-hash-policy layer3+4
auto vmbr0
iface vmbr0 inet static
address 10.10.0.11/24
gateway 10.10.0.1
bridge-ports bond0
bridge-vlan-aware yes
bridge-vids 2-4094
# Corosync Ring 0 - eigener Port, eigenes VLAN, KEIN Bond
auto eno1.20
iface eno1.20 inet static
address 10.20.0.11/24
mtu 1500
# Ceph - eigener Bond, Jumbo Frames durchgaengig
auto bond1
iface bond1 inet manual
bond-slaves ens1f0 ens1f1
bond-mode 802.3ad
mtu 9000
auto vmbr1
iface vmbr1 inet static
address 10.30.0.11/24
bridge-ports bond1
mtu 9000
Bridge-Modell: eine Trunk-Bridge statt eine Bridge je VLAN
Beim Anlegen der VM-Netze gibt es zwei Wege – und die Wahl entscheidet, wie gut das Setup im Cluster skaliert.
Variante A – Bridge-per-VLAN (klassisch): Pro VLAN eine eigene Bridge mit getaggtem Sub-Interface als Port, der Bridge-Name kodiert die VLAN-ID:
1
2
3
4
5
6
7
8
auto eno1.30
iface eno1.30 inet manual
auto vmbr30
iface vmbr30 inet manual
bridge-ports eno1.30 # vmbr30 -> VLAN 30, vmbr45 -> VLAN 45, ...
bridge-stp off
bridge-fd 0
Das ist selbsterklaerend (vmbr30 = VLAN 30) und im Homelab beliebt. Nachteil im Cluster: Jede neue VLAN heisst interfaces editieren und ifreload -a auf jedem Node, jede Bridge muss auf allen Nodes namensgleich existieren (sonst bricht die Live-Migration), und der Host bekommt schnell eine IP in jedem Segment – unnoetige Angriffsflaeche.
Variante B – VLAN-aware Bridge (Enterprise-Standard): Eine Trunk-Bridge, der VLAN-Tag steht am VM-NIC (Feld VLAN Tag in der GUI, net0: ...,tag=30). Neue VLAN = neuer Tag, keine Host-Aenderung:
1
2
3
4
5
6
7
auto vmbr0
iface vmbr0 inet static
address 10.10.0.11/24
gateway 10.10.0.1
bridge-ports bond0
bridge-vlan-aware yes
bridge-vids 2-4094
| Kriterium | A: Bridge-per-VLAN | B: VLAN-aware / SDN |
|---|---|---|
| Neue VLAN | interfaces + ifreload auf jedem Node |
Tag/VNet setzen, keine Host-Aenderung |
| Cluster-Konsistenz | Bridge muss ueberall namensgleich sein | VNet clusterweit identisch (SDN verteilt es) |
| Live-Migration / HA | Ziel-Node braucht gleichnamige Bridge | Tag/VNet reist mit der VM-Config |
| Host-Exposure | Host-IP in jedem VLAN | Mgmt bleibt in einem Segment |
| SDN nutzbar | nein | ja (baut genau darauf auf) |
Namenskonvention behalten, nur verlagern: Die Idee, die VLAN-ID in den Namen zu ziehen (
vmbr30), ist auch im Enterprise-Umfeld goldwert – nur haeng sie nicht an die Bridge, sondern an die SDN-VNets (vnet30,vnet45, …). So behaeltst du die Lesbarkeit und bekommst clusterweite Konsistenz, Live-Migration und automatische Verteilung. Genau das macht Schritt 7 (SDN).
Fuer einen einzelnen Node ist Variante A voellig in Ordnung; sobald Cluster mit HA/Migration im Spiel ist, ist Variante B (bzw. SDN) der Standard.
Warum zwei Corosync-Ringe statt eines Bonds
Ein naheliegender Gedanke: “Ich packe Corosync doch einfach auf einen redundanten LACP-Bond, dann ist es auch ausfallsicher.” Das ist ein Trugschluss. Ein Bond schuetzt nur gegen den Ausfall eines NIC-Ports oder Kabels – beide Bond-Mitglieder haengen aber typischerweise am selben Switch (oder an einem MLAG-Paar mit gemeinsamer Konfiguration). Ein Switch-Absturz, ein fehlerhaftes Firmware-Update oder ein STP-Sturm nimmt dir den kompletten Bond auf einen Schlag.
Corosync loest das anders und besser: Es kennt nativ mehrere unabhaengige Ringe (link0, link1). Du legst Ring 0 und Ring 1 auf zwei physisch getrennte Ports, zwei getrennte VLANs und zwei getrennte Switches. Faellt ein kompletter Pfad (Switch, VLAN, Verkabelung) aus, laeuft der Heartbeat ueber den anderen weiter – ohne dass ein einziger Node das Quorum verliert. Das ist echte Pfad-Redundanz, die ein Bond prinzipbedingt nicht liefern kann.
Merksatz: Bonding = Redundanz gegen Port-/Kabel-Ausfall (fuer Storage/Management sinnvoll). Zwei Corosync-Ringe = Redundanz gegen Pfad-/Switch-Ausfall (fuer den Cluster-Heartbeat Pflicht). Beides ergaenzt sich, das eine ersetzt das andere nicht.
Wie viele Ports brauchst du wirklich?
Die Portzahl skaliert mit dem Anspruch – eine ehrliche Staffelung:
| Ports | Aufteilung | Geeignet fuer |
|---|---|---|
| 2 | 1 Bond, alles gemeinsam | Nur Lab / PoC – nicht fuer Produktion |
| 4 | 1 Bond (Mgmt + VM) + 2× 1 GbE Corosync (Ring 0/1) | Einstiegs-Produktion, moderate I/O |
| 6 | + eigener Ceph-Bond (2× 10/25 GbE) | Empfohlener Standard fuer HA + Ceph |
| 8 | + Ceph Public und Cluster auf getrennten Bonds | I/O-intensive oder grosse Cluster |
Die 4-Port-Variante ist das realistische Minimum fuer einen ernsthaften Cluster; ab 6 Ports wird es entspannt. Wichtiger als “moeglichst viele” ist die richtige Zuordnung: Corosync zuerst absichern, dann Storage trennen.
Out-of-Band: das Netz, das man vergisst
Zusaetzlich zu den Daten-NICs gehoert jeder Enterprise-Node an ein Out-of-Band-Management (IPMI / iDRAC / iLO) in einem eigenen, gesicherten Segment. Zwei Gruende: Erstens braucht das Fencing im Idealfall genau diesen Kanal (der Cluster kann einen haengenden Node ueber IPMI hart abschalten, selbst wenn dessen OS voellig weg ist). Zweitens ist es im Air-Gap deine einzige “Remote Hands” – es gibt keinen Cloud-KVM, mit dem du dich auf die Konsole eines Nodes schaltest. Ohne IPMI stehst du bei einem Boot-Problem im Serverraum.
Jumbo Frames fuer Storage
Das Ceph-Netz laeuft mit MTU 9000 (Jumbo Frames). Bei 10/25 GbE und grossen Bloecken reduziert das die Paket-Anzahl (und damit CPU-Overhead) deutlich. Wichtig: durchgaengig – NIC, Bond, Bridge/VLAN und die Switches muessen 9000 koennen, sonst zerschiesst du dir die Verbindung mit fragmentierten Paketen. Management- und Corosync-Netze bleiben bei MTU 1500.
Adresskonzept fuer ein grosses Netz
Statt eines flachen /24 planen wir sauber getrennte Segmente. Beispielhaftes Schema (an dein Netz anpassen):
| Segment | VLAN | Netz | Zweck |
|---|---|---|---|
| Management | 10 | 10.10.0.0/24 |
PVE-Web-UI, SSH, API |
| Corosync Ring 0 | 20 | 10.20.0.0/24 |
Cluster-Heartbeat |
| Corosync Ring 1 | 21 | 10.21.0.0/24 |
Redundanter Heartbeat |
| Ceph Public | 30 | 10.30.0.0/24 |
Storage-Zugriff |
| Ceph Cluster | 31 | 10.31.0.0/24 |
OSD-Replikation |
| VM-Prod | 100–199 | 172.16.0.0/16 |
Produktions-VMs (per SDN) |
| VM-Mgmt-Services | 40 | 10.40.0.0/24 |
Mirror, CA, PBS, NTP, AD |
Andere Blöcke sind Konvention, nicht Pflicht. Dass hier die Infrastruktur in
10.xund die Workload-VMs in172.16.xliegen, dient allein der optischen Trennung von Underlay (die Plattform) und Overlay (die VMs). Technisch sind10.0.0.0/8,172.16.0.0/12und192.168.0.0/16gleichwertig – es sind RFC-1918-Blöcke, keine “Klassen” (modernes Networking ist klassenlos, CIDR). Genauso valide waere alles in einem10.0.0.0/8(z. B. VM-Prod in10.100.0.0/16). Was im grossen Netz wirklich zaehlt: (1) keine Ueberschneidung mit bereits belegten Bereichen (Routing, VPN-Kopplung, Firmen-Fusionen) und (2) der IPAM-Policy deines Netzes folgen. In vielen Unternehmen ist10/8bereits flaechendeckend vergeben – dann weicht man fuer neue SDN-/VM-Segmente bewusst auf172.16/12aus.
Feste Node-Adressen (Beispiel Management-Segment):
1
2
3
pve-01 10.10.0.11 Corosync0 10.20.0.11 Corosync1 10.21.0.11 Ceph 10.30.0.11 / 10.31.0.11
pve-02 10.10.0.12 Corosync0 10.20.0.12 Corosync1 10.21.0.12 Ceph 10.30.0.12 / 10.31.0.12
pve-03 10.10.0.13 Corosync0 10.20.0.13 Corosync1 10.21.0.13 Ceph 10.30.0.13 / 10.31.0.13
Schritt 2: Air-Gap-Basisdienste bereitstellen
Ein geschlossenes Netz aendert nicht was du tust, aber woher die Bausteine kommen. Vier Dienste musst du vorher intern bereitstellen, sonst kommst du beim Cluster-Aufbau nicht weit:
flowchart LR
subgraph EXT["Einschleus-Zone (temporaer / getrennt)"]
DL["Download-Host mit Internet"]
end
subgraph INT["Geschlossenes Netz"]
MIR["APT-Mirror<br/>Debian + PVE + Ceph"]
NTP["NTP-Server<br/>chrony"]
DNS["Interner DNS<br/>Forward + Reverse"]
CA["Interne CA<br/>step-ca (ACME)"]
end
DL -->|"Datentraeger / Diode"| MIR
MIR --> PVE["Proxmox-Nodes"]
NTP --> PVE
DNS --> PVE
CA --> PVE
Arbeite die folgenden vier Teilschritte der Reihe nach ab. Jeder endet mit einem Verifikations-Befehl, mit dem du pruefst, ob es geklappt hat, bevor du weitergehst.
1.1 Interner Paket-Mirror
Ziel: Debian-, PVE- und Ceph-Pakete ohne Internet auf allen Nodes verfuegbar machen.
a) In der Einschleus-Zone (ein Host mit Internet) apt-mirror einrichten:
1
apt-get install -y apt-mirror
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# /etc/apt/mirror.list (Einschleus-Host)
set base_path /srv/mirror
set nthreads 10
set _tilde 0
# Debian Base (Bookworm) - main + security
deb http://deb.debian.org/debian bookworm main contrib
deb http://security.debian.org/debian-security bookworm-security main contrib
# Proxmox VE - No-Subscription (Umgebungen ohne Subscription)
deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription
# Proxmox Ceph (Reef)
deb http://download.proxmox.com/debian/ceph-reef bookworm no-subscription
clean http://deb.debian.org/debian
b) Spiegel ziehen und ausschleusen – als wiederholbares Skript (auch fuer spaetere Updates):
1
2
3
4
5
6
#!/usr/bin/env bash
# sync-mirror.sh - auf dem Einschleus-Host ausfuehren
set -euo pipefail
apt-mirror # zieht/aktualisiert /srv/mirror
rsync -a --delete /srv/mirror/mirror/ /media/transfer/pve-mirror/ # auf Wechseldatentraeger/Diode
echo "Fertig - Datentraeger ins geschlossene Netz bringen und dort nach /srv/mirror einspielen."
c) Im geschlossenen Netz den Spiegel per Webserver bereitstellen (nginx-Beispiel):
1
2
3
4
5
6
7
8
# /etc/nginx/sites-available/mirror
server {
listen 80;
server_name mirror.corp.local;
root /srv/mirror; # hierhin wurde der Spiegel eingespielt
autoindex on;
location / { try_files $uri $uri/ =404; }
}
1
2
ln -s /etc/nginx/sites-available/mirror /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
d) Auf jedem Node die Paketquellen auf den internen Host umbiegen:
1
2
3
4
5
# /etc/apt/sources.list.d/pve-internal.list (jeder Node)
deb http://mirror.corp.local/debian bookworm main contrib
deb http://mirror.corp.local/debian-security bookworm-security main contrib
deb http://mirror.corp.local/pve bookworm pve-no-subscription
deb http://mirror.corp.local/ceph-reef bookworm no-subscription
Verifikation (auf einem Node):
1
2
curl -sI http://mirror.corp.local/pve/dists/bookworm/Release | head -1 # -> HTTP/1.1 200 OK
apt update # -> keine "Could not resolve"-Fehler
Der Mirror ist auch dein Update-Kanal. Sicherheitsupdates spielst du ein, indem du
sync-mirror.shneu laufen laesst, den Delta-Stand einschleust und auf den Nodesapt update && apt full-upgradefaehrst. Plane das als wiederkehrenden Prozess – ein Air-Gap ist keine Ausrede fuer veraltete Pakete.
1.2 Interne Zeit (NTP)
Ziel: Alle Nodes laufen auf exakt gleicher Zeit – Corosync, Ceph und TLS reagieren allergisch auf Drift.
Auf dem internen NTP-Server (idealerweise an DCF77/GPS oder AD-Zeit):
1
2
3
4
5
# /etc/chrony/chrony.conf (ntp.corp.local)
# externe Quelle ODER lokale Referenzuhr; hier: als Stratum-Quelle fuers Netz
allow 10.0.0.0/8
local stratum 8
makestep 1.0 3
Auf jedem Node:
1
2
3
4
5
6
7
apt-get install -y chrony
cat > /etc/chrony/chrony.conf <<'EOF'
server ntp.corp.local iburst
makestep 1.0 3
rtcsync
EOF
systemctl restart chrony
Verifikation (auf einem Node):
1
2
chronyc sources -v # -> ntp.corp.local mit "^*" (synchronisiert)
chronyc tracking # -> Leap status: Normal, kleiner Offset
1.3 Interner DNS (Forward + Reverse)
Ziel: Jeder Node loest sich selbst und alle anderen in beide Richtungen auf (A + PTR). Fehlt das Reverse, bricht der Cluster-Join gern an unerwarteter Stelle ab.
Neben den DNS-Eintraegen (A + PTR fuer jede Node-IP im Management-Segment) traegst du zur Sicherheit auf jedem Node einen /etc/hosts-Fallback ein:
1
2
3
4
5
# /etc/hosts (jeder Node)
127.0.0.1 localhost
10.10.0.11 pve-01.corp.local pve-01
10.10.0.12 pve-02.corp.local pve-02
10.10.0.13 pve-03.corp.local pve-03
Verifikation (auf jedem Node):
1
2
3
hostname -f # -> pve-01.corp.local (FQDN korrekt)
dig +short pve-02.corp.local # -> 10.10.0.12 (Forward)
dig +short -x 10.10.0.12 # -> pve-02.corp.local. (Reverse/PTR)
1.4 Interne CA im Trust-Store
Ziel: Das Root-Zertifikat deiner internen CA ist auf jedem Node (und spaeter jedem Client) vertraut – Voraussetzung fuer die spaeteren PVE-Zertifikate (Details im Zertifikate-Kapitel).
Root-CA-Zertifikat einspielen (copy&paste, auf jedem Node):
1
2
3
# corp-root-ca.crt vom internen Mirror/Share holen und als vertrauenswuerdig markieren
cp corp-root-ca.crt /usr/local/share/ca-certificates/corp-root-ca.crt
update-ca-certificates # -> "1 added"
Verifikation:
1
openssl verify -CApath /etc/ssl/certs /usr/local/share/ca-certificates/corp-root-ca.crt # -> OK
Schritt 3: Nodes installieren
3.1 ISO einschleusen und booten
Die Proxmox-VE-ISO wird einmalig eingeschleust und intern verteilt – per USB oder, fuer viele Nodes eleganter, per PXE/HTTP-Boot vom internen Mirror. Fuer identische Nodes lohnt die Proxmox Automated Installation mit Answer-File.
3.2 Installer durchklicken (je Node)
- Vom Installer booten und als Ziel ZFS (RAID1) ueber die zwei OS-SSDs waehlen – ein gespiegeltes Root ueberlebt den Ausfall einer OS-Disk.
- Zeitzone, Tastatur, ein starkes root-Passwort und eine gueltige Admin-Mail (fuer lokale Notifications) setzen.
- Management-Netz konfigurieren: FQDN
pve-01.corp.local, IP10.10.0.11/24, Gateway, interner DNS. - Nach dem ersten Boot per SSH/Konsole anmelden und mit dem Post-Install-Skript (unten) fertig konfigurieren.
3.3 Automatisierte Installation mit Answer-File (viele/identische Nodes)
Das manuelle Durchklicken (3.2) je Server per IPMI-KVM ist bei mehreren Nodes muehsam und fehleranfaellig. Seit PVE 8.2 gibt es die Automated Installation: Eine Datei answer.toml beschreibt die komplette Installation, das Werkzeug proxmox-auto-install-assistant backt sie in die offizielle ISO. Jeder Node wird damit bit-identisch und ohne Interaktion aufgesetzt – im Air-Gap Gold wert, weil du dich sonst per Remote-Konsole durch jeden Installer klicken muesstest.
flowchart LR
A["answer.toml<br/>+ offizielle ISO"] --> B["prepare-iso<br/>(Answer eingebettet)"]
B --> C["pve-XX-auto.iso"]
C --> D["Boot per USB/IPMI<br/>installiert automatisch"]
D --> E["Reboot<br/>dann pve-postinstall.sh"]
a) Werkzeug installieren (auf dem Einschleus-Host):
1
apt-get install -y proxmox-auto-install-assistant
b) answer.toml schreiben (Beispiel: ZFS-Mirror, feste IP, dt. Tastatur):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[global]
keyboard = "de"
country = "de"
fqdn = "pve-01.corp.local"
mailto = "[email protected]"
timezone = "Europe/Berlin"
# Passwort-Hash erzeugen mit: openssl passwd -6
root_password_hashed = "$6$SALT$HASH..."
reboot_on_error = false
[network]
source = "from-answer"
cidr = "10.10.0.11/24"
gateway = "10.10.0.1"
dns = "10.40.0.53"
# Management-NIC per MAC auswaehlen (robuster als der Interface-Name):
filter.ID_NET_NAME_MAC = "*aabbccddee01"
[disk-setup]
filesystem = "zfs"
zfs.raid = "raid1"
disk_list = ["nvme0n1", "nvme1n1"]
c) Validieren und ISO backen:
1
2
3
4
5
6
7
# Syntax/Vollstaendigkeit pruefen
proxmox-auto-install-assistant validate-answer answer.toml
# Answer in die offizielle ISO einbetten
proxmox-auto-install-assistant prepare-iso proxmox-ve_8.x.iso \
--fetch-from iso --answer-file answer.toml \
--output pve-01-auto.iso
d) Mehrere Nodes – je Node ein Answer-File (Unterschiede: fqdn, cidr, NIC-MAC), dann alle ISOs in einem Rutsch:
1
2
3
4
5
for n in 01 02 03; do
proxmox-auto-install-assistant prepare-iso proxmox-ve_8.x.iso \
--fetch-from iso --answer-file answer-${n}.toml \
--output pve-${n}-auto.iso
done
Noch weniger Handarbeit – HTTP-Fetch: Mit
--fetch-from httpholt der Installer das Answer-File zur Laufzeit von einer URL (z. B. vom internen Mirror). Ein kleiner Endpoint liefert pro Node das richtige File anhand von Seriennummer/MAC – dann brauchst du nur eine ISO fuer die ganze Flotte. Im Air-Gap zeigt die URL auf einen internen Host.
e) Verteilen und booten: Die ISO per USB oder IPMI Virtual Media mounten und booten – die Installation laeuft vollautomatisch durch, der Node rebootet und ist bereit fuer das Post-Install-Skript (3.4).
3.4 Post-Install-Skript (auf jedem Node)
Statt jeden Node haendisch nachzuziehen, laeuft ein einziges Skript durch: Repos umbiegen, Enterprise-Repo deaktivieren, NTP setzen, ZFS-ARC begrenzen, Kernel-Tuning fuer Cluster/Storage, Update. Oben die drei Variablen anpassen, dann copy&paste:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/usr/bin/env bash
# pve-postinstall.sh - auf JEDEM Node nach der Installation ausfuehren (als root)
set -euo pipefail
MIRROR="mirror.corp.local" # interner APT-Mirror
NTP="ntp.corp.local" # interner Zeitserver
ARC_MAX_GB=16 # ZFS-ARC-Obergrenze (an RAM/Ceph anpassen)
echo ">> 1/6 Enterprise-Repos deaktivieren"
rm -f /etc/apt/sources.list.d/pve-enterprise.list /etc/apt/sources.list.d/ceph.list
echo ">> 2/6 interne Paketquellen setzen"
cat > /etc/apt/sources.list.d/pve-internal.list <<EOF
deb http://${MIRROR}/debian bookworm main contrib
deb http://${MIRROR}/debian-security bookworm-security main contrib
deb http://${MIRROR}/pve bookworm pve-no-subscription
deb http://${MIRROR}/ceph-reef bookworm no-subscription
EOF
echo ">> 3/6 Zeitserver (chrony)"
apt-get install -y chrony
cat > /etc/chrony/chrony.conf <<EOF
server ${NTP} iburst
makestep 1.0 3
rtcsync
EOF
systemctl restart chrony
echo ">> 4/6 ZFS-ARC begrenzen (${ARC_MAX_GB} GB)"
echo "options zfs zfs_arc_max=$((ARC_MAX_GB*1024*1024*1024))" > /etc/modprobe.d/zfs.conf
update-initramfs -u -k all
echo ">> 5/6 Kernel-Tuning fuer Cluster/Storage"
cat > /etc/sysctl.d/99-pve-cluster.conf <<'EOF'
# groessere Netzpuffer fuer Ceph/Migration
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# mehr ARP-Eintraege in grossen L2-Segmenten
net.ipv4.neigh.default.gc_thresh1 = 4096
net.ipv4.neigh.default.gc_thresh2 = 8192
net.ipv4.neigh.default.gc_thresh3 = 16384
# Kernel-Panic-Reboot als letzte Fencing-Reserve
kernel.panic = 10
EOF
sysctl --system >/dev/null
echo ">> 6/6 System aktualisieren"
apt-get update && apt-get -y full-upgrade
echo ">> fertig - bitte Node einmal neu starten (reboot)"
Ohne Subscription blendet die GUI beim Login einen Hinweis ein – rein kosmetisch, ohne Funktionsbezug. Er laesst sich patchen, aber da das offizielle Verhalten sich mit Updates aendern kann, hier bewusst kein Snippet. Ignorieren oder ueber ein separat gepflegtes Post-Update-Skript entfernen.
3.5 Verifikation (je Node)
1
2
3
4
pveversion # -> pve-manager/8.x, kein Fehler
apt-get update # -> zieht sauber vom internen Mirror
chronyc tracking | grep Stratum # -> synchronisiert
arcstat 1 1 2>/dev/null || cat /sys/module/zfs/parameters/zfs_arc_max # -> Limit gesetzt
Schritt 4: Cluster aufbauen (redundantes Corosync)
Jetzt kommt der Kern. Voraussetzung: Schritt 1–3 sind auf allen drei Nodes durch, die Ring-Netze (VLAN 20/21) sind konfiguriert und die Nodes sehen sich (ping auf den Ring-Adressen).
4.1 Firewall fuer Corosync freigeben
Corosync spricht UDP 5405–5412 auf den Ring-Netzen. Sind Host-Firewalls aktiv, muss das offen sein, sonst findet der Cluster nicht zusammen:
1
2
# auf jedem Node (Beispiel nftables/pve-firewall: Ring-Netze erlauben)
# Cluster-Netze 10.20.0.0/24 und 10.21.0.0/24 duerfen UDP 5405-5412
4.2 Cluster auf pve-01 erzeugen
Auf dem ersten Node den Cluster mit zwei Corosync-Links (redundante Ringe) anlegen:
1
2
3
4
# NUR auf pve-01
pvecm create DC-CLUSTER \
--link0 address=10.20.0.11 \
--link1 address=10.21.0.11
4.3 pve-02 und pve-03 beitreten
Der Join laeuft vom beitretenden Node aus (nicht von pve-01), jeweils mit dessen eigenen Ring-Adressen:
1
2
3
4
5
6
7
8
9
# Auf pve-02
pvecm add 10.20.0.11 \
--link0 address=10.20.0.12 \
--link1 address=10.21.0.12
# Auf pve-03
pvecm add 10.20.0.11 \
--link0 address=10.20.0.13 \
--link1 address=10.21.0.13
4.4 Verifikation
1
2
3
4
5
6
7
8
pvecm status
# -> Quorate: Yes | Nodes: 3 | Expected votes: 3
corosync-cfgtool -s
# -> LINK ID 0 ... enabled connected UND LINK ID 1 ... enabled connected
pvecm nodes
# -> pve-01, pve-02, pve-03 alle mit Status "online"
Die resultierende
corosync.confhat pro Node zwei Ringadressen. Editiere Corosync nach dem Setup nie direkt – und wenn, dann nur ueber/etc/pve/corosync.conf(die vom Cluster synchronisierte Version), mit erhoehtemconfig_version, und nie bei fehlendem Quorum (sonst zerlegst du den Cluster).
Quorum: Warum ungerade Zahlen zaehlen
Ein Cluster trifft Entscheidungen per Mehrheit. Bei 3 Nodes ist die Mehrheit 2 – faellt ein Node aus, bleiben 2 von 3, Quorum steht, HA laeuft weiter. Bei 2 Nodes ist die Mehrheit 2 – faellt einer aus, ist Quorum weg und beide verweigern den Dienst (bzw. riskieren Split-Brain). Deshalb ist 3 das praktische Minimum.
flowchart LR
subgraph OK["3 Nodes - 1 faellt aus"]
direction LR
Q1["pve-01 OK"]
Q2["pve-02 OK"]
Q3["pve-03 DOWN"]
QV["2/3 = Quorum -> Cluster laeuft"]
end
Kannst du (etwa aus Kostengruenden) nur 2 vollwertige Nodes stellen, ergaenzt du ein QDevice: ein kleiner externer Host (z. B. eine VM auf einem separaten Standort/Server) mit corosync-qnetd, der als Tie-Breaker eine dritte Stimme liefert:
1
2
3
4
5
# Auf dem QDevice-Host (nicht Teil des Clusters)
apt install corosync-qnetd
# Auf einem Cluster-Node
apt install corosync-qdevice
pvecm qdevice setup 10.40.0.9 # IP des QDevice-Hosts
Schritt 5: Ceph-Storage einrichten
HA bedeutet, dass eine VM nach dem Ausfall ihres Nodes auf einem anderen Node wieder startet. Das geht nur, wenn ihre Disk nicht am ausgefallenen Node haengt. Also brauchst du Shared Storage. Drei enterprise-taugliche Optionen:
| Option | Wann sinnvoll | Air-Gap |
|---|---|---|
| Ceph (RBD) | Storage soll im Cluster leben, keine externe SAN | Voll offline moeglich |
| iSCSI / FC-SAN | Es gibt bereits ein Enterprise-SAN | Offline, SAN-abhaengig |
| NFS | Einfacher shared Storage, geringere Anforderungen | Offline, NAS-abhaengig |
Fuer einen selbsttragenden Cluster ohne externe Abhaengigkeit ist Ceph die Standard-Wahl – es ist in Proxmox integriert und braucht keine Internet-Verbindung.
5.1 Ceph installieren und Netze trennen
Ceph bekommt zwei eigene Netze (Public + Cluster) aus Schritt 1. Das Cluster-Netz traegt die OSD-Replikation (Recovery-Traffic!) und muss vom Public-Netz getrennt sein.
1
2
3
4
5
# a) auf JEDEM Node installieren
pveceph install --repository no-subscription --version reef
# b) NUR auf pve-01 initialisieren, mit getrennten Netzen
pveceph init --network 10.30.0.0/24 --cluster-network 10.31.0.0/24
5.2 Monitore und Manager (auf jedem Node)
MONs bilden ihr eigenes Quorum – deshalb drei (ungerade). MGRs liefern Statistiken/Dashboard; mindestens zwei fuer Redundanz.
1
2
3
# auf pve-01, pve-02, pve-03 jeweils:
pveceph mon create
pveceph mgr create
Verifikation:
1
2
ceph mon stat # -> 3 mons, quorum pve-01,pve-02,pve-03
ceph -s | grep mgr # -> active + standbys
5.3 OSDs anlegen (je Datendisk)
Jede Enterprise-SSD/NVMe wird ein OSD. Die Disk muss leer sein (ggf. vorher ceph-volume lvm zap --destroy /dev/nvmeXn1):
1
2
3
# je Datendisk auf JEDEM Node (Beispiel: zwei NVMe pro Node)
pveceph osd create /dev/nvme1n1
pveceph osd create /dev/nvme2n1
Verifikation:
1
2
ceph osd tree
# -> je Host ein Block mit seinen OSDs, alle "up"
5.4 Pool anlegen und in Proxmox einbinden
1
2
3
4
5
# NUR einmal (auf pve-01): RBD-Pool mit dreifacher Redundanz
pveceph pool create vm-pool --size 3 --min_size 2 --pg_autoscale_mode on
# als Storage fuer VM-Disks/Container registrieren
pvesm add rbd ceph-vm --pool vm-pool --content images,rootdir
size=3 / min_size=2 ist die Standard-Sicherheit: Jeder Block liegt auf 3 OSDs (auf 3 verschiedenen Nodes, dank Default-CRUSH-Regel), und Schreibzugriffe funktionieren noch, solange 2 Kopien erreichbar sind. Faellt ein Node aus, bleiben 2 – die Daten sind sicher und der Cluster schreibt weiter.
5.5 Verifikation (Gesamtzustand)
1
2
3
4
ceph -s
# -> health: HEALTH_OK | mon: 3 | mgr: active | osd: N up, N in
pvesm status
# -> ceph-vm rbd active
flowchart TB
VM["VM-Disk (RBD-Image)"] --> P["Pool vm-pool (size=3)"]
P --> O1["OSD @ pve-01"]
P --> O2["OSD @ pve-02"]
P --> O3["OSD @ pve-03"]
O3 -. "faellt aus" .- X["Recovery re-repliziert<br/>auf verbleibende OSDs"]
CRUSH-Failure-Domain: Die Default-Regel verteilt Kopien pro Host – genau richtig fuer einen 3-Node-Cluster. In groesseren Setups mit mehreren Racks/Reihen setzt du die Failure-Domain auf
rack, damit ein kompletter Rack-Ausfall keine Daten kostet. Das ist der Punkt, an dem Ceph im Enterprise glaenzt.
Schritt 6: Hochverfuegbarkeit (HA) konfigurieren
Mit Quorum (Corosync) und Shared Storage (Ceph) sind die Voraussetzungen erfuellt. HA in Proxmox besteht aus zwei Bausteinen: HA-Groups (wo darf eine VM laufen) und Fencing (wie stellt der Cluster sicher, dass ein “toter” Node wirklich tot ist, bevor die VM woanders startet).
Mit Quorum (Schritt 4) und Shared Storage (Schritt 5) sind die Voraussetzungen fuer HA erfuellt.
6.1 Warum Fencing zuerst kommt
Bevor der Cluster eine VM auf einem anderen Node neu startet, muss er sicher sein, dass sie auf dem alten Node nicht mehr laeuft – sonst schreiben zwei Instanzen gleichzeitig auf dieselbe RBD-Disk (Datenkorruption). Proxmox nutzt dafuer einen Watchdog: Verliert ein Node das Quorum, tritt der Watchdog nach ~60 s in Kraft und rebootet den Node hart. Erst danach uebernehmen die anderen.
Ohne funktionierendes Fencing ist HA gefaehrlich, nicht hilfreich. Der Software-
softdogist der Default und funktioniert; ein echter IPMI-/iTCO-Hardware-Watchdog ist im Enterprise aber Pflicht (er greift auch, wenn der Kernel selbst haengt). Und: Teste das Fencing bewusst, nicht erst im echten Ausfall.
6.2 Hardware-Watchdog aktivieren (auf jedem Node)
Statt des Software-softdog den IPMI-Watchdog verwenden:
1
2
3
4
5
6
7
8
9
10
11
12
13
# a) IPMI-Watchdog-Modul laden und persistent machen
echo "ipmi_watchdog" >> /etc/modules
modprobe ipmi_watchdog
# b) Proxmox anweisen, diesen Watchdog zu nutzen
if grep -q '^WATCHDOG_MODULE=' /etc/default/pve-ha-manager; then
sed -i 's/^WATCHDOG_MODULE=.*/WATCHDOG_MODULE=ipmi_watchdog/' /etc/default/pve-ha-manager
else
echo 'WATCHDOG_MODULE=ipmi_watchdog' >> /etc/default/pve-ha-manager
fi
# c) Watchdog-Multiplexer neu starten
systemctl restart watchdog-mux
Verifikation:
1
journalctl -u watchdog-mux -b | grep -i watchdog # -> "Using watchdog 'ipmi_watchdog'"
6.3 HA-Group und Ressource anlegen
Eine HA-Group legt fest, auf welchen Nodes eine VM bevorzugt laeuft (mit Prioritaeten). Beispiel: prod-vms bevorzugt pve-01/02, faellt auf pve-03 zurueck.
1
2
3
4
5
6
7
# HA-Group mit Node-Prioritaeten (hoehere Zahl = bevorzugt)
ha-manager groupadd prod-vms --nodes "pve-01:2,pve-02:2,pve-03:1"
# VM 1001 unter HA stellen
ha-manager add vm:1001 --group prod-vms --state started --max_restart 2 --max_relocate 2
ha-manager status # -> service vm:1001 (pve-01, started)
6.4 Failover bewusst testen
Der einzige Weg, HA zu vertrauen, ist es auszuloesen. VM 1001 laeuft auf pve-01 – jetzt den Node hart simulieren (echter Test, kein sauberes Shutdown):
1
2
# auf pve-01 einen Kernel-Panic ausloesen (harter Ausfall, Watchdog muss fencen)
echo 1 > /proc/sys/kernel/sysrq && echo c > /proc/sysrq-trigger
1
2
3
# auf pve-02 beobachten:
watch -n2 ha-manager status
# nach ~1-2 min: service vm:1001 (pve-02, started) -> Failover erfolgreich
Erwartung: pve-01 wird gefenced (rebootet), vm:1001 startet automatisch auf pve-02, die RBD-Disk wird dort gemountet. Der Ablauf im Detail:
sequenceDiagram
participant N1 as pve-01 (VM 1001)
participant CL as Cluster (Quorum)
participant WD as Watchdog
participant N2 as pve-02
N1->>CL: Heartbeat...
Note over N1: Node crasht
N1--xCL: Kein Heartbeat
CL->>WD: Node ohne Quorum -> fence
WD->>N1: Hard-Reset (Watchdog)
CL->>N2: VM 1001 neu starten
N2->>N2: RBD-Disk mounten, VM boot
Schritt 7: SDN einrichten
In einem grossen Netz willst du VM-Netze zentral im Cluster verwalten, statt auf jedem Node haendisch Bridges und VLANs zu pflegen (genau der Nachteil des Bridge-per-VLAN-Modells aus Schritt 1). Proxmox SDN (“Software Defined Networking”) ist die Antwort: Du beschreibst das Netz einmal deklarativ, der Cluster rollt es auf alle Nodes identisch aus. Ein neuer Node bekommt beim Beitritt automatisch dieselben Netze – ohne dass du eine einzige Zeile interfaces anfasst.
Das mentale Modell: Zone -> VNet -> Subnet (-> Controller)
SDN stapelt vier Objekte. Wer die Rollen einmal sauber trennt, versteht den Rest von selbst:
- Zone = der Transport: wie kommen Frames von Node zu Node? Per VLAN-Tag auf dem Trunk (
vlan), per VXLAN-Overlay (vxlan) oder per EVPN mit BGP-Routing (evpn). Die Zone bindet an das physische Fundament (Trunk-Bridge bzw. Underlay-Peers). - VNet = das eigentliche Netz. Es wird auf jedem Node zu einer echten Bridge – der clusterweite Ersatz fuer deine
vmbr30/vmbr45. Hier haengst du die VM-NICs ein. - Subnet = die IP-Ebene: CIDR, Gateway, IPAM (Adressvergabe) und optional DHCP. Ohne Subnet ist ein VNet reines L2.
- Controller = nur bei EVPN: der BGP-Sprecher (FRR), der Routen und MAC-Adressen zwischen den Nodes verteilt.
flowchart TB
Z["Zone corpvlan<br/>Typ VLAN · Trunk vmbr0"] --> V["VNet prod100<br/>Tag 100 = Bridge je Node"]
V --> S["Subnet 172.16.100.0/24<br/>Gateway .1 · IPAM pve"]
S --> APPLY(["pvesh set /cluster/sdn<br/>= Apply"])
APPLY --> N1["pve-01<br/>Bridge prod100"]
APPLY --> N2["pve-02<br/>Bridge prod100"]
APPLY --> N3["pve-03<br/>Bridge prod100"]
classDef def fill:#1f2a37,stroke:#e57000,stroke-width:2px,color:#fff;
classDef node fill:#0d1117,stroke:#8aa0b6,stroke-width:1.5px,color:#fff;
class Z,V,S def;
class N1,N2,N3 node;
Wo das landet: Alle Definitionen liegen als Textdateien unter /etc/pve/sdn/*.cfg (zones.cfg, vnets.cfg, subnets.cfg, controllers.cfg). Weil /etc/pve das Cluster-Dateisystem (pmxcfs) ist, sind sie sofort auf jedem Node vorhanden – du editierst nie eine Node-lokale Datei.
Pending vs. Applied: Neue/geaenderte Objekte sind zunaechst pending (nur vorgemerkt). Erst pvesh set /cluster/sdn (GUI: Datacenter -> SDN -> Apply) generiert daraus die echte Node-Konfiguration und laedt das Netzwerk neu. So aenderst du gefahrlos mehrere Objekte und rollst sie atomar aus.
Zone-Typen und wann welcher
| Zone-Typ | Prinzip | Braucht | Wann |
|---|---|---|---|
| VLAN | 802.1Q-Tags auf dem vorhandenen Trunk | VLAN-aware Trunk-Bridge | Es gibt bereits eine VLAN-Fabric (haeufigster Enterprise-Fall) |
| VXLAN | L2-Overlay ueber ein beliebiges L3-Underlay | Erreichbarkeit der Node-IPs (Peers) | Viele isolierte Netze, mandantenfaehig, ohne die Fabric anzufassen |
| EVPN | VXLAN + BGP-Controller, integriertes L3-Routing | Controller (FRR) + Exit-Nodes | Grosse, dynamische Umgebungen mit Routing/Anycast-Gateway im Cluster |
Faustregel: An eine bestehende Unternehmens-VLAN-Fabric koppelst du mit einer VLAN-Zone. Willst du Netze im Cluster erzeugen, ohne jedes Mal Switch-Ports/VLANs zu beantragen, nimmst du VXLAN; brauchst du zusaetzlich geroutete Segmente mit lokalem Gateway auf jedem Node, EVPN.
7.1 VLAN-Zone (an bestehende Fabric andocken)
Der Standardfall. Voraussetzung: vmbr0 ist eine VLAN-aware Trunk-Bridge (Schritt 1, Variante B). Alle Befehle laufen einmal auf einem beliebigen Node:
1
2
3
4
5
6
7
8
9
10
11
12
# a) Zone an den Trunk binden
pvesh create /cluster/sdn/zones --zone corpvlan --type vlan --bridge vmbr0
# b) VNet = VLAN 100 (wird auf jedem Node zur Bridge "prod100")
pvesh create /cluster/sdn/vnets --vnet prod100 --zone corpvlan --tag 100
# c) Subnet mit integriertem IPAM (kein Cloud-Dienst noetig)
pvesh create /cluster/sdn/subnets --subnet 172.16.100.0/24 \
--vnet prod100 --gateway 172.16.100.1 --snat 0
# d) Ausrollen
pvesh set /cluster/sdn
GUI-Weg: Datacenter -> SDN -> Zones -> Add -> VLAN, dann VNets -> Add und Subnets -> Add, zum Schluss oben Apply. CLI und GUI schreiben dieselben
.cfg-Dateien – du kannst sie beliebig mischen.
7.2 VXLAN-Zone (Overlay ohne Fabric-Eingriff)
Ein VXLAN-Overlay spannt L2-Netze ueber ein beliebiges geroutetes Underlay – die physische Fabric muss die VM-VLANs gar nicht kennen, sie transportiert nur die Node-zu-Node-IP-Pakete. Als “Peers” gibst du die (Underlay-)IPs aller Cluster-Nodes an:
1
2
3
4
5
6
7
8
# Zone kennt alle Node-IPs des Underlays
pvesh create /cluster/sdn/zones --zone overlay --type vxlan \
--peers 10.10.0.11,10.10.0.12,10.10.0.13
# VNet: die Zahl ist hier die VXLAN-VNI, nicht ein VLAN-Tag
pvesh create /cluster/sdn/vnets --vnet tenant-a --zone overlay --tag 100000
pvesh set /cluster/sdn
MTU-Falle: VXLAN kostet 50 Byte Overhead. Das Underlay muss also mindestens VNet-MTU + 50 koennen. Praxis: Underlay auf MTU 9000 (Jumbo, wie das Ceph-Netz), VNet auf 1500 – dann hast du reichlich Luft. Vergisst du das, brechen genau die grossen Pakete weg (SSH tippt noch,
scphaengt).
7.3 EVPN-Zone (geroutetes Overlay mit lokalem Gateway)
EVPN ergaenzt VXLAN um einen BGP-Controller (FRR auf den Nodes) und um integriertes Routing: Das Subnet-Gateway existiert als Anycast-Adresse auf jedem Node – eine VM nutzt immer ihr lokales Gateway, egal auf welchem Node sie gerade laeuft (ideal fuer Live-Migration). Zwei Objekte mehr als bei VLAN:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1) Controller: der BGP-Sprecher (eigene private ASN)
pvesh create /cluster/sdn/controllers --controller evpnctl --type evpn \
--asn 65000 --peers 10.10.0.11,10.10.0.12,10.10.0.13
# 2) EVPN-Zone: nutzt den Controller, eigene VRF-VNI, Exit-Nodes fuers Raus-Routing
pvesh create /cluster/sdn/zones --zone evpn0 --type evpn \
--controller evpnctl --vrf-vxlan 10000 \
--exitnodes pve-01,pve-02 --mtu 8950
# 3) VNet + Subnet (Gateway wird zum Anycast-GW auf allen Nodes)
pvesh create /cluster/sdn/vnets --vnet app-net --zone evpn0 --tag 11000
pvesh create /cluster/sdn/subnets --subnet 10.100.0.0/24 \
--vnet app-net --gateway 10.100.0.1
pvesh set /cluster/sdn
Die Exit-Nodes sind die Nodes, ueber die Traffic aus dem Overlay in den Rest des (Air-Gap-)Netzes geroutet wird – zwei fuer Redundanz. Die BGP-Ebene verifizierst du auf einem Exit-Node ueber FRR:
1
vtysh -c "show bgp l2vpn evpn summary" # sind die Nachbarn "Established"?
7.4 Subnets, IPAM und optional DHCP
Das Subnet macht aus dem reinen L2-VNet ein IP-Netz:
- Gateway – bei VLAN/VXLAN dein vorhandener Fabric-Router, bei EVPN das Anycast-GW auf jedem Node.
- IPAM – Proxmox bringt einen eingebauten IP-Address-Manager mit (
pve), der belegte Adressen clusterweit verwaltet. Im Air-Gap reicht das voellig; externe IPAM (NetBox, phpIPAM) sind optional anbindbar. - SNAT –
--snat 1NATtet das Subnet hinter der Node-IP (fuer Overlays ohne eigene Route sinnvoll); an einer normalen VLAN-Fabric bleibt es0. - DHCP (optional) – SDN kann per dnsmasq automatisch Adressen an VMs vergeben. Dazu die Zone auf DHCP stellen und dem Subnet einen Bereich geben:
1
2
3
4
pvesh set /cluster/sdn/zones/corpvlan --dhcp dnsmasq
pvesh set /cluster/sdn/subnets/corpvlan-172.16.100.0-24 \
--dhcp-range start-address=172.16.100.100,end-address=172.16.100.200
pvesh set /cluster/sdn
Der Subnet-Bezeichner in der API ist
<zone>-<cidr-mit-bindestrichen>(siehepvesh get /cluster/sdn/subnets). Unsicher bei einem Flag?pvesh help /cluster/sdn/subnets create -vlistet alle Optionen der jeweiligen Version.
7.5 Verifizieren und Fehlersuche
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1) Pending-Config sauber angewandt? (idempotent, zeigt Fehler sofort)
pvesh set /cluster/sdn
# 2) VNet ist auf JEDEM Node als Bridge da?
for n in pve-01 pve-02 pve-03; do
echo "== $n =="; ssh $n "ip -br link show app-net"
done
# 3) Objekte im Ueberblick
pvesh get /cluster/sdn/vnets
pvesh get /cluster/sdn/zones
# 4) DHCP laeuft? (eine dnsmasq-Instanz je Zone)
systemctl status dnsmasq@corpvlan
Taucht ein VNet auf einem Node nicht als Bridge auf, hat dort meist der Reload geklemmt – ifreload -a auf dem betroffenen Node ausfuehren und die Logs mit journalctl -u pvedaemon -e pruefen.
7.6 Migration: von Bridge-per-VLAN zu SDN-VNets
Genau der Umstieg, den viele aus dem Homelab mitbringen (vmbr30, vmbr45, …). Er geht unterbrechungsarm, weil das VNet dieselben VLAN-Tags nutzt wie deine alten Bridges:
- VLAN-Zone auf dem Trunk anlegen (7.1).
- Pro altem
vmbrXXein VNet mit gleichem Tag – und, wenn du magst, gleichem sprechenden Namen:vmbr30wird zu VNetvnet30 --tag 30. So bleibt deine Nomenklatur erhalten, jetzt aber clusterweit. pvesh set /cluster/sdn– die VNets erscheinen als Bridges auf allen Nodes.- VMs einzeln nacheinander von
vmbrXXauf das passendevnetXXumhaengen (NIC editieren; kein Neustart noetig, nur ein kurzer Link-Flap). - Sind alle VMs umgezogen, die alten
vmbrXX/enoX.XX-Eintraege aus/etc/network/interfacesentfernen.
Ergebnis: Deine lesbare
…30/…45-Konvention ueberlebt – nur ist der Name jetzt ein VNet, das auf jedem Node automatisch existiert, bei der Live-Migration mitreist und sich zentral pflegen laesst. Genau der Punkt aus Schritt 1.
Air-Gap-Hinweis: Der integrierte IPAM reicht im geschlossenen Netz voellig; kein externer Cloud-IPAM noetig. Auch der EVPN-Controller (FRR) laeuft komplett offline. SDN ist einer der Bereiche, in denen ein Air-Gap gar keine Einschraenkung ist.
Schritt 8: Zertifikate mit interner PKI
Die Proxmox-Weboberflaeche laeuft per Default mit einem selbstsignierten Zertifikat – jeder Browser meckert. In Produktion willst du Zertifikate deiner internen CA, denen alle Firmen-Clients vertrauen. Ohne Internet faellt ACME/Let’s-Encrypt weg – aber Proxmox’ ACME-Client kann gegen jeden ACME-Server sprechen, auch einen internen.
Voraussetzung: Die Root-CA ist bereits im Trust-Store (Schritt 2.4).
8.1 Variante A: Manuell mit interner CA
Fuer eine klassische interne CA (AD Certificate Services oder OpenSSL-CA). Moderne Browser brauchen Subject Alternative Names (SAN) – deshalb eine CSR-Config statt nur -subj:
1
2
3
4
5
6
7
8
9
10
11
12
13
# pve-01-csr.cnf
[req]
distinguished_name = dn
req_extensions = v3_req
prompt = no
[dn]
CN = pve-01.corp.local
O = Corp
[v3_req]
subjectAltName = @san
[san]
DNS.1 = pve-01.corp.local
IP.1 = 10.10.0.11
1
2
3
4
5
6
7
8
9
# a) Key + CSR erzeugen
openssl req -new -newkey rsa:2048 -nodes \
-keyout pve-01.key -out pve-01.csr -config pve-01-csr.cnf
# b) pve-01.csr von der internen CA signieren lassen -> pve-01.crt
# c) Zertifikat + Key einspielen und pveproxy neu starten
pvenode cert set pve-01.crt pve-01.key --force
systemctl restart pveproxy
Verifikation:
1
2
3
echo | openssl s_client -connect pve-01.corp.local:8006 2>/dev/null \
| openssl x509 -noout -issuer -subject -dates
# -> issuer = deine interne CA, subject = CN=pve-01.corp.local, gueltiges Ablaufdatum
8.2 Variante B: Automatisiert mit internem ACME-Server (empfohlen)
Manuelles Signieren skaliert nicht und laeuft ab. Eleganter: ein interner ACME-Server wie step-ca (smallstep) – Proxmox erneuert die Zertifikate dann selbst, wie bei Let’s-Encrypt, nur ohne Internet.
1
2
3
4
5
6
7
8
9
# a) Internen ACME-Account gegen step-ca registrieren (auf jedem Node)
pvenode acme account register default [email protected] \
--directory https://step-ca.corp.local/acme/acme/directory
# b) Domain am Node hinterlegen und Zertifikat anfordern
pvenode config set --acme domains=pve-01.corp.local
pvenode acme cert order
# c) automatische Erneuerung ist per systemd-Timer bereits aktiv (pve-daily-update)
Verifikation:
1
pvenode acme cert order --force # erzwingt eine Erneuerung als Test -> "Success"
flowchart LR
N["pve-01 (ACME-Client)"] -->|"order + challenge"| S["step-ca<br/>(interner ACME-Server)"]
S -->|"validiert HTTP-01/DNS-01"| N
S -->|"signiertes Cert"| N
ROOT["step-ca Root-CA"] -->|"im Trust-Store"| CLIENTS["alle Firmen-Clients"]
Best Practice: Root-CA offline und gut geschuetzt halten, nur eine Intermediate-CA signiert im Betrieb. Kurze Zertifikats-Laufzeiten (z. B. 90 Tage) sind mit automatischer Erneuerung kein Problem – und genau deshalb ist der interne ACME-Weg dem manuellen vorzuziehen.
Schritt 9: User, Rollen und Rechte
Im Unternehmen meldet sich niemand mit dem root-Account an. Proxmox bringt ein feingranulares Rechtemodell mit: Realms (woher kommen die User), Roles (was darf man), ACLs (worauf, mit welcher Rolle) und Pools (Gruppen von VMs/Storage).
9.1 AD-Realm anbinden
Statt lokaler PVE-User bindest du das Active Directory an – zentrale Identitaeten, zentrale Sperrung:
1
2
3
pveum realm add corp-ad --type ad --domain corp.local \
--server1 dc1.corp.local --server2 dc2.corp.local --secure 1 --default 0
pveum realm sync corp-ad
Verifikation: pveum user list | grep @corp-ad zeigt die synchronisierten User.
9.2 Rollen – least privilege
Vergib nie mehr als noetig. Praxisnahe Zuschnitte:
| Rolle | Rechte | Fuer wen |
|---|---|---|
PVEVMUser (eingebaut) |
VM starten/stoppen/Konsole, kein Anlegen/Loeschen | 1st-Level / App-Teams |
BackupOperator (custom) |
VM.Backup, Datastore.AllocateSpace, VM.Audit |
Backup-Team |
Auditor (custom) |
nur *.Audit |
Revision / Security |
PVEAdmin (eingebaut) |
fast alles ausser Realm-/Cluster-Kern | Plattform-Team |
9.3 Komplettes Rechte-Setup (copy&paste)
Realm ist gesetzt – jetzt Pools, Custom-Rollen, Rechte und ein Service-Token in einem Rutsch:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# a) Pools (fachliche Buendel; Rechte gibst du auf den Pool, nicht die Einzel-VM)
pveum pool add prod --comment "Produktions-VMs"
pveum pool add dev --comment "Entwicklung"
# b) Custom-Rollen
pveum role add BackupOperator --privs "VM.Backup VM.Audit Datastore.AllocateSpace Datastore.Audit"
pveum role add Auditor --privs "VM.Audit Datastore.Audit Sys.Audit Pool.Audit"
# c) Rechte vergeben (Gruppe -> Rolle -> Pfad; jeweils so eng wie moeglich)
pveum acl modify /pool/prod --group "app-team@corp-ad" --role PVEVMUser
pveum acl modify /pool/prod --group "backup-team@corp-ad" --role BackupOperator
pveum acl modify / --group "auditors@corp-ad" --role Auditor --propagate 1
# d) API-Token fuer Automatisierung (Ansible/Backup) mit Privilege Separation
pveum user token add svc-backup@pve automation --privsep 1
# -> gibt EINMALIG den Secret aus: sicher wegspeichern!
pveum acl modify /pool/prod --tokens "svc-backup@pve!automation" --role BackupOperator
9.4 2FA erzwingen
TOTP fuer alle administrativen Accounts aktivieren – funktioniert offline (kein SMS-Gateway noetig). Entweder pro User (GUI: Datacenter → Permissions → Two Factor) oder realm-weit als Pflicht:
1
pveum realm modify corp-ad --tfa type=totp # TOTP fuer diesen Realm verpflichtend
Verifikation:
1
2
pveum acl list # alle Zuweisungen auf einen Blick
pveum user permissions app-team@corp-ad # effektive Rechte eines Users pruefen
flowchart TB
U["AD-User (corp-ad)"] --> G["AD-Gruppe backup-team"]
G -->|"ACL: Rolle BackupOperator"| POOL["/pool/prod"]
POOL --> V1["VM 1001"]
POOL --> V2["VM 1002"]
T["API-Token svc-backup (privsep)"] -->|"nur VM.Backup"| POOL
Schritt 10: Backup mit Proxmox Backup Server
Snapshots auf demselben Ceph sind kein Backup – faellt der Cluster, ist auch der Snapshot weg. Das Backup gehoert auf einen getrennten Proxmox Backup Server (PBS), idealerweise auf eigener Hardware und in einem anderen Brandabschnitt.
PBS speichert dedupliziert und inkrementell (nur geaenderte Bloecke per Dirty-Bitmap) – das macht taegliche Vollsicherungen bezahlbar. Der Datenfluss:
flowchart LR
subgraph CL["Proxmox-Cluster"]
V["VMs (RBD)"]
end
V -->|"inkrementell, dedupliziert, verschluesselt"| DS["PBS Datastore<br/>(Namespaces: prod / dev)"]
DS -->|"Verify (Pruefsummen)"| DS
DS -->|"Sync / Tape"| OFF["Offsite / Wechseldatentraeger"]
10.1 Datastore und Namespace (auf dem PBS)
1
2
proxmox-backup-manager datastore create main /mnt/datastore/main
proxmox-backup-manager namespace create main prod
10.2 Backup-User und Token (auf dem PBS)
Der Cluster meldet sich mit einem Token (nicht mit einem Passwort) an, mit Rechten nur auf diesen Namespace:
1
2
3
4
5
proxmox-backup-manager user create backup@pbs --password 'CHANGE-ME'
proxmox-backup-manager user generate-token backup@pbs cluster
# -> gibt das Token-Secret EINMALIG aus: sicher wegspeichern!
proxmox-backup-manager acl update /datastore/main/prod DatastoreBackup \
--auth-id 'backup@pbs!cluster'
10.3 PBS am Cluster einbinden (mit Verschluesselung)
1
2
3
4
5
6
7
8
# Fingerprint auf dem PBS holen: proxmox-backup-manager cert info | grep Fingerprint
# dann auf dem Cluster (einmal):
pvesm add pbs pbs-main --server pbs.corp.local --datastore main --namespace prod \
--fingerprint "AA:BB:CC:DD:..." --username 'backup@pbs!cluster' --password 'TOKEN-SECRET'
# Client-seitige Verschluesselung aktivieren (Key wird auf dem Cluster erzeugt)
pvesm set pbs-main --encryption-key autogen
# -> Key unter /etc/pve/priv/storage/pbs-main.enc -> SEPARAT sichern (Tresor/Ausdruck)!
10.4 Backup-, Prune- und Verify-Job planen
1
2
3
4
5
6
7
8
9
10
# a) taeglicher Backup-Job fuer den Pool prod (auf dem Cluster)
pvesh create /cluster/backup --storage pbs-main --pool prod \
--schedule "02:00" --mode snapshot --enabled 1
# b) Aufbewahrung/Prune (auf dem PBS): 7 taeglich, 4 woechentlich, 6 monatlich
proxmox-backup-manager prune-job create daily-prune --store main \
--schedule "04:00" --keep-daily 7 --keep-weekly 4 --keep-monthly 6
# c) woechentlicher Verify-Job (auf dem PBS) - prueft die Pruefsummen
proxmox-backup-manager verify-job create weekly-verify --store main --schedule "sun 05:00"
10.5 Restore testen (Pflicht, nicht Kuer)
1
2
3
4
5
# verfuegbare Snapshots auflisten (auf einem Node)
pvesm list pbs-main
# Test-Restore unter NEUER VM-ID (Original nicht ueberschreiben)
qmrestore pbs-main:backup/vm/1001/2026-07-17T02:00:00Z 9999 --storage ceph-vm
Restore testen, nicht hoffen. Ein Backup gilt erst als funktionierend, wenn du es zurueckgespielt hast – plane regelmaessige Test-Restores (ganze VM + einzelne Datei per File-Level-Restore). Im Air-Gap ist der Offsite-Weg meist ein Wechseldatentraeger oder Tape statt Cloud-Sync; auch dieser Prozess gehoert getestet. Und: Den Encryption-Key getrennt sichern – ohne ihn ist auch dein Backup wertlos.
vzdump als Fallback
Fuer einzelne Nodes oder ohne PBS gibt es weiterhin vzdump (Voll-Backups als Archiv auf NFS/lokal). Im Cluster-Kontext ist PBS aber wegen Deduplizierung, Inkrementen und Verify klar ueberlegen.
Schritt 11: Betrieb, Monitoring und Updates
11.1 Metriken an einen internen Server pushen
Proxmox schiebt Metriken an einen externen Metric-Server (InfluxDB/Graphite), Visualisierung per Grafana – alles intern, keine Cloud. Einmal auf dem Cluster konfiguriert, senden alle Nodes:
1
2
3
4
5
6
7
8
9
# InfluxDB v1 per UDP (einfachster Weg im internen Netz)
pvesh create /cluster/metrics/server/influx \
--type influxdb --server metrics.corp.local --port 8089 \
--influxdbproto udp --enable 1
# InfluxDB v2 per HTTPS + Token (Alternative):
# pvesh create /cluster/metrics/server/influx --type influxdb \
# --server metrics.corp.local --port 8086 --influxdbproto https \
# --organization corp --bucket proxmox --token 'INFLUX-TOKEN' --enable 1
Verifikation:
1
2
pvesh get /cluster/metrics/server # -> influx, enable 1
journalctl -u pvestatd -n 20 | grep -i metric # -> keine Sendefehler
11.2 Benachrichtigungen (internes SMTP oder Gotify)
Der Cluster meldet Backup-/HA-/Ceph-/Update-Ereignisse. Ziel und Filter (Matcher) werden zentral gesetzt – hier ein internes Mail-Relay:
1
2
3
4
5
6
7
8
9
10
11
12
# a) SMTP-Ziel (internes Mail-Relay im geschlossenen Netz)
pvesh create /cluster/notifications/endpoints/smtp/corp-mail \
--server mail.corp.local --mode starttls \
--from-address [email protected] --mailto [email protected]
# b) Matcher: welche Ereignisse an dieses Ziel gehen (ab Warnung)
pvesh create /cluster/notifications/matchers/warn-to-mail \
--target corp-mail --match-severity warning,error
# Alternative Gotify (statt SMTP):
# pvesh create /cluster/notifications/endpoints/gotify/corp-gotify \
# --server https://gotify.corp.local --token 'GOTIFY-TOKEN'
Verifikation:
1
2
pvesh get /cluster/notifications/targets # -> corp-mail gelistet
# Test-Benachrichtigung ausloesen (Datacenter -> Notifications -> Test in der GUI)
11.3 Rolling Updates (ohne Downtime)
Updates laufen ueber den internen Mirror – ein Node nach dem anderen: VMs per Live-Migration wegschieben, updaten, rebooten, zurueck, dann der naechste:
1
2
3
4
5
# pro Node, nacheinander:
ha-manager crm-command node-maintenance enable pve-01 # VMs wandern weg
apt update && apt full-upgrade -y && reboot
# nach dem Boot:
ha-manager crm-command node-maintenance disable pve-01
Verifikation nach jedem Node:
1
2
pvecm status | grep Quorate # -> Yes (Quorum die ganze Zeit erhalten)
ceph -s | grep health # -> HEALTH_OK, bevor der naechste Node drankommt
Mehrere Umgebungen: Test, Staging, Produktion (und Schulung)
Sobald Proxmox nicht mehr “ein Server”, sondern eine Plattform ist, brauchst du getrennte Umgebungen. Der Grund ist immer derselbe: Du willst jede Aenderung – ein Ceph-Upgrade, einen neuen Kernel, eine geaenderte Firewall-Regel – erst woanders ausprobieren, bevor sie die Produktion trifft. Im Air-Gap wiegt das doppelt: Du kannst nicht eben schnell in einer Cloud nachbauen, um etwas zu testen.
Warum trennen? Der Blast Radius
Der entscheidende Begriff ist Blast Radius – wie weit reicht der Schaden, wenn etwas schiefgeht. In einem Cluster teilen sich alle VMs dieselbe Corosync-Domain, dasselbe Ceph und dieselbe PVE-Version. Ein misslungenes clusterweites Upgrade oder ein Ceph-Problem trifft dann alles gleichzeitig – Test wie Produktion. Getrennte Umgebungen begrenzen genau diesen Radius.
Dazu kommen drei praktische Treiber: unterschiedliche Lebenszyklen (Test wird staendig umgebaut, Prod bleibt stabil), unterschiedliche SLAs (ein Test-Ausfall stoert niemanden, ein Prod-Ausfall schon) und saubere Rechtetrennung (Entwickler duerfen in Test viel, in Prod nichts).
Zwei Wege der Trennung – mit klarem Trade-off
| Getrennte Cluster | Ein Cluster + Segmentierung | |
|---|---|---|
| Isolation | Stark (eigene Quorum-/HA-/Ceph-Domain) | Schwach (geteilte Failure-Domain) |
| Upgrade-Risiko | pro Umgebung isoliert | trifft potenziell alles |
| Hardware-Bedarf | hoeher (min. 3 Nodes je Cluster) | niedrig (ein Cluster) |
| Betriebstrennung | physisch/logisch komplett | per Pools, SDN-Zones, RBAC |
| Empfohlen fuer | Prod vs. Rest | Dev/Staging untereinander |
Best Practice im Enterprise: Die Produktion bekommt einen eigenen Cluster – harte Trennung, eigenes Quorum, eigenes Ceph, eigener Upgrade-Zyklus. Test und Staging koennen sich einen zweiten, kleineren Cluster teilen und dort per Pool + SDN-Zone + Rechten getrennt werden. So bezahlst du nicht dreimal drei Nodes, schuetzt aber die Produktion kompromisslos.
flowchart TB
subgraph SHARED["Zentrale Dienste - einmal fuer alle Umgebungen"]
MIR["Paket-Mirror"]
CA["Interne CA / ACME"]
AD["AD / LDAP"]
ND["NTP + DNS"]
PBS["PBS - Namespaces je Umgebung"]
end
subgraph PROD["Cluster PROD (eigenes Quorum + Ceph)"]
P1["pve-prod-01..03"]
end
subgraph NONPROD["Cluster NONPROD (geteilt)"]
T1["Test - Pool/SDN/RBAC"]
S1["Staging - Pool/SDN/RBAC"]
end
SHARED --> PROD
SHARED --> NONPROD
Was geteilt wird – und was nicht
Die zentralen Air-Gap-Dienste (Mirror, CA, NTP, DNS, AD) betreibst du einmal und bedienst damit alle Cluster – alles andere waere doppelte Pflegearbeit. Getrennt bleibt dagegen konsequent alles, was den Blast Radius bestimmt:
- Corosync/Ceph: pro Cluster eigen – und mit eigenen IP-Segmenten, sonst kollidieren Ring- und Storage-Netze zweier Cluster. Also z. B. Prod-Ring 0 auf
10.20.0.0/24, Nonprod-Ring 0 auf10.25.0.0/24. - PBS: ein PBS reicht, aber je Umgebung ein Namespace (
prod,staging,test) mit eigener Retention und eigenen Zugriffsrechten. Fuer harte Trennung (oder getrennte Aufbewahrungspflichten) einen separaten PBS fuer Prod. - Rechte: eigene AD-Gruppen und Rollen je Umgebung (
prod-admins,test-users), damit niemand versehentlich in der falschen Umgebung landet.
Der Promotion-Pfad
Getrennte Umgebungen entfalten ihren Wert erst mit einem klaren Promotion-Pfad: Neues wird erst in Test verprobt, dann in Staging unter prod-aehnlichen Bedingungen bestaetigt, dann kontrolliert in Prod ausgerollt.
flowchart LR
T["TEST<br/>PVE/Ceph-Upgrades,<br/>neue VM-Templates"] --> S["STAGING<br/>prod-nah bestaetigen"]
S --> P["PROD<br/>kontrolliert ausrollen"]
Im Air-Gap heisst das konkret: Ein neues Paket landet zuerst im internen Mirror, wird auf dem Test-Cluster eingespielt (rolling), laeuft ein paar Tage, wandert nach Staging – und erst dann faehrst du das Prod-Update. So wird “wir haben das Ceph-Upgrade schon dreimal gemacht, bevor es Prod erreicht” vom Wunsch zum Prozess. Genau dafuer ist es sinnvoll, den Test-Cluster bewusst eine Version vor Produktion laufen zu lassen.
Schulung: die Wegwerf-Umgebung
Fuer Schulungen und Trainings willst du eine Umgebung, die man absichtlich kaputtmachen und zuruecksetzen kann, ohne dass es irgendjemanden juckt. Zwei praktikable Wege:
- Nested Virtualization: ganze Proxmox-Nodes als VMs auf einem Trainings-Host (PVE-in-PVE). Teilnehmer bauen darin ihren eigenen “Cluster” – inklusive Corosync und Ceph im Kleinen – und nach dem Kurs wirft man die VMs einfach weg und spielt das Template neu aus. Kostet keine Extra-Hardware und ist ideal fuer ein Air-Gap-Lab.
- Kleiner dedizierter Cluster aus ausgemusterter Hardware, der per Snapshot/Template auf einen bekannten Startzustand zurueckgesetzt wird.
Wichtig: Die Schulungs-Umgebung darf an denselben zentralen Diensten haengen (Mirror etc.), bekommt aber strikt getrennte Rechte und IP-Bereiche. Ein uebermuetiger Kursteilnehmer darf niemals aus Versehen die Produktion erreichen – genau deshalb ist die harte Netz- und Rechtetrennung hier keine Kuer, sondern Pflicht.
Best-Practice-Checkliste
Bevor der Cluster in Produktion geht – die Punkte, die im Ernstfall den Unterschied machen:
- 3+ Nodes (ungerade) oder 2 Nodes + QDevice
- Corosync auf dedizierten NICs, zwei Ringe auf getrennten Switches
- Storage-Netz getrennt (Ceph Public + Cluster), MTU 9000 durchgaengig
- Ceph size=3 / min_size=2, CRUSH-Failure-Domain passend (host/rack)
- Hardware-Watchdog aktiv und Fencing getestet
- Shared Storage vorhanden (sonst kein HA)
- Interne CA vertraut auf allen Nodes + Clients, Zertifikate automatisiert erneuert
- AD-Realm + least privilege,
rootnur im Notfall, 2FA fuer Admins - PBS getrennt, Retention + Verify + Encryption, Restore getestet
- Interner Mirror/NTP/DNS stehen und werden gepflegt
- Rolling-Update-Prozess dokumentiert und geuebt
- Umgebungen getrennt: Prod als eigener Cluster, eigene IP-Segmente je Cluster, PBS-Namespace + Rechte je Umgebung
- Out-of-Band-Management (IPMI/iDRAC/iLO) im eigenen Segment, fuers Fencing und als Air-Gap-“Remote Hands”
Fazit
Ein produktiver Proxmox-Cluster im Unternehmensnetz ist kein “grosses Homelab”, sondern eine Kette abhaengiger Entscheidungen: Ohne getrennte Netze kein stabiles Corosync, ohne Corosync kein Quorum, ohne Quorum kein HA, ohne Shared Storage kein Recovery, ohne Fencing kein sicheres Recovery, ohne getrenntes Backup keine Wiederherstellung. Jede Saeule traegt die naechste.
Der Air-Gap aendert daran erstaunlich wenig – er verschiebt nur die Quelle: interner Mirror statt Internet-Repo, interne CA statt Let’s-Encrypt, interner NTP/DNS statt Cloud, Wechseldatentraeger statt Offsite-Sync. Wer diese vier internen Dienste sauber aufsetzt, betreibt einen Cluster, der genauso aktuell, sicher und hochverfuegbar ist wie ein angebundener – nur eben unabhaengig vom Internet.
| Aspekt | Heim-Setup | Enterprise / Air-Gap |
|---|---|---|
| Nodes | 1–2 | 3+ (oder 2 + QDevice) |
| Netz | 1 NIC, flaches /24 |
4–6 NICs, getrennte VLANs, Jumbo-Storage |
| Storage | lokal (ZFS) | Ceph / SAN (shared, Pflicht fuer HA) |
| Zertifikate | selbstsigniert / Let’s-Encrypt | interne CA / interner ACME |
| Auth | lokale User | AD-Realm + least privilege + 2FA |
| Backup | vzdump auf NAS | PBS getrennt, Verify + Encryption + Offsite |
| Updates | direkt aus dem Netz | interner Mirror, Rolling Update |
Damit steht das Fundament. Aufsetzen kannst du darauf beliebig – von der einzelnen Test-VM bis zur mandantenfaehigen Plattform mit SDN-Overlays.