Mein 3-Node-Proxmox-Cluster, so wie er heute läuft
Drei Maschinen, ein Cluster, kein gemeinsamer Speicher, kein Ceph. Alles unten ist aus dem laufenden Cluster kopiert. Kopiere, was du brauchst. Wenn sich etwas ändert, füge ich eine datierte Zeile zum Log hinzu und aktualisiere die Ausgabe.
Log
- 2026-10-17 sevro auf 10G umgestellt. Das alte Node-zu-Node-DAC geht jetzt an den Switch, als Active-Backup-Bond mit dem 2.5G-Port. reaper und sevro auf 9.2.21 aktualisiert. Das BIOS F41 von skynet hat den GPU-Passthrough kaputt gemacht, im BIOS behoben (dieser Beitrag).
- 2026-10-16 BIOS 1.03 der MS-A2 auf beiden Minis übersprungen. Es bricht PCI-Passthrough, und die Option, die das behebt, ist gesperrt [3]. Bleibt auf 1.02.
Die Maschinen
| Node | Hardware | PVE | Pools |
|---|---|---|---|
| reaper | Minisforum MS-A2, Ryzen 9 9955HX, 32G, BIOS 1.02 | 9.2.21, kernel 7.0.14-24 | rpool 472G, data-zfs 952G |
| sevro | Minisforum MS-A2, Ryzen 9 9955HX, 32G, BIOS 1.02 | 9.2.21, kernel 7.0.14-24 | rpool 472G, data-zfs 952G, nas-pool 952G |
| skynet | Gigabyte B650 AORUS ELITE AX V2, Ryzen 9 7950X3D, 96G, RTX 5070 Ti | 9.2.20, kernel 7.0.14-19 | rpool 1.81T, tank 476G |
skynet ist die Ausnahme. Es ist ein Desktop-Board mit der GPU durchgereicht an meine tägliche Desktop-VM, deshalb wird es zuletzt aktualisiert.
Quorum
$ pvecm status
Cluster information
-------------------
Name: howlers
Config Version: 9
Transport: knet
Secure auth: on
Votequorum information
----------------------
Expected votes: 3
Highest expected: 3
Total votes: 3
Quorum: 2
Flags: Quorate
Membership information
----------------------
Nodeid Votes Name
0x00000001 1 192.168.1.14
0x00000002 1 192.168.1.13 (local)
0x00000003 1 192.168.1.166
Drei Stimmen, zwei werden gebraucht. Jede einzelne Maschine kann ausfallen, die anderen zwei laufen weiter. Mit zwei Nodes braucht man ein QDevice; ein dritter echter Node hat das bei mir gelöst.
corosync.conf
$ cat /etc/pve/corosync.conf
nodelist {
node {
name: reaper
nodeid: 1
quorum_votes: 1
ring0_addr: 192.168.1.14
}
node {
name: sevro
nodeid: 2
quorum_votes: 1
ring0_addr: 192.168.1.13
}
node {
name: skynet
nodeid: 3
quorum_votes: 1
ring0_addr: 192.168.1.166
}
}
quorum {
provider: corosync_votequorum
}
totem {
cluster_name: howlers
config_version: 9
interface {
knet_link_priority: 10
linknumber: 0
}
ip_version: ipv4-6
link_mode: passive
secauth: on
token_coefficient: 125
version: 2
}
Eine Verbindung im LAN des Hauses. Die Doku will eine zweite, separate corosync-Verbindung [1]. Die habe ich noch nicht. Das ist der nächste Schritt.
Netzwerk: 10G mit 2.5G-Fallback
$ cat /etc/network/interfaces
auto bond0
iface bond0 inet manual
bond-slaves nic2 nic0
bond-mode active-backup
bond-primary nic2
bond-miimon 100
auto vmbr0
iface vmbr0 inet static
address 192.168.1.13/24
gateway 192.168.1.1
bridge-ports bond0
bridge-vlan-aware yes
bridge-vids 2-4094
bridge-stp off
bridge-fd 0
$ grep -E "Mode|Active|Interface|Speed" /proc/net/bonding/bond0
Bonding Mode: fault-tolerance (active-backup)
Currently Active Slave: nic2
Slave Interface: nic2
Speed: 10000 Mbps
Slave Interface: nic0
Speed: 2500 Mbps
nic2 ist das 10G-DAC, nic0 das 2.5G-Kupfer. DAC ziehen, und der Traffic wandert zu nic0. Active-Backup braucht nichts vom Switch [2]. Die Bridge ist VLAN-aware, also bekommt ein Guest einfach tag=101 auf seiner NIC.
Mit Sicherheitsnetz anwenden. Der Rollback läuft als systemd-Timer, also feuert er auch, wenn die ssh-Sitzung stirbt:
cp /etc/network/interfaces /root/interfaces.bak
systemd-run --on-active=2min --unit=net-rollback sh -c 'cp /root/interfaces.bak /etc/network/interfaces && ifreload -a'
ifreload -a
# still in? cancel the rollback
systemctl stop net-rollback.timer
storage.cfg
$ cat /etc/pve/storage.cfg
dir: local
path /var/lib/vz
content vztmpl,import,iso,backup
zfspool: local-zfs
pool rpool/data
content rootdir,images
sparse 1
zfspool: data-zfs
pool data-zfs
content images,rootdir
mountpoint /data-zfs
nodes sevro,reaper
sparse 1
cifs: nas-iso
path /mnt/pve/nas-iso
server 192.168.1.20
share share
content iso
subdir /lab/images/iso
username neal
Replikation braucht ZFS-Storage mit derselben Storage-ID auf beiden Nodes [4]. Deshalb haben beide Minis data-zfs. ISOs liegen auf dem NAS, damit jeder Node dieselbe Liste sieht.
Replikation statt gemeinsamem Speicher
$ pvesr status
JobID Enabled Target LastSync NextSync Duration FailCount State
100-0 Yes local/reaper 2026-10-17_14:50:00 2026-10-17_14:55:00 1.647333 0 OK
103-0 Yes local/reaper 2026-10-17_14:50:02 2026-10-17_14:55:00 1.470997 0 OK
106-0 Yes local/reaper 2026-10-17_14:50:03 2026-10-17_14:55:00 1.581741 0 OK
108-0 Yes local/reaper 2026-10-17_14:50:05 2026-10-17_14:55:00 1.914492 0 OK
109-0 Yes local/reaper 2026-10-17_14:50:07 2026-10-17_14:55:00 1.929232 0 OK
Alle 5 Minuten macht jeder Guest einen Snapshot und schickt die Differenz zur anderen Mini. Dauert unter 2 Sekunden. Einen anlegen:
pvesr create-local-job 109-0 reaper --schedule "*/5"
Einen replizierten Guest zu verschieben geht schnell, weil der meiste Speicherplatz schon da ist:
qm migrate 105 reaper --online --with-local-disks
pct migrate 109 reaper --restart
HA ist aus. Kein Fencing-Gerät, also starte ich nach einem Node-Ausfall die Replik auf dem anderen von Hand. Im schlechtesten Fall verliere ich 5 Minuten.
Einen Node aktualisieren
# empty the node first
qm migrate <id> <other-node> --online --with-local-disks
pct migrate <id> <other-node> --restart
apt update && apt full-upgrade -y
reboot
pveversion
Ein Node nach dem anderen. Zwei bleiben oben, das Quorum hält.
Was ich ändern würde
- Eine zweite corosync-Verbindung in ihrem eigenen Netz.
- Jeder Pool hier ist eine einzelne Disk. Zwei 12TB-Laufwerke sind als ZFS-Mirror auf dem Weg.
- reaper und skynet auf 10G wie sevro. Die DACs sind bestellt.