„Stell einen Load Balancer davor“ ist der häufigste Satz im System Design, und er verdeckt eine Handvoll Entscheidungen, die in Produktion zählen: auf welcher Schicht er arbeitet, wie er ein Backend auswählt, woher er weiß, dass ein Backend gesund ist, und was mit laufenden Anfragen passiert, wenn eines entfernt wird.
Dieser Artikel geht diese Entscheidungen durch und zeichnet das zweistufige Setup, bei dem die meisten großen Deployments landen: ein Layer-4-Balancer vor einer Flotte von Layer-7-Proxys.
Layer 4 vs. Layer 7
| Layer 4 | Layer 7 | |
|---|---|---|
| Sieht | IP-Adressen, Ports, TCP- oder UDP-Verbindungen | HTTP-Anfragen: Host, Pfad, Header, Cookies |
| Verteilt | Verbindungen | Anfragen |
| TLS | Reicht es durch oder terminiert es | Terminiert es meistens |
| Kann routen nach | Port, Quelladresse | Pfad, Host, Header, Methode |
| Kann außerdem | Sehr hohen Durchsatz, niedrige Latenz | Retries, Timeouts, Rewrites, Authentifizierung, Rate Limits |
| Beispiele | Network Load Balancer der Clouds, IPVS, Balancer im Stil von Maglev | Envoy, NGINX, HAProxy im HTTP-Modus, Application Load Balancer der Clouds |
Der Unterschied, der wehtut: Ein Layer-4-Balancer verteilt Verbindungen, keine Anfragen. Bei HTTP/2 oder gRPC öffnet ein Client eine langlebige Verbindung und schickt Tausende Anfragen darüber, die alle beim selben Backend landen. gRPC gut zu verteilen braucht einen Layer-7-Proxy oder clientseitiges Load Balancing.
Warum zwei Stufen
Große Setups stapeln meist beides. Eine Layer-4-Stufe hält die öffentlichen IP-Adressen, fängt Verbindungsfluten ab und verteilt Verbindungen per Hash über jeden Flow auf eine Flotte von Layer-7-Proxys. Die Proxys terminieren TLS, wenden Routing-Regeln an und verteilen einzelne Anfragen auf Backend-Pools.
Jede Stufe skaliert für sich: die Layer-4-Stufe nach Paketen und Verbindungen, die Proxy-Flotte nach Anfragen und TLS-Handshakes. Die Trennung begrenzt auch den Explosionsradius, weil eine fehlerhafte Proxy-Konfiguration die Proxys hinter einer Stufe trifft, nicht die Adressen, mit denen sich Nutzer verbinden. Und weil Proxys zustandslos sind und eine Konfiguration teilen, bedeutet ein weiterer Proxy einfach mehr Kapazität.
Balancing-Algorithmen
| Algorithmus | Wie er auswählt | Gut für | Achtung bei |
|---|---|---|---|
| Round Robin | Das nächste Backend der Reihe nach | Identische Backends, ähnliche Anfragen | Langsame Anfragen stauen sich auf langsamen Backends |
| Weighted Round Robin | Round Robin im Verhältnis von Gewichten | Gemischte Instanzgrößen, schrittweise Verschiebungen | Gewichte sind statisch |
| Least Connections | Wenigste offene Verbindungen | Langlebige Verbindungen | Braucht globalen Zustand im Balancer |
| Least Request (Power of Two Choices) | Zwei zufällig wählen, an das weniger ausgelastete senden | Die meisten HTTP-Services | Braucht Anfragezahlen pro Backend |
| Konsistentes Hashing | Hash eines Schlüssels, etwa der Nutzer-ID | Caches, feste Zuordnung | Ungleiche Last bei Hot Keys |
Least Request mit zwei zufälligen Kandidaten ist ein guter Standard für HTTP-Services. Die Load-Balancer-Dokumentation von Envoy beschreibt es: zwei Hosts zufällig wählen (standardmäßig zwei) und die Anfrage an den mit weniger aktiven Anfragen senden. Forschung von Mitzenmacher und anderen zeigt, dass das fast so gut ist, wie jedes Backend zu prüfen, ohne die Kosten. Konsistentes Hashing, etwa Ring Hash oder der Maglev-Algorithmus, hält denselben Schlüssel auf demselben Backend und verschiebt bei Änderungen der Backends nur einen kleinen Teil der Schlüssel, genau das, was Caches wollen.
Health Checks
Ein Balancer muss aufhören, Traffic an Backends zu schicken, die ihn nicht bedienen können. Zwei Mechanismen arbeiten zusammen:
- Aktive Checks: Der Balancer ruft einen Health-Endpunkt auf, zum Beispiel
GET /healthzalle 5 Sekunden, und nimmt ein Backend nach einer Anzahl aufeinanderfolgender Fehlschläge heraus. - Passive Checks (Outlier Detection): Der Balancer beobachtet echte Antworten, wirft ein Backend hinaus, das Fehler liefert oder in Timeouts läuft, und holt es nach einer Abkühlphase zurück.
Mach den Health-Endpunkt ehrlich, aber günstig. Prüft er jede Abhängigkeit, macht eine langsame Datenbank alle Backends gleichzeitig ungesund, und der Balancer hat keinen Ort mehr für den Traffic. Prüf, was diese Instanz braucht, um Anfragen zu bedienen, und überlass gemeinsame Abhängigkeiten der Fehlerbehandlung der Anwendung.
Draining und Sticky Sessions
Wird ein Backend für ein Deployment oder ein Herunterskalieren entfernt, sollte es keine neuen Anfragen mehr bekommen, die laufenden aber abschließen. Konfiguriere eine Draining-Phase (Cloud-Balancer nennen sie Deregistration Delay), die länger ist als deine langsamste normale Anfrage, und lass die Anwendung beim Herunterfahren keine neue Arbeit mehr annehmen.
Sticky Sessions schicken einen Nutzer jedes Mal zum selben Backend, meist per Cookie. Sie helfen, wenn Zustand im Speicher eines Servers liegt, und schaden allem anderen: ungleiche Last, verlorene Sessions, wenn ein Backend stirbt, schwierigere Deployments. Verschieb den Session-Zustand in einen gemeinsamen Speicher und schalte Stickiness ab, wo du kannst.
Über eine Region hinaus
Alles oben passiert innerhalb eines Standorts. Nutzer über Regionen zu verteilen ist eine eigene Ebene: DNS-basiertes Routing, das mit der nächsten gesunden Region antwortet (Latenz- oder Geolocation-Routing), oder Anycast, bei dem viele Standorte dieselbe IP-Adresse ankündigen und das Internet-Routing jeden Nutzer zu einem nahen Standort bringt. Globales Balancing wählt die Region; die zwei Stufen in diesem Artikel wählen den Server. Lass Health-Signale nach oben fließen, damit eine Region, deren Proxys ihre Pools nicht erreichen, keine Nutzer mehr bekommt, statt ihnen Fehler zu liefern.
Das Architekturdiagramm
Diagramminhalt
- Clients
- DNS: one name
- Layer 4 load balancer
- Layer 7 proxy 1
- Layer 7 proxy 2
- Orders pool: 6 instances
- Search pool: 4 instances
- Static files bucket
- Proxy fleet, same config
- resolve
- TCP 443
- flow hash
- /api/orders
- /api/search
- /static
So liest du es:
- Links: Clients lösen einen DNS-Namen auf und öffnen TCP-Verbindungen auf Port 443 zum Layer-4-Load-Balancer.
- Mitte: Die Layer-4-Stufe verteilt Verbindungen per Flow-Hash auf die Proxy-Flotte. Der Rahmen zeigt, dass jeder Proxy dieselbe Konfiguration ausführt.
- Rechts: Die Proxys terminieren TLS und routen jede Anfrage nach Pfad:
/api/ordersund/api/searchin ihre Pools,/staticin einen Bucket. - Der Markdown-Block enthält die Routing-Tabelle, den Algorithmus pro Pool und die Health-Check-Einstellungen, also die Details, die sonst nur in einer Konfigurationsdatei stehen.
Was an ein Load-Balancer-Diagramm gehört
- Schicht und Protokoll an jedem Hop: TCP 443, HTTP/2, gRPC.
- Wo TLS terminiert. Das entscheidet, was jede Stufe sehen kann und wo Zertifikate liegen.
- Der Algorithmus pro Pool, und der Hash-Schlüssel, falls du hashst.
- Health-Check-Pfad, Intervall und Schwellenwert.
- Draining- und Stickiness-Einstellungen, falls vorhanden.
Das API-Gateway-Architekturdiagramm behandelt die Layer-7-Funktionen, die auf dem Routing aufsetzen, das Kubernetes-Ingress-Diagramm zeigt dieselben Ideen innerhalb eines Clusters, und das Netzwerktopologie-Diagramm ordnet Load Balancer in das größere Netzwerk ein.
In Archboard nachbauen
- Melde dich mit Google oder GitHub an und leg ein Diagramm an.
- Füge aus dem C4-Modell-Pack eine Person für die Clients hinzu und aus dem Netzwerk-Pack einen DNS- und einen Lastverteiler-Node.
- Füge aus dem Basis-Pack einen Rahmen für die Proxy-Flotte hinzu, mit zwei Container-Nodes darin.
- Füge aus dem Cloud-Pack zwei Recheninstanz-Nodes und einen Objektspeicher-Node für die Pools und den Bucket hinzu.
- Beschrifte die Kanten mit Protokollen und Routen und füge einen Markdown-Block aus dem Basis-Pack mit Routing-Tabelle und Health Checks hinzu.
Das Beispiel nutzt 10 Diagramm-Nodes und bleibt damit im Free-Plan.
FAQ
Was ist der Unterschied zwischen einem Layer-4- und einem Layer-7-Load-Balancer? Ein Layer-4-Balancer verteilt TCP- oder UDP-Verbindungen, ohne hineinzuschauen. Ein Layer-7-Balancer versteht HTTP, kann also nach Pfad oder Host routen und einzelne Anfragen verteilen.
Welchen Load-Balancing-Algorithmus sollte ich nutzen? Least Request mit zwei zufälligen Kandidaten ist ein starker Standard für HTTP. Nimm konsistentes Hashing, wenn derselbe Schlüssel dasselbe Backend erreichen soll, wie bei Caches.
Warum schickt mein gRPC-Service den ganzen Traffic an einen Pod? Weil gRPC langlebige HTTP/2-Verbindungen nutzt und ein Layer-4-Balancer Verbindungen verteilt, keine Anfragen. Setz einen Layer-7-Proxy dazwischen oder nutze clientseitiges Load Balancing.
Was sollte ein Health-Check-Endpunkt prüfen? Dass diese Instanz Anfragen bedienen kann: Der Prozess läuft, und seine eigenen kritischen Ressourcen sind verfügbar. Prüf keine gemeinsamen Abhängigkeiten, die alle Backends gleichzeitig scheitern ließen.
Sind Sticky Sessions eine gute Idee? Nur wenn der Zustand den Server nicht verlassen kann. Sie verursachen ungleiche Last und verlorene Sessions bei Ausfällen; ein gemeinsamer Session-Speicher ist meist besser.