Der Originalartikel wurde von Maximilian Wilhelm auf Englisch verfasst. Bei dieser deutschen Version handelt es sich um eine KI-gestützte Übersetzung.
Dieser Blogbeitrag ist der erste Teil einer Reihe über die Entwicklung und Architektur des Network Stacks hinter Hetzner Cloud. In diesem Beitrag betrachten wir unsere bisherige Entwicklungsarbeit und erklären, wie der aktuelle Network Stack auf Basis von Open vSwitch funktioniert. Obwohl der Network Stack für Cloud Server und Load Balancer ähnlich ist, konzentrieren wir uns auf Cloud Server und Virtualisierungshosts (VM-Hosts), da diese mehr Dienste und Funktionen bereitstellen.
Um den Beitrag kurz zu halten, lassen wir den Cloud Control Plane, der alle Server, Load Balancer, Private Networks usw. verwaltet, außen vor und betrachten ausschließlich den Network Stack auf den Hosts.
Wie wir ticken
Alle Cloud-Produkte benötigen vielseitige und zuverlässige Anbindungsmöglichkeiten, damit unsere Kunden und gegebenenfalls auch deren Kunden die Dienste erreichen können, die in unserem Netzwerk laufen.
Bei Hetzner setzen wir grundsätzlich auf eigene Lösungen, die wir selbst betreiben können, und auf Bausteine, die uns als Grundlage für weitere Entwicklungen dienen. Das gilt auch für unser Netzwerk und insbesondere für die Netzwerkanbindung unserer Cloud-Angebote.
Obwohl sich die Technologien im Laufe der Jahre verändert haben, haben wir stets Lösungen bevorzugt, bei denen die aktiven Komponenten auf den Hosts laufen, statt auf eine intelligente und komplexe physische Netzwerkinfrastruktur zu setzen. Deshalb haben wir unseren Aufbau so gestaltet, dass das physische Netzwerk eine stabile IP-Anbindung für die Hosts bereitstellt und die Hosts die übergeordneten Netzwerkfunktionen übernehmen. Dazu gehören Firewalls, Private Networks und unterstützende Infrastrukturdienste.
Indem wir diese Dienste und Netzwerkfunktionen auf den Hosts betreiben, gewinnen wir viel Freiheit und Flexibilität bei der Konzeption und Entwicklung unseres Produkts und sind nicht auf das Angebot der Hardwarehersteller beschränkt. So können wir die Zuständigkeiten zwischen Teams und Technologien klar trennen und bei den meisten Fehlern die Auswirkungen auf einen einzelnen Host begrenzen. Das bedeutet auch, dass wir die Lösung vollständig selbst verantworten und an unsere Bedürfnisse anpassen können und müssen. Aber das liegt ohnehin in unserer DNA.
Ein bisschen Geschichte
Im Laufe der Zeit hat Hetzner verschiedene Technologien eingesetzt, um die Netzwerkanbindung seiner Cloud-Produkte bereitzustellen.
Die Hetzner vServer starteten 2011 mit HDDs, nativen Linux-Bridges und statischen Routen. Dieses Setup bot Dual-Stack-Konnektivität mit bis zu 1 Gb/s pro Host. Zuletzt liefen rund 25.000 Instanzen mit diesem Setup.
Das Nachfolgeprodukt startete im September 2015 mit einem hyperkonvergenten Setup auf Basis von Ceph. Dabei wechselten wir zu dynamischem Routing über BGP und einem Linux-Bridge-Setup mit 1:1-NAT für IPv4 und vollständig gerouteten IPv6-Präfixen. Das ermöglichte eine bessere IP-Mobilität und die Wartung der Hosts ohne Auswirkungen auf Kunden. 2016 erhöhten wir die Bandbreite der Host-Anbindungen auf 2 × 10 Gb/s. Dieses Setup blieb bis 2018 im Einsatz und wuchs auf rund 50.000 Instanzen.
Mit dem Start von Hetzner Cloud im Jahr 2018 verzichteten wir auf NAT und wechselten zu direktem IPv4-Routing. Im Juli 2019 führten wir eine Data Plane auf Basis von Open vSwitch ein und ergänzten die Unterstützung für private Cloud-Netzwerke auf Basis von VXLAN-Kapselung. Im November 2020 begannen wir, auch den öffentlichen Teil des Netzwerks auf Open vSwitch umzustellen. Dadurch konnten wir im März 2021 zustandsbehaftete Cloud-Firewalls auf Basis von Open-vSwitch-Flows und netfilter einführen.
Bestandteile eines VM-Hosts
Wir haben bereits festgestellt, dass wichtige Teile des Network Stacks auf den VM-Hosts laufen. Aber was müssen diese bereitstellen, damit ein Cloud Server funktioniert?
Die meisten Server müssen über das Internet erreichbar sein, damit Nutzer von zu Hause oder aus dem Ausland auf das System und seine Dienste zugreifen können. Einige Server nutzen Private Networks – entweder anstelle der öffentlichen Netzwerkanbindung oder ergänzend dazu. Diese verbinden Server, Load Balancer und gegebenenfalls Dedicated Server über ein privates, virtuelles Netzwerk, auf das nur deine Systeme zugreifen können. Der Datenverkehr im privaten Netzwerk wird mithilfe von Virtual eXtensible LAN (VXLAN) und einem eindeutigen Virtual Network Identifier (VNI) pro privatem Netzwerk gekapselt. Der Network Stack des Hosts stellt sicher, dass nur Teilnehmer, die mit dem jeweiligen privaten Netzwerk verbunden sind, Daten senden oder empfangen können.
Um den Zugriff auf deine über das Internet verfügbaren Dienste zu steuern, bieten wir die Cloud-Firewall-Funktion an. Damit kannst du den Zugriff auf bestimmte Ports oder Protokolle des Servers steuern oder den ausgehenden Datenverkehr einschränken. Das sollte möglichst nah am Server geschehen, also auf dem Host. Eine zentrale Umsetzung zustandsbehafteter Firewalls würde zusätzliche Hardware und Netzwerk-Overlays erfordern, um den „bereinigten“ Datenverkehr zu den Servern zu transportieren. Auch eine hochverfügbare, zentrale Verbindungsverfolgung wäre dadurch deutlich komplexer.
Standardmäßig nutzen die Images unserer Cloud Server das Dynamic Host Configuration Protocol (DHCP), um IPv4-Adressen und Routen dynamisch zu konfigurieren. Deshalb müssen wir einen DHCP-Server bereitstellen, über den die Server ihre IPv4-Netzwerkkonfiguration für öffentliche und private Netzwerkschnittstellen anfordern können.
In den meisten Images übernimmt cloud-init Teile der Systemkonfiguration: zum Beispiel die Konfiguration des IPv6-Netzwerks, das Anstoßen von DHCP für private Netzwerkschnittstellen oder das Einbinden von Cloud Volumes. Dafür muss cloud-init Informationen über das lokale System oder nutzerspezifische Konfigurationsangaben von unserem Metadatenserver unter http://169.254.169.254/ abrufen. Neben cloud-init nutzen viele weitere Integrationen den Metadatenserver, darunter unser CSI-Treiber, hc-utils, Distributionswerkzeuge wie Afterburn und Ignition von Flatcar Linux oder Talos Linux.
Sowohl der DHCP-Server als auch der Metadatenserver laufen lokal auf jedem VM-Host, um eine möglichst hohe Ausfallsicherheit und Verfügbarkeit zu gewährleisten.
Neben diesen Diensten für die Nutzer betreiben die VM-Hosts eine Reihe interner Dienste. Sie stellen sicher, dass deine Server laufen, der Network Stack die richtigen Informationen zur Konfiguration der Netzwerkanbindung erhält und unser Monitoring weiß, was passiert.
In diesem Beitrag konzentrieren wir uns jedoch auf den Network Stack und schauen uns seine interne Funktionsweise genauer an.
Open vSwitch
Den größten Teil des Netzwerkdatenpfads übernimmt Open vSwitch, kurz OVS, ein virtueller Open-Source-Multilayer-Switch. Sein Datenpfad ist seit Langem Bestandteil des Linux-Kernels. Pakete für den User Space beziehungsweise die Control Plane stehen für alle gängigen Linux-Distributionen zur Verfügung.
OVS ist der standardmäßige Network Stack einiger Virtualisierungsumgebungen, und viele fertige Lösungen unterstützen OVS. Dazu gehören unter anderem Proxmox VE, OpenStack und oVirt, um einige bekannte Beispiele zu nennen. Wir nutzen jedoch keine dieser Lösungen, sondern verwalten KVM und alle zugehörigen Komponenten mit selbst entwickelten Werkzeugen.
Open vSwitch ist eine vielseitige Netzwerklösung mit zahlreichen Funktionen, von denen wir für unsere Anwendungsfälle und unser Setup nur einen Teil nutzen. OVS verwendet OpenFlow, ein offenes Protokoll, mit dem sich die Data Plane von Netzwerkgeräten konfigurieren und die Weiterleitung von Paketen festlegen lässt. Für jede Kommunikationsrichtung muss ein Flow vorhanden sein, der vorgibt, wie Pakete zu verarbeiten sind und wohin sie weitergeleitet werden sollen.
Solche Flows können exakte Übereinstimmungskriterien verwenden, zum Beispiel für die Kommunikation mit dem Gateway über ARP, NDP, ICMP usw., für Datenverkehr zu anderen Teilnehmern eines privaten Netzwerks oder für Verbindungen zu einem Dienst im Internet.
Orchestrierung von Open vSwitch – Flusskrebs
Um die Netzwerkanbindung unserer Server und Load Balancer bereitzustellen und eine oder mehrere Firewalls zu konfigurieren, die auf einen Server angewendet werden, müssen wir die erforderlichen Flows in Open vSwitch einrichten. Über diese Flows stellt Open vSwitch die entsprechenden Verbindungen und Funktionen bereit.
Eine gängige Lösung zur Orchestrierung von Open vSwitch ist das Control-Plane-Projekt Open Virtual Network (OVN). Es entstand parallel zu OVS und dient dazu, eine Flotte von Nodes zu verwalten, auf denen Open vSwitch läuft. OVN bietet eine Abstraktionsschicht, über die sich logische Routen und Switches konfigurieren lassen. Diese Konfiguration übersetzt OVN anschließend in OpenFlow.
Als wir Open vSwitch in den Stack von Hetzner Cloud integrierten, entschieden wir uns gegen diesen Ansatz und entwickelten stattdessen eine eigene Lösung namens Flusskrebs. Flusskrebs ist in Python geschrieben und stellt eine REST-API bereit, die der lokale Teil unserer Cloud Control Plane auf dem jeweiligen Host nutzt. Flusskrebs konfiguriert die Flows, die jede VM oder jeder Load Balancer für den ordnungsgemäßen Betrieb benötigt. Dazu gehören auch Flows zur Umsetzung sämtlicher Firewall-Regeln, die der Nutzer oder unser Backend konfiguriert hat – zum Beispiel, um ausgehenden SMTP-Datenverkehr standardmäßig zu blockieren.
Der Controller dient außerdem auf jedem Host als zentraler DHCP-Server für die privaten Netzwerkschnittstellen.
Network Stack eines VM-Hosts
Mit diesen grundlegenden Architekturideen und Bausteinen im Hinterkopf schauen wir uns nun den Network Stack unserer VM-Hosts genauer an. Die folgende Abbildung zeigt einen vereinfachten Überblick über die interne Funktionsweise des Network Stacks eines VM-Hosts.
Die meisten Hosts nutzen zwei Uplink-Schnittstellen mit jeweils 10 Gb/s, die mittels LACP zu einer Link Aggregation Group (LAG) beziehungsweise einem bond unter Linux gebündelt sind. Sie sind mit zwei physischen Switches verbunden, die ein virtuelles Chassis bilden, um Ausfallsicherheit und Bandbreite zu erhöhen. Um die Ausfallsicherheit weiter zu verbessern, haben wir im Laufe dieses Jahres begonnen, auf zwei nativ geroutete Uplinks mit BGP umzustellen, die mit zwei unabhängigen Switches verbunden sind. Dabei sind die Uplink-Schnittstellen nicht direkt mit der OVS-Bridge verbunden. Stattdessen übernimmt das Linux-System das Routing zwischen den Uplinks und OVS. So bleibt der Host unabhängig von OVS erreichbar, und die Installation und Wartung der Hosts wird einfacher.
Eine zentrale Open-vSwitch-Bridge bildet die Hauptkomponente für die Netzwerkanbindung und die Netzwerkfunktionen. Alle Cloud Server auf dem Host sind mit ihr verbunden. Jeder Server kann eine öffentliche Netzwerkschnittstelle und zusätzlich bis zu drei private Netzwerkschnittstellen haben. Mit den folgenden Templates erzeugen wir Flows, die ausgehenden Datenverkehr vom Server zum Netzwerk erlauben:
# Only process packets with correct source MAC addresstable=0, priority=9999, in_port={vm_port_no}, eth_src={mac}, actions=set_field:1->reg0,set_field:{vm_port_no}->reg5,resubmit(,1)# Only accept packets with an IPv4/IPv6 address associated with the servertable=3, priority=9999, in_port={vm_port_no}, ip_src={ip}, ip, actions=resubmit(,4)table=3, priority=9999, in_port={vm_port_no}, ipv6_src={ip}/64, ipv6, actions=resubmit(,4)table=3, priority=9999, in_port={vm_port_no}, arp, arp_op=2, arp_spa={ip}, actions=resubmit(,4)
Die folgenden Flows leiten eingehenden Datenverkehr zum Server weiter – jeweils ein Flow pro IPv4- oder IPv6-Präfix. Der Network Stack des Hosts identifiziert sich gegenüber der VM über eine fest definierte „virtuelle Gateway-MAC-Adresse“, die immer d2:74:7f:6e:37:e3 ist.
table=22, priority=9999, in_port={internet_port_no}, ip, ip_dst={ip}, actions=mod_dl_src:d2:74:7f:6e:37:e3,{vm_port_no}table=22, priority=9999, in_port={internet_port_no}, ipv6, ipv6_dst={ip}/64, actions=mod_dl_src:d2:74:7f:6e:37:e3,{vm_port_no}
Infrastrukturdienste wie DHCP- und Metadatenserver sind ebenfalls mit Open vSwitch verbunden. Sie laufen parallel zu jedem Server in einem eigenen Linux Network Namespace pro Server.
Beachte, dass der DHCP-Server udhcpd in der obigen Abbildung ausschließlich für die öffentliche Netzwerkschnittstelle des jeweiligen Servers zuständig ist. Wie oben beschrieben, enthält der Flusskrebs-Controller einen zentralen DHCP-Server, der alle privaten Netzwerkschnittstellen bedient. Die Flows zur Anbindung des öffentlichen DHCP-Servers sind ebenso einfach aufgebaut:
table=1, priority=9999, in_port={vm_port_no}, udp, udp_dst=67, actions={service_port_no}table=1, priority=9999, in_port={service_port_no}, udp, udp_src=67, actions={vm_port_no}
Neben Open vSwitch nutzen wir das Connection Tracking von Linux netfilter, um die zustandsbehaftete Cloud-Firewall-Funktion umzusetzen. Da alle Server eine globale Connection-Tracking-Tabelle gemeinsam nutzen, müssen wir verhindern, dass diese überläuft, und eine faire Nutzung für alle Server und Kunden sicherstellen. Diese Aufgabe übernimmt ctcount, ein weiteres internes Projekt. Es erfasst die Connection-Tracking-Einträge und setzt die Obergrenze von 80.000 gleichzeitig aktiven Verbindungen pro Server durch. Erreicht ein Server dieses Limit, können erst wieder neue Verbindungen aufgebaut werden, wenn eine bestehende Verbindung geschlossen wurde.
Status Quo
Diese Data Plane auf Basis von Open vSwitch hat uns gute Dienste geleistet und tut das größtenteils auch heute noch – selbst bei über einer Million Cloud Servern. Allerdings sind wir an einige Grenzen gestoßen und wollten Skalierbarkeit, Ausfallsicherheit und Flexibilität mit einem stärker spezialisierten Network Stack verbessern. In den vergangenen Jahren hat unser SDN-Team an einer solchen Lösung gearbeitet. Sie ist auf unsere Bedürfnisse zugeschnitten, lässt sich einfach betreiben und bietet eine solide Grundlage für neue Funktionen wie IPv6 für private Netzwerke.
Im nächsten Teil dieser Reihe stellen wir die nächsten Schritte auf unserem Weg vor: unseren neuesten, vollständig selbst entwickelten Network Stack und seine Funktionsweise. Bleib also dran!
Auf dem Hetzner Summit hatten wir die Session "Der neue Hetzner Cloud Netzwerkstack - Ein technischer Deep-Dive, wie wir deine Pakete zustellen", in der wir über unseren aktuellen Stack und unsere Entwicklungen gesprochen haben. Die Aufzeichnungen sollten in Kürze auf der Summit Page verfügbar sein.
