Mein 3-Node-Proxmox-Cluster, so wie er heute läuft

Live-Log · letztes Update 2026-10-17

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

Die Maschinen

NodeHardwarePVEPools
reaperMinisforum MS-A2, Ryzen 9 9955HX, 32G, BIOS 1.029.2.21, kernel 7.0.14-24rpool 472G, data-zfs 952G
sevroMinisforum MS-A2, Ryzen 9 9955HX, 32G, BIOS 1.029.2.21, kernel 7.0.14-24rpool 472G, data-zfs 952G, nas-pool 952G
skynetGigabyte B650 AORUS ELITE AX V2, Ryzen 9 7950X3D, 96G, RTX 5070 Ti9.2.20, kernel 7.0.14-19rpool 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

Quellen

  1. Proxmox VE: Cluster Manager, corosync redundancy
  2. Linux kernel: bonding driver, active-backup mode
  3. Proxmox forum: MS-A2 BIOS 1.03 and passthrough
  4. Proxmox VE: Storage Replication