Ein etwas technischerer Artikel über die Problematik – und die Lösung – beim Betrieb einer Linux-Maschine, die über PXE bootet, das Root-System über NFS bekommt und dieses Root-System nicht nur über ein Bonding-Interface bekommt, sondern dieses zusätzlich als Bridge bereitgestellt wird.
Die Problemstellung
Die Problemstellung kam aus der Praxis: Es ging um den erweiterten Ersatz eines VMware-Clusters durch einen Proxmox-Cluster. Der VMware-Cluster startet die Server von SD-Karten. Eine nur sehr beschränkt zuverlässige Lösung, zumindest dann, wenn auf die SD-Karten auch geschrieben wird: Die SD-Karten stellen einen single point of failure dar und wenn auf die SD-Karten regelmäßig geschrieben wird, steigt auch das Ausfallrisiko.
Zur Verfügung standen ein modernes SAN, bestückt mit NVMe, ein „altes“ SAN mit drehenden Platten, der Rechencluster mit zwei Nodes und redundante 10G-Verbindungen zwischen den Komponenten.
Das moderne SAN, betrieben unter TrueNAS, sollte die virtuellen Platten für die virtuellen Maschinen über iSCSI und die 10G-Verbindung bereitstellen, das alte SAN sollte aussortiert werden. Drehende Systeme sind in der heutigen Zeit langsam.
Die erste Lösung
Die erste Lösung war, das TrueNAS in zwei Funktionen zu nutzen: a) als Datenspeicher für die virtuellen Platten, freigegeben an die Nodes via iSCSI und b) also Root-Dateisysteme für die Nodes, freigegeben via NFS.
Die Lösung ist zwar nicht banal, aber „eigentlich“ unkritisch: Grundsätzlich wird über PXE-Boot der DHCP-Server nach den Boot-Parametern befragt, der Node auf den TFTP-Server umgeleitet, von dort wird der Kernel und die initrd geladen, das System fährt hoch. Es muss lediglich eine individuelle initrd erzeugt werden, die das Bonding bereitstellt, damit das per NFS bereitgestellte Root-Dateisysteme über Bonding genutzt wird, falls ein Datenweg wegen eines Hardwareschadens zusammenbricht.
Sinnvollerweise wird das Script über die pxelinux.cfg gesteuert, hier werden die Slave-Interfaces als Parameter angegeben, die das Script auswertet und zu einem Bonding-Interface zusammenstellt. Außerdem wird in der pxelinux.cfg angegeben, die die IP-Konfiguration lautet und wo das NFS-Root-System liegt. Der Aufbau ist z. B. unter https://www.kernel.org/doc/html/v6.1/admin-guide/nfs/nfsroot.html dokumentiert, bei uns sah der Eintrag so aus:
DEFAULT vmlinuz-6.8.12-9-pve
LABEL vmlinuz-6.8.12-9-pve
KERNEL server2/vmlinuz-6.8.12-9-pve
INITRD server2/initrd.img-6.8.12-9-pve
APPEND root=/dev/nfs nfsroot=192.168.3.11:/mnt/server2,rw ip=:::::bond0:dhcp bondslaves=eno1np0,eno2np1
Das Script selbst wurde als /etc/initramfs-tools/scripts/nfs-top/00_bonding_init angelegt und hat die folgende Gestalt:
PREREQS=""
case $1 in
prereqs) echo "${PREREQS}"; exit 0;;
esac
echo "Network interfaces loaded: "
echo `ls /sys/class/net`
for x in $cmdline; do
case $x in
bondslaves=*)
bondslaves="${x#bondslaves=}"
;;
esac
done
ip link add bond0 type bond
echo active-backup > /sys/class/net/bond0/bonding/mode
echo 1 > /sys/class/net/bond0/bonding/miimon
IFS=","
for x in $bondslaves; do
echo "+$x" > /sys/class/net/bond0/bonding/slaves
done
Kein Hexenwerk, das System startete und lief. Die Simulation der Leitungsausfälle (Ziehen einer Netzwerkverbindung, Auschalten eines Switches) funktionierte, alles wie gewünscht.
Der Ehrgeiz des Administrators
Wir hatten also eine schönes, redundantes und funktionierendes System. Und wir hatten ein „altes“ SAN mit vielen TB Kapazität. Viel zu schade, um es zu entsorgen. Es wäre prima geeignet als Datenablage, als Backupplatz etc…
Die beste Anbindungsmöglichkeit für das SAN ist iSCSI, also die Bereitstellung von Blockdevices. Nun sollten diese Blockdevices direkt den VMs bereitgestellt werden. Der Virtualisierungshost würde unnötig „aufgeblasen“, wenn er für diese Blockdevices als Initiator dienen und sie dann an die VMs weiter geben würde. Sinnvoller ist, es dann die VMs selbst Initiator werden. Nur dann können die VMs auch zwischen den verschiedenen Nodes verschoben werden.
Die Probleme
Doch mit diesem guten Gedanken kommen die Probleme:
- Damit die VMs auf die iSCSI-Targets zugreifen können, müssen sie im selben Netzwerk liegen.
- Dies bedeutet, dass die Netzwerkkarte, die auf dem Node an dem Netzwerk angebunden ist, eine Bridge bilden muss: Der Node und die VMs müssen die Netzwerkkarte nutzen.
Unter Proxmox ist die Bildung einer Bridge Standard. Normalerweise ist in der /etc/network/interfaces z. B. wie folgt konfiguriert:
auto lo
iface lo inet loopback
iface eno1 inet manual
auto vmbr0
iface vmbr0 inet static
address 192.168.3.2/24
gateway 192.168.3
.1
bridge-ports eno1
bridge-stp off
bridge-fd 0
Das System fährt hoch und konfiguriert das Netzwerk.
Das Problem liegt in unserem Fall jedoch darin, dass diese Konfiguration im Root-Dateisystem liegt. Das heißt, die Netzwerkverbindung, die geändert werden soll, wurde und wird zu dem Zeitpunkt der Änderung schon genutzt, um auf das Root-Dateiesystem zuzugreifen. Man zöge in diesem Szenario also dem Node das Root-System weg. Die Folge ist ein gnadenloser Absturz.
Der Lösungsgedanke: Bilderung der Bridge in der initrd
Somit muss die Bridge analog zum Bonding schon in der initrd gebaut werden, dann die IP-Adresse per DHCP über das Bridgeinterface geholt werden. Grundsätzliche Anpassung an der pxelinux.cfg:
ip=:::::vmbrX:dhcp
Und Anpassung des Scriptes um die Zeilen der Bridgebildung:
brctl addbr vmbrX
brctl addif vmbrX bond0
Trivial, oder? Die Lösung hat nur einen Nachteil: Sie funktioniert nicht. Das Debugging ergab: Es werden keine DHCP-Request abgesetzt. Die Codeanalyse, Funktion configure_networking in der Datei scripts/functions der initrd führte nicht weiter. Alles wurde korrekt ausgeführt, das Programm ipconfig in der initrd machte die korrekten DHCP-Anfragen (ja, in der initrd ist für die DHCP-Anfragen das Programm ipconfig zuständig), doch diese Anfragen tauchten nicht im Netzwerk auf. Was war anders, wo war das Problem?
Die Lösung der fehlenden Anfragen
Die Lösung ist so banal wie versteckt. Wir arbeiten mit gestapelten Netzwerkinterfaces: Unten die physischen Interfaces, darüber das Bonding-Interface und darüber die Bridge. Ursache war, dass das o.g. Standardscript, configure_networking, das Bondiginterface zwar konfigurierte jedoch nicht startete. Ist nur das Bondinginterface vorhanden, wird es durch ipconfig bei der DHCP-Anfrage gestartet. Genauso startete ipconfig beim Stack die Bridge. Die unten liegende physischen Interfaces werden bei der Bonding-Konfiguration gestartet. Doch das Bondinginterface wurde beim Stack nicht „durch eine „nebenbei“ mitgestartet, so dass es ausdrücklich gestartet werden muss.
Diesen Start eingebaut und siehe: Alles funktioniert: Der Node startet per PXE und NFS-Root über das Bridgeinterface, diese steht dann den VMs zur Verfügung, so dass diese iSCSI-Initiatoren sein können.
Haben Sie Fragen zur Lösung, unserem Vorgehen beim Debugging, zum Netzwerkbridgeing und Bonding: Nehmen Sie mit uns, der ima GmbH, Kontakt auf!