Warum und wie Dropbox von Nginx auf Envoy umgestiegen ist
(dropbox.tech)Ein Artikel, der die Vorteile von Envoy gegenüber Nginx gut erklärt – bei Dropbox, das zig Millionen gleichzeitige Verbindungen, mehrere Millionen Requests pro Sekunde und Bandbreite im Terabit-Bereich verarbeitet.
Bisher: nginx (Open-Source-Version) + python2 + Jinja2 + YAML
→ Schon bei einer einzigen Änderung war ein vollständiges Redeployment nötig
→ Dynamische Teile wurden in Lua entwickelt
→ Komplexe Logik wurde im Go-basierten Proxy Bandaid verarbeitet
Das funktionierte fast 10 Jahre lang gut, passt aber nicht mehr gut zur heutigen Umgebung
→ Interne und externe (nicht öffentliche) APIs werden schrittweise von REST auf gRPC umgestellt, daher wird Transcoding im Proxy benötigt
→ Protocol Buffers sind zum Standard für interne Service-Definitionen geworden
→ Alle Software wird unabhängig von der Sprache mit Bazel gebaut und getestet
→ Mitarbeitende beteiligen sich ziemlich intensiv an den Open-Source-Communities wichtiger Infrastrukturprojekte
Auch operativ ist Nginx teuer in der Wartung
→ Die Logik zur Config-Erzeugung ist zu flexibel und auf YAML, Jinja2 und Python verteilt
→ Monitoring ist ein Mix aus Lua / Log-Parsing / systembasiertem Monitoring
→ Durch die starke Abhängigkeit von Third-Party-Modulen leiden Stabilität und Performance, außerdem entstehen Kosten durch häufige Upgrades
→ Deployment und Prozessmanagement von nginx unterscheiden sich stark von anderen Services. Es hängt stark von Dingen ab, die sich von den Standardsystemen wie syslog oder logrotate stark unterscheiden
Deshalb wurde erstmals seit 10 Jahren entschieden, nach einem Ersatz für Nginx zu suchen
- Warum nicht Bandaid (der von Dropbox selbst entwickelte Go-basierte Proxy)?
→ Go verbraucht mehr Ressourcen als C/C++.
→ Der TLS-Stack von Go unterstützt kein FIPS (Federal Information Processing Standards der USA)
→ Als internes Tool gibt es keine Unterstützung aus einer externen Community
Heute: Umstellung auf eine Traffic-Infrastruktur auf Basis von Envoy
----- Was Envoy besser machte als Nginx ------
- Performance *
Die Architektur von Nginx ist event-driven / Multi-Prozess. Unterstützung für SO_REUSEPORT & EPOLLEXCLUSIVE
Sie basiert auf einer Event Loop, ist aber nicht vollständig non-blocking. Beim Öffnen von Dateien oder beim Logging kann die Event Loop anhalten. (selbst wenn aio, aio_write und Threadpool aktiviert sind)
Dadurch entstehen Tail-Latenzen, teilweise mit Verzögerungen von mehreren Sekunden
Envoy hat ebenfalls eine ähnliche event-driven Architektur, ist aber thread- statt prozessbasiert
Unterstützt SO_REUSEPORT (mit BPF-Filter-Unterstützung) sowie Event Loops über libevent
Kein blockierendes I/O in der Event Loop. Auch Event-Logging ist non-blocking implementiert.
Theoretisch schienen die Performance-Eigenschaften ähnlich zu sein, und in den meisten Workload-Tests waren sie das auch tatsächlich.
Allerdings hatte Nginx im Long Tail höhere Latenzen. Ursache war die Unterbrechung der Event Loop bei viel I/O.
Ohne Statistik-Erfassung zeigt Nginx eine ähnliche Performance wie Envoy, aber das intern verwendete Lua-Tool zur Statistik-Erfassung machte Nginx in High-RPS-Tests dreimal langsamer. (Grund ist lua_shared_dict, das über Mutexes synchronisiert wird). Zwar liegt das Problem teilweise in der Statistik-Erfassungsmethode von Dropbox, aber eine effiziente Neuentwicklung wurde verworfen. (Man erwartete, dass Instrumentierung innerhalb von Nginx spätere Upgrades erschweren würde)
Da es solche Probleme bei Envoy nicht gab, konnten nach der Migration bis zu 60 % der Server freigegeben werden, die zuvor vom bestehenden Nginx genutzt wurden.
- Observability *
Die kostenlose Version von Nginx bietet im stub status-Modul nur 7 Statistiken
Das war natürlich unzureichend, daher wurden über den log_by_lua-Handler zusätzliche Statistiken bereitgestellt.
Außerdem gab es einen Parser für error.log, um Fehlerinformationen auszugeben, sowie einen separaten Exporter für interne Statuswerte von nginx.
Ein grundlegendes Envoy-Setup liefert Tausende verschiedene Metriken im Prometheus-Format
Von Proxy-Traffic-Informationen bis zu internen Serverzuständen,
sowie Statistiken pro Cluster / Upstream / Virtual Host und TCP/HTTP/TLS-Downstream-Statistiken pro Listener usw.
Zusammen mit diesen vielfältigen Statistiken erlaubt Envoy das Einstecken von Tracing Providern.
Das ist nicht nur für das Traffic-Team, sondern auch für Applikationsentwickler nützlich.
Schließlich kann Envoy Access Logs per gRPC streamen.
Dadurch sinkt der Aufwand, die syslog-to-hive-Bridge des Traffic-Teams zu unterstützen.
Einen gewöhnlichen gRPC-Service zu betreiben ist viel einfacher und sicherer, als eigene TCP/UDP-Listener anzubinden.
- Integration *
Die Integration von Nginx ist sehr Unix-artig. Die Configuration ist sehr statisch.
Sie hängt von Dateien wie Config-Files, TLS-Zertifikaten und Allowlists/Blocklists ab.
Das ist einfach und abwärtskompatibel, daher lässt es sich mit ein paar Shell-Skripten automatisieren,
aber je größer das System wird, desto wichtiger werden Testbarkeit und Standardisierung.
Envoy hat für solche Integrationen seinen eigenen Ansatz.
Es stellt APIs namens xDS bereit und fördert die Nutzung von protobuf und gRPC.
Envoy findet solche dynamischen Ressourcen, indem es diese xDS abfragt.
- xDS entwickelt sich inzwischen über Envoy hinaus unter dem Namen Universal Data Place API (UDPA) weiter und versucht, zum de-facto-Standard für L4/L7-Load-Balancer zu werden. Nach unserer Erfahrung funktioniert das gut. Wir versuchen, UDPA nicht nur für Envoy, sondern auch für den Katran-eBPF/XDP-L4-Load-Balancer zu verwenden.
Da Dropbox intern Services über gRPC integriert, passt das deutlich besser.
- Configuration *
Nginx hat den großen Vorteil einer für Menschen gut lesbaren Config-Datei.
Dieser Vorteil geht jedoch zunehmend verloren, wenn die Configuration komplexer wird und automatisch generiert wird.
Bei Dropbox wurde sie über Python2, Jinja2 und YAML erzeugt, wodurch sich auch das Datenmodell verheddert und komplex wurde.
Envoy besitzt ein einheitliches Datenmodell für Configuration. Alle Config-Werte sind in Protocol Buffers definiert. Dadurch werden Probleme der Datenmodellierung gelöst und Typinformationen kommen zu den Config-Werten hinzu.
Da protobuf innerhalb von Dropbox stark genutzt wird, erleichtert das die Integration
- Extensibility *
Für die Erweiterbarkeit von Nginx müssen C-Module geschrieben werden. Um sichere Module zu schreiben, braucht es erfahrene Senior-Entwickler. Für leichtere Modulentwicklung gibt es zwar auch Perl- und JS-Schnittstellen, diese sind aber sehr eingeschränkt. Deshalb ist der gebräuchlichste Weg die Nutzung von lua-nginx-module.
Der wichtigste Erweiterungsmechanismus von Envoy sind C++-Plugins. Die Dokumentation ist zwar nicht so gut wie bei nginx, aber es ist sehr einfach. Das liegt an sauberen, gut kommentierten Interfaces sowie an C++14 und der Standardbibliothek
Ein großer Unterschied von Envoy zu anderen Webservern ist die Unterstützung von WebAssembly (WASM).
Dadurch lassen sich Erweiterungen in verschiedenen Sprachen wie Rust entwickeln.
Dropbox nutzt WASM bisher noch nicht, aber das könnte sich ändern, wenn es irgendwann Unterstützung für das Go SDK von proxy-wasm gibt
- Building and Testing *
Nginx nutzt grundsätzlich eine Custom-Shell-basierte Configuration und ein make-basiertes Build-System. Das ist einfach und hervorragend, aber die Integration in ein mit Bazel gebautes Monorepo erfordert erheblichen Aufwand
Nginx hat Perl-basierte Integrationstests, aber keine Unit-Tests.
Envoys Build-System basiert bereits auf Bazel und ließ sich einfach in unser Monorepo integrieren.
Unterstützt Unit-Tests auf Basis von gtest/gmock sowie ein Integration-Test-Framework
- Security *
Der Nginx-Code ist sehr klein und hat nur wenige externe Abhängigkeiten, daher gibt es nicht viele sicherheitsrelevante Schwachstellen.
Envoy hat viel mehr Code, daher scheint es naturgemäß mehr Angriffsflächen zu geben. Deshalb setzt Envoy stark auf moderne Security Practices. Es nutzt AddressSanitizer, ThreadSanitizer, MemorySanitizer usw.
- Features *
Dieser Teil enthält viele subjektive Einschätzungen, bitte entsprechend einordnen
Nginx begann ursprünglich als Webserver zum Ausliefern statischer Dateien mit sehr geringem Ressourcenverbrauch.
Die Hauptfunktionen sind also Static Serving, Caching und Range Caching
Aus Proxy-Sicht fehlen Nginx viele Funktionen, die moderne Infrastruktur heute verlangt.
Es kann keine HTTP/2-Verbindungen zu Backends aufbauen, kein gRPC-Proxying mit Multiplexing und auch kein gRPC-Transcoding.
Durch das Open-Core-Lizenzmodell fehlen einige wichtige Funktionen in der „Community Version“
Envoy wurde von Anfang an als Ingress-/Egress-Proxy entwickelt und wird stark in Umgebungen mit viel gRPC-Last eingesetzt.
Die Webservice-Funktionen sind sehr rudimentär. Kein File Serving, Caching noch in Entwicklung, keine Brotli-Unterstützung usw.
Für solche Umgebungen wird auch ein Nginx-Setup genutzt, das Envoy als Upstream-Cluster verwendet
Sobald Envoy HTTP-Caching unterstützt, dürfte sich auch eine Migration solcher Static-Serving-Umgebungen anbieten
Envoy unterstützt viele gRPC-bezogene Funktionen
-
gRPC proxying
-
HTTP/2 zu Backends
-
gRPC → HTTP bridge (+ umgekehrt)
-
gRPC-WEB
-
gRPC JSON transcoder
Außerdem kann Envoy auch als Outbound-Proxy eingesetzt werden
-
Egress Proxy
-
Third-party-software-Service-Discovery mit der Courier-gRPC-Bibliothek
- Community *
Die Entwicklung von Nginx ist zentralisiert und größtenteils verborgen.
Die Entwicklung von Envoy ist offen und dezentralisiert. Sie läuft über GitHub-Issues/PRs und ist auch über Mailinglisten/Slack usw. sehr aktiv
----- Aktueller Migrationsstatus bei Dropbox -----
Nginx und Envoy werden seit einem halben Jahr parallel betrieben, während der Traffic per DNS schrittweise umgestellt wird
Die Migration verlief nicht völlig ohne Probleme; es gab kleinere Issues, aber keine schwerwiegenden Ausfälle.
Auch Lösungen für Probleme, die durch „unusual“ oder „non-RFC“-Verhalten entstanden, wurden zusammengefasst (Details dazu stehen im Haupttext)
** Nächste Schritte **
-
HTTP/3: Envoy unterstützt das inzwischen experimentell. Es soll getestet werden, sobald der Linux-Kernel für UDP-Beschleunigung aktualisiert wurde
-
Interner xDS-basierter Load-Balancer und Outlier Detection
-
WASM-basierte Envoy-Erweiterungen
-
Ersatz von Bandaid (Go-basierter Proxy) durch Envoy
-
Einsatz von Envoy Mobile auch in mobilen Apps
3 Kommentare
Vielen Dank, dass Sie den guten Inhalt so kurz und übersichtlich
zusammengefasst haben.
Vielen Dank.
Vielen Dank. :)
Ihre ausführliche Zusammenfassung und sogar Ihre freundlichen Anmerkungen haben mir sehr geholfen, es besser zu verstehen. Vielen Dank :)