Diese Artikel-Serie beschäftigt sich mit der Einrichtung eines hybriden Kubernetes- und KVM-Clusters mithilfe von Proxmox bei Hetzner Online.
Erster Teil | Vorheriger Teil | Nächster Teil

Im letzten Teil haben wir einen Proxmox Cluster aus zwei Worker Nodes und einem CX21 als Quorum erstellt. In Teil drei der Serie widmen wir uns nun den Storage- und Netzwerktechnischen Aspekten für einen Proxmox Cluster bei Hetzner.

Welchen Storage haben wir?

Wer sich an Teil 2 zurück erinnert, weiß, dass wir unsere HDDs in einen ZFS Pool gesteckt haben. Der Hintergrund ist, dass Replikation nur mit ZFS unterstützt wird.
Für unsere spätere Kubernetes-Installation werden wir lokale volumes Benötigen, auch diese werden dann mit auf dem ZFS-Pool liegen, dazu aber mehr an späterer Stelle.

Schauen wir uns mal die aktuelle Storage-Konfiguration im Cluster an:

Baumansicht der Proxmox-Server-View
In der Web-GUI sieht man nach der Installation bei jedem Host einen “local” storage. Das ist nicht unser ZFS-Pool, sondern ein Ordner auf der Root-Partition. Diesen Ordner werden wir nicht für VMs nutzen.

Zunächst müssen wir uns ein kleines Speicherkonzept erarbeiten. Ich möchte unsere Backups auf eine Hetzner Storage Box speichern. Dort werde ich – um sie im gesamten Cluster zur Verfügung zu haben – auch gleich die ISO-Images für die Installationen der VMs ablegen. Die Festplatten der Gast-VMs werden wie, bereits bei der Grundinstallation festgelegt im lokalen ZFS-Pool der Worker Nodes liegen.

Einrichtung des lokalen ZFS-Pools

Um etwas Ordnung zu schaffen, führen wir auf allen Worker Nodes folgenden Befehl aus: zfs create rpool/vmPool – Dadurch wird ein Volume erstellt, in dem später unsere Proxmox VM-Disks liegen werden. rpool ist übrigens der Name des zpools, den der PVE Installer erstellt.

Wir loggen uns als nächstes auf das Webinterface eines unserer Worker-Nodes ein. Wichtig, nicht den Master nehmen, da dieser kein ZFS besitzt.
Dort klicken wir auf Datacenter -> Storage, anschließend oben auf Create -> ZFS.
Als Namen für den Pool nehme ich local-vmPool und wählen im Dropdown unser neu angelegtes volume rpool/vmPool.
Wichtig ist der Haken bei “Thin Provision”, der eine dichtere und damit effizientere Nutzung des verfügbaren Speicherplatzes ermöglicht. Da wir NVMes haben, stört uns eine dadurch resultierende Fragmentierung nicht.
Unter Nodes selektieren wir nur unsere Worker, sodass Proxmox nicht versucht, den Pool auf dem Master zu finden (was zu einem Fehler führen würde).

Proxmox-Dialog zum Hinzufügen eines Pools
So sollte der Dialog am Ende aussehen

Des weiteren sollten wir hier auch gleich noch eine Anpassung durchführen: Die Root-Partition ist für unser VE nämlich tabu, wir dürfen den Eintrag aber nicht entfernen, daher schränken wir deren Nutzung sicherheitshalber auf “Nur Images” ein und deaktivieren ihn, indem wir den Eintrag wie folgt anpassen:

Proxmox-Dialog zum editieren des obsoleten local Pools
local Storage deaktiviert

Wichtig zu wissen ist, dass das uns die Möglichkeit nimmt, Container am Master-Node auszuführen. Sollten wir das wollen, könnte man die Änderung entweder rückgängig machen, oder aber einen neuen Storage auf einen speziellen Pfad (Typ Directory) nur für den Master-Node anlegen.

Die Storage Box für Backups

Außerdem wollen wir eine Hetzner Storage Box als Backup- und ISO-Storage für Proxmox anbinden.
Im Hetzner Robot findet ihr alle relevanten Daten zur Storage Box. Ich persönlich nutze einen Sub-Account, falls ich mal ein anderes System noch mit anbinden möchte, man kann aber auch einfach den Hauptaccount benutzen. Die Anbindung an Proxmox erfolgt auch wieder direkt im Web-GUI, über die Funktion Create -> CIFS.
Der Eintrag sieht dann beispielhaft so aus:

Proxmox-Dialog zum Hinzufügen der Storage Box als CIFS-Pool für Backups

Auch hier ist der Name des Storage wieder frei wählbar. Wichtig sind abgesehen von Server und Benutzerdaten die Angaben unter Content, welche besagen, dass Backups, ISO-Abbilder und VZ-Templates (Vorlagen für LXC Container) darauf liegen dürfen. Man könnte die Angabe noch um “Disk Image” erweitern, wodurch man VMs drauf parken könnte – Die Performance reicht hierfür aber i.d.R. nicht aus, von der Stabilität ganz abgesehen. Es wäre aber vielleicht für “Vorlagen” interessant, die ebenfalls als KVM image gehandhabt werden.
Darüber hinaus ist die Angabe Max Backups wichtig. Sie legt fest, wie viele Backups je VM vorgehalten werden sollen. Wenn man später einen Backup-Plan erstellt, der beispielsweise tägliche Snapshots sichert, wird bei Erreichen dieser Zahl immer der älteste vom Speicher gelöscht. So kann man ganz einfach ein vollautomatisches Backup einrichten.

Übersicht aller Pools
Wenn alles gespeichert wurde, sollte die Storage-Liste nun in etwa so aussehen.

Knock, Knock – Lassen wir unsere Proxmox-VMs miteinander reden

Damit unsere VMs unter Proxmox miteinander – und später auch mit der Außenwelt – reden können, brauchen wir ein privates Netzwerk zwischen den Hosts. Na gut, nach außen geht jetzt schon mit host-gebundenen privaten IPs. Wir wollen aber eine gewisse Redundanz schaffen, daher werden wir einen Open vSwitch konfigurieren, über den die VMs miteinander und mit der Router-VM (dem Master) sprechen können.

Vorbereitend müssen wir auf dem Master Node eine Systemkonfigurationsvariable anpassen, damit die ARP-Kommunikation zwischen Hetzner Routern und unserem Cluster ordnungsgemäß funktioniert, und die Hetzner Router wissen, dass sie Pakete an unsere VM-WAN-IPs an den Master routen müssen.
Dafür schreiben wir die Zeile net.ipv4.conf.ens3.proxy_arp = 1 in die /etc/sysctl.conf.
Zusätzlich nehmen wir die Raute vor folgender Zeile noch weg:
net.ipv4.ip_forward=1 Rebootet wird an dieser Stelle noch nicht.

Für den nächsten Schritt sollten wir uns eine Node-zu-ID Liste erstellen. Glücklicherweise haben wir unsere Nodes sinnvoll benamt. Der Master ist bei mir die 0, Node 1 die 1, Node 2 die 2. Genau wie bei den privaten IPS(.10, .11, .12, …).

Unser virtuelles Netzwerk

Wir werden nun die Nodes auf vmbr1 – dem internen Netz – untereinander über einen Hetzner vSwitch, sowie die vmbr0 von jedem Worker Node aus per GRE mit dem “Master” verbinden.
Wichtig ist, dass dieses Setup nach wie vor für ein Low-Cost-Cluster gedacht ist. Hat man viele Nodes, sollte man auch die paar Euro für ein floating network ausgeben können und alles standardgemäß ausführen.

Wir werden in den nächsten Schritten mehrfach manuelle Änderungen an der interfaces-Datei durchführen. Dabei ist es wichtig, dass die Reihenfolge nach Abhängigkeiten der Schnittstellen gewahrt wird.
Zuerst die physikalischen Interfaces, dann die darauf basierenden VLAN-Interfaces.
Erst zum Schluss die vmbr-Einträge.

Interconnect zwischen den Worker Nodes

Zunächst legen wir im Hetzner Robot einen vSwitch mit der VLAN ID 4001 an (auch hier wieder sinnvoll benamt – vmbr1 bekommt VLAN 4001), hängen unsere Nodes jeweils hinein und konfigurieren in der /etc/network/interfaces auf jedem der (physikalischen) Worker Nodes zusätzlich folgendes Interface:

auto enp34s0.4001
iface enp34s0.4001 inet manual
	mtu 1400
	vlan-raw-device enp34s0
# Physical Hetzner vSwitch Interface for LAN

Hinweis: Falls eine interfaces.new existiert, wurden Netzwerkkonfigurationen in Proxmox noch nicht übernommen. In diesem Fall müsste die Anpassung in der .new Datei durchgeführt werden. Dadurch ergibt sich im weiteren Verlauf eine Abweichung, weshalb ich empfehle, dann vor dem Eintragen kurz den betreffenden Node zu rebooten.
Außerdem muss in allen Konfigurationen enp34s0 ggnf durch den Namen eurer Netzwerkkarte ersetzt werden. Die MTU von 1400 ist von Hetzner vorgegeben.

Den Master-Node mitreden lassen

Wir werden im nächsten Schritt die vmbr0 unserer Nodes mit der des Masters über GRE verbinden.
Dafür müssen wir auf jedem Worker Node einen einzelnen sowie auf dem Master-Node je einen virtuellen Port pro Worker in die OVS-Bridges einfügen.
Wichtig zu wissen: Wir verbinden nur die WAN-Bridge mit dem Master. Er kann ohnehin keine VMs halten und wir brauchen uns so keine Gedanken um Verschlüsselung machen, da wir die Daten einfach 1:1 durchreichen.

Wir nutzen auf der vmbr0 ein kleines privates IP-Netz, das wir in Teil 2 definiert hatten, welches lediglich dem Transport der WAN-Daten dient. Die privaten IPs sind gleichzeitig eine Möglichkeit, den Zustand des Netzes zu prüfen, sie sind streng genommen an den Worker Nodes überflüssig.
Wir müssen dafür nochmals Hand an die /etc/network/interfaces Datei aller Nodes legen.

Auf dem “Master” Node (zukünftige Control Plane):

Im Konfigurationsblock der vmbr0 – sollte nach Teil 2 bereits existieren – fügen wir für jeden Worker Node einige Zeilen (up/down script) hinzu, um den vSwitch per GRE zu den Worker Nodes zu verbinden.

allow-ovs vmbr0
iface vmbr0 inet static
	address  192.168.254.10
	netmask  24
	ovs_type OVSBridge
	up ovs-vsctl add-port ${IFACE} gre_node1 -- set interface gre_node1 type=gre options:remote_ip=xxx.yyy.zzz.101
	down ovs-vsctl del-port ${IFACE} gre_node1
	up ovs-vsctl add-port ${IFACE} gre_node2 -- set interface gre_node2 type=gre options:remote_ip=xxx.yyy.zzz.101
	down ovs-vsctl del-port ${IFACE} gre_node2
#HA WAN Network

Wir stellen zwei Verbindungen her. Über gre_node1 (weil Host ID 1) zur externen IP des Worker Node 1 (xxx.yyy.zzz.101).
Das gleiche nochmal über gre_node2 zum zweiten Worker Node. Eventuelle weitere Nodes würden dem entsprechend über gre_node3, 4, 5 usw verbunden werden.

Auf den Worker Nodes:

Hier müssen wir abgesehen vom vmbr0 GRE-Tunnel auch die Hetzner vSwitch-Interfaces für vmbr1 mit berücksichtigen. Hierfür passen wir die up und down lines der vmbr-interfaces auf den Worker Nodes in der Datei /etc/network/interfaces wie folgt an – Öfentliche Master-IP-Adresse natürlich wieder korrekt eintragen:

allow-ovs vmbr0
iface vmbr0 inet static
	address  192.168.254.1x
	netmask  24
	ovs_type OVSBridge
	up ovs-vsctl add-port ${IFACE} gre_master -- set interface gre_master type=gre options:remote_ip=xxx.yyy.zzz.100
	down ovs-vsctl del-port ${IFACE} gre_master
#WAN via master

allow-ovs vmbr1
iface vmbr1 inet static
	ovs_type OVSBridge
	up ovs-vsctl add-port ${IFACE} enp34s0.4001
	down ovs-vsctl del-port ${IFACE} enp34s0.4001
#VM-LAN

Die Worker Nodes verbinden ihre vmbr0 über gre_master mit dem Master (xxx.yyy.zzz.100). Einen direkten “Crosslink” zwischen den Nodes gibt es nicht. Der macht auch ob seines Zwecks als reines WAN-Netz (Uplink) keinen Sinn. vmbr1 wird die 1400er MTU vom VLAN-Interface erben. Wir müssen das später auch in dem VMs immer von Hand korrekt einstellen.

Anschließend rebooten wir den gesamten Proxmox Cluster (auch den Master!) um alle noch nicht übernommenen sysctl und interface Änderungen von vorhin zu aktivieren und zu prüfen, ob unser System ordnungsgemäß erreichbar ist. Dabei allerdings sollte man aber nie mehr als 50% der Nodes gleichzeitig ausschalten, um immer “quorate” zu bleiben und eine fälschlich angenommenes Split-Brain-Szenario zu vermeiden.

Nach dem Reboot können wir das Netz kurz testen, indem wir von allen Nodes aus alle anderen einmal über deren private IP auf vmbr0 anpingen.

Hallo Welt! – Externes Routing

Unsere späteren VMs sollen auch mit der Außenwelt kommunizieren können, benötigen aber nicht alle eigene öffentliche IP-Adressen. Statt dessen werden wir einen Reverse-Proxy einrichten (das ist aber nicht teil dieser Serie).
Die VMs sollen darüber hinaus wie ein PC im heimischen Netzwerk Daten aus dem Internet abrufen können. Hierfür brauchen wir zwei Dinge: eine öffentliche IP und eine Router/Firewall-VM.
Als IPs für die öffentliche Seite nutzen wir eine Floating IP aus der Cloud Konsole. Wir müssen diese IP dem Master Node zuweisen. Von dort aus können wir sie – dank unserer sysctl-Anpassung – über die vmbr0 in unser Virtuelles WAN-Netz weiterrouten, um diese dann unserer Firewall-VM zuzuweisen.

Zunächst laden wir uns das ISO-Image von OpnSense herunter und übertragen es auf den Server in den ISO-Store. Am einfachsten geht das, indem man in der Web-GUI von Proxmox links in der Server View bei einem beliebigen Node auf den Storage klickt, der die ISO-Images hält. Unter Content klicken wir dann oben auf Upload.
An der selben Stelle kann man dann auch Container templates für LXC-Container hochladen bzw über Templates aus einem Store herunterladen.

Erstellen der ersten VM

Wir erstellen nun in Proxmox eine VM für OpnSense über den Create VM Button oben rechts.
Als Node wählen wir einen beliebigen Worker Node aus – nur eben nicht den Master. Wichtig ist auch der Haken bei Start at boot.

Proxmox-Assistent zum Hinzufügen einer VM

Im zweiten Schritt mounten wir das ISO-Image, das wir eben auf den Store geladen haben und klicken auf Next, setzen den Haken bei Qemu Agent -> Next.
Ich persönlich bevorzuge es, die Platten etwas größer auszulegen (wir haben ja ohnehin thin provisioning aktiv) und setze diese daher auf 40 GB und wähle wieder Next.
Für eine Firewall bietet es sich an, die AES-Extensions explizit zu aktivieren (vorher unten Advanced anhaken) und zwei vCores zuzuweisen.

AES sollte in diesem Proxmox-Fenster aktiviert werden

Wir weisen im nächsten Schritt 1024 MiB RAM zu, setzen den Minimum auf 512 MiB was ein Ballooning ermöglicht.
Nach einem Klick auf Next dürfen wir die erste Netzwerkschnittstelle konfigurieren.
Wir stecken diese in unser WAN-Helper-Netz auf vmbr0 und belassen alles weitere so, wie es ist. Next. Finish.

Eine Firewall / Router VM braucht aber mehr als nur eine NIC. Wir klicken links auf unsere neu kreierte VM, anschließend auf Hardware und fügen über Add -> Network Device eine neue NIC hinzu. Diese NIC sollte auch vom Typ VirtIO sein und im richtigen vSwitch hängen. Die Firewall lassen wir aus. Man könnte hier manuelle MAC-Adressen zuweisen, um diese bspw. fortlaufend zu generieren.

Proxmox-Dialog zum Hinzufügen einer Netzwerkkarte

Ist das erledigt, schalten wir die VM durch einen Klick auf Start ein und öffnen die Konsole mit dem Console-Button.

Installation von OpnSense als Router-VM unter Proxmox

Nach circa einer Minute ist der Installer gebootet. Wir loggen uns an der Konsole mit dem Benutzer “installer” und dem Passwort “opnsense” ein.
Als erstes werden wir die Keymap korrigieren

OpnSense Installer

Danach starten wir das Guided Setup und installieren auf den Datenträger da0 um GPT/UEFI mode. Die SWAP-Partition lassen wir anlegen, denn einen OOM-Absturz der Firewall wollen wir absolut vermeiden.
Nach 1-2 Minuten fragt uns das System nach einem root-Passwort. Wir tragen eines ein und speichern dieses. Anschließend können wir die Firewall rebooten. Wir müssen in Proxmox die “CD” auswerfen, sobald die Firewall meldet, dass sie in 5 Sekunden rebootet. Dafür editieren wir das CD/DVD-Drive:

Proxmox-Dialog zum entfernen der CD

Sollte der Reboot nicht funktionieren, kann man bedenkenlos über Proxmox oben rechts einen Reset auslösen. Das passiert leider manchmal beim Entfernen der CD, da sie ja auch das Bootmedium ist bzw war.

Basiskonfiguration der OpnSense

Nach dem Reboot können wir uns wieder an der Konsole anmelden. Benutzer ist root, das Passwort haben wir eben selbst gewählt. Wir müssen nun die Interfaces korrekt zuordnen. Dafür wählen wir Option 1 und drücken Enter.

Notiert euch anhand der MAC-Adressen welche vtnet-Interfaces in welche Netze gehen und fügt diese entsprechend ein:
VLANs -> Enter
WAN -> vtnetX (das Interface auf vmbr0)
LAN -> vtnetX (das Interface auf vmbr1)
OPT -> Enter

Kurze Kontrolle, wenn alles stimmt -> y und anschließend Enter

Nun benötigen unsere Interfaces noch IP-Adressen. Dafür müssen wir uns zunächst ein internes Netz aussuchen. Wir verwenden im Beispiel die 192.168.200.0/24
In der OpnSense-Shell wählen wir nun Option 2 und konfigurieren alle Interfaces:

Interface -> 1 (LAN)
DHCP -> N
IPv4 -> 192.168.200.1
Subnet bit Count -> 24
Gateway -> ENTER
IPv6 via WAN Interface Tracking -> N
DHCPv6 -> N
Enter IPv6 -> ENTER
Enable DHCP -> N
HTTP -> N

Interface -> 2 (WAN)
DHCP -> N
IPv4 -> xxx.yyy.zzz.nnn (Eine freie Floating IP der CloudVM)
Subnet bit Count -> 32
Gateway -> 192.168.254.10 (Die vmbr0-IP des Master Nodes)
Gateway as Name Server -> N
Name Server -> 213.133.98.98
DHCPv6 -> N
Enter IPv6 -> ENTER
HTTP -> N

Um die Firewall konfigurieren zu können, benötigen wir Zugriff auf die Web GUI von OpnSense. Hierfür geben wir einem der Worker Nodes temporär eine IP auf der vmbr1, die ja mit der LAN-Schnittstelle der Firewall verdrahtet ist. Leider müssen wir den Host hierfür wieder rebooten, also empfiehlt sich ein Host, auf dem gerade noch keine VMs laufen.
Wie das geht, muss ich denke ich nicht nochmals erläutern.

Während dessen können wir an der Master-VM schon mal die IP-Weiterleitung konfigurieren. Hierfür legen wir einfach eine Forwarding-Route an, welche besagt, dass die öffentliche IP der Firewall im Netzerk hinter vmbr0 liegt:
ip route add xxx.yyy.zzz.nnn/32 dev ${IFACE}
Hierbei ist xxx.yyy.zzz.nnn die zuvor für die Firewall verwendete öffentliche Floating IP.
Diese müssen wir noch persistent machen, dafür tragen wir den selben Befehl im vmbr0 Block der /etc/network/interfaces als “up” action mit ein. Strikt nach Anleitung sieht der Block dann jetzt so aus:

allow-ovs vmbr0
iface vmbr0 inet static
	address  192.168.254.10
	netmask  24
	ovs_type OVSBridge
	up ovs-vsctl add-port ${IFACE} gre_node1 -- set interface gre_node1 type=gre options:remote_ip=xxx.yyy.zzz.101
	down ovs-vsctl del-port ${IFACE} gre_node1
	up ovs-vsctl add-port ${IFACE} gre_node2 -- set interface gre_node2 type=gre options:remote_ip=xxx.yyy.zzz.101
	down ovs-vsctl del-port ${IFACE} gre_node2
	up ip route add xxx.yyy.zzz.nnn/32 dev ${IFACE}
#HA WAN Network

Anmerkung: Wollen wir einmal eine andere VM direkt ins WAN nehmen, funktioniert das ganze ähnlich.
– Die VM bekommt am WAN-Interface die öffentliche Floating IP zugeordnet mit /32er Netzmaske (255.255.255.255)
– Die VM braucht eine direkte (persistente) Route via ihres WAN-Interfaces zur öffentlichen IP des Masters
– Der Master muss wissen, dass die IP über vmbr0 weitergereicht werden muss (Forwarding-Route)
Wichtig: Alle VMs die über die vmbr0 kommunizieren wollen, benötigen eine MTU von 1350! Das ist technisch bedingt, ansonsten verwerfen die Switches unsere Pakete.

Stellen wir nun mit PuTTY eine SSH-Verbindung mit dem eben rebooteten Host her, können wir über einen Rechtsklick auf die Titelleiste -> Change settings einen SSH-tunnel einstellen:

Putty-Konfiguration

Anschließend können wir die OpnSense GUI über https://127.0.0.1:8000 aufrufen.
Bereits an dieser Stelle sollten VMs aus dem LAN auf das Internet zugreifen können. Wenn wir später Dienste nach draußen freigeben wollen, nutzen wir die Funktion Firewall -> NAT.
Zur späteren Verwaltung im Betrieb empfehle ich, einen VPN-Zugriff über OpenVPN einzurichten.

In der Firewall können wir nun eine Regel im WAN-Interface erstellen, die es uns erlaubt, ICMP-Pakete über WAN an die Firewall zu senden:

Danach lässt sich unsere erste VM auch schon aus dem Internet erreichen. Dies können wir wieder mit einem simplen Ping überprüfen.

Wie weiter oben bereits erwähnt müssen alle VMs auf vmbr0 eine MTU von 1350 nutzen. Stand 29.12.2019 ist ein Feature in Proxmox gerade in ENtwicklung, welches das für VirtIO-Netzerkinterfaces einfacher macht. Aktuell muss man dies aber leider noch manuell bei den VMs in die Netzerkkonfiguration eintragen, so auch in die Firewall unter Interfaces -> [WAN]:

Die MTU muss auf 1350 gestellt werden

Ab jetzt ist unsere Firewall und die zukünftigen VMs dahinter online. Wir sollten nun abschließend einmal auf Aktualisierungen für die OpnSense-Software prüfen und diese installieren. Das geht im Menü unter System -> Firmware -> Update.

Schlusswort und Vorschau

Wir haben heute unser Cluster mit Storage und einer Internetverbindung ausgestattet. Natürlich gibt es einen besseren weg für WAN-Konnektivität: Die IP-Subnetze auf dem Hetzner vSwitch. Die kosten allerdings auch deutlich mehr und können ebenfalls nicht mit Standard-Mitteln bis zur Cloud VM geroutet werden. Darüber hinaus hätten wir hier nur 1 TB Traffic frei.
Wie zuverlässig unser Setup am Ende ist, wird sich zeigen. Falls es zu Problemen kommt, habe ich ja die Möglichkeit offen, auf ein vSwitch-Netz umzusteigen.

Im nächsten Teil werden wir uns nun endlich der Installation und Konfiguration von Kubernetes widmen. Allerdings hat sich eine kleine Planänderung ergeben: Wir werden Kubernetes nicht wie ursprünglich geplant direkt auf die Nodes, sondern in VMs installieren.

Ich möchte an dieser Stelle allen Lesern einen guten Rutsch und alles Gute fürs neue Jahr wünschen. Hier geht es dann im Januar wieder weiter.

About the Author

Inhaber / Streamer / Lead Dev von Firesplash Entertainment und ganz nebenbei Fachinformatiker Systemintegration in einem mittelständigen Unternehmen

View Articles