3 Punkte von GN⁺ 2024-10-27 | 1 Kommentare | Auf WhatsApp teilen
  • Der Betreiber von OpenFreeMap bündelte mehrere regionale VPS über A-Records derselben Subdomain zu einem Round-Robin-DNS-Setup und testete, welchen Server Browser und Cloudflare tatsächlich auswählen
  • Ohne separaten Load Balancer kann man Lastverteilung und Ausfallschutz erwarten, doch die tatsächlichen Ergebnisse hängen stark davon ab, wie Clients Adressen sortieren und Verbindungen erneut versuchen
  • Tests mit drei VPS in den USA, Europa und Singapur zeigten: Chrome und Firefox neigten dazu, beim Start einen zufälligen Server festzulegen, während Safari und curl nach wiederholten Anfragen zum näher gelegenen EU-Server konvergierten
  • Wenn einige Server offline waren, wechselten Browser und curl schnell auf Ersatzserver; Anfragen über den Cloudflare-Proxy verwendeten jedoch weiterhin den pro Client-IP festgelegten Origin, wodurch 521-Fehler auftreten können
  • Wenn Cloudflare Offline-Origins oder Server mit niedriger Latenz nicht korrekt auswählt, kann eine Verteilung auf Basis von Round-Robin-DNS Nutzer unabhängig von ihrem Standort mit einem langsamen Server verbinden

Grundidee von Round-Robin-DNS

  • Eine typische VPS-basierte Website fügt beim DNS-Anbieter einen einzelnen A-Record hinzu, um Traffic an eine bestimmte IP zu senden
  • Round-Robin-DNS ist ein Verfahren, bei dem mehrere Server-IPs für dieselbe Subdomain angegeben werden
    • Im Beispiel wurden mehrere A-Records für rr-direct.hyperknot.com und rr-cf.hyperknot.com eingerichtet
  • Von dieser Konfiguration kann man erwarten, dass sie die Last auf mehrere Server verteilt und Offline-Server meidet
  • Bei den meisten DNS-Anbietern lässt sich das ohne separaten Load Balancer einrichten; es ist ein einfacher und nahezu kostenloser Ansatz
  • Load-Balancing-Funktionen von Diensten wie Cloudflare können erhebliche Kosten verursachen

Nach welchen Kriterien kann ein Client einen Server auswählen?

  • Als relevante Standards werden RFC 8305 Happy Eyeballs und RFC 6724 genannt
  • Der Abschnitt zur Adresssortierung in RFC 8305 erklärt, dass ein zustandsbehafteter Client, der Aufzeichnungen über die erwartete Round-Trip-Time (RTT) jedes Adresspfads besitzt, eine Regel zur Auswahl von Zieladressen hinzufügen sollte, die Adressen mit niedrigerer RTT bevorzugt
  • Der Experimentator versteht dies als folgendes Verhalten
    • Prüfen, ob ein Server online oder offline ist
    • Online-Server nach Ping-Zeit sortieren

Testaufbau

  • Es wurden VPS in drei Regionen weltweit erstellt
    • USA
    • Europa
    • Singapur
  • In Cloudflare wurden drei proxied A-Records und drei nicht proxied A-Records konfiguriert
  • Jeder Server liefert mit nginx dieselbe Antwortstruktur
    • Alle Pfadanfragen werden auf color.png umgeschrieben
    • /server gibt /etc/hostname als text/plain zurück
  • color.png ist eine 1px-PNG-Datei, deren Farbe je nach Server unterschiedlich ist
    • US: grün
    • EU: blau
    • SG: rot
  • Die Hostnamen werden als test-eu, test-us und test-sg unterschieden
  • Da der Teststandort in Europa liegt, wäre die erwartete Auswahl der nächstgelegene EU-Server
  • Die HTML-Testseite visualisiert die Serverauswahl, indem sie ein 10x10-Raster mit zufälligen Bildern füllt

Client-Verhalten, wenn alle Server online sind

  • Chrome wählt aus mehreren Standorten eher zufällig einen aus und neigt dazu, beim einmal ausgewählten Server zu bleiben
    • Nach einigen Stunden bewertet er die Auswahl neu
    • Im Test blieb er teils stundenlang am langsamsten Server in Singapur hängen
    • Wenn kein HTTP/2 verwendet wird, kann er auch zufällig zwischen zwei Servern wählen und Muster erzeugen
  • Firefox verhält sich ähnlich wie Chrome
    • Beim Start wird ein zufälliger Standort gewählt
    • Nach einem Neustart des Browsers kann ein anderer zufälliger Standort ausgewählt werden
  • Safari wählt stets korrekt den nächstgelegenen Server
    • Selbst wenn ein Server kurz offline war und wieder zurückkehrt, findet Safari nach einigen Aktualisierungen wieder den EU-Server
  • curl korrigiert ebenfalls zum nahegelegenen Server
    • Nicht unbedingt beim ersten Lauf, aber wenn der Befehl zweimal ausgeführt wird, wechselt er immer zum nächstgelegenen Server
    • Im Beispiel wechselte die erste Anfrage von test-us zur nächsten Anfrage auf test-eu

Verhalten über den Cloudflare-Proxy

  • Cloudflare wählt auf Basis der Client-IP einen zufälligen Standort und verwendet anschließend weiterhin denselben Standort
  • Das beobachtete Verhalten ähnelt client_ip_hash modulo server_num
  • Von der Heim-IP aus verbindet Cloudflare unabhängig von allen Manipulationen zum US-Server
  • Über einen mobilen Hotspot wird immer zum EU-Server verbunden
  • Führt man denselben curl-Befehl auf mehreren VPS aus, verbindet sich jeder VPS mit einem zufälligen Standort weltweit, verwendet aber immer denselben Server
    • Das Beispielergebnis ist test-sg

Unterschiede, wenn einige Server offline sind

  • Auf dem US-Server wurde nginx mit service nginx stop angehalten und anschließend das Verhalten geprüft
  • Chrome, Firefox, Safari und curl erkennen den Offline-Server alle und wählen einen anderen Server aus
  • Selbst wenn der Server während des Ladens abgeschaltet wird, funktioniert die Ersatzverbindung so schnell, dass die Korrektur in weniger als einer Sekunde erfolgt
  • Cloudflare erkennt den Offline-Server nicht
    • Für eine Client-IP greift es weiterhin auf den einmal festgelegten Server zu, unabhängig davon, ob dieser online ist
    • Ist dieser Server offline, erhalten Nutzer einen Fehler
    • Das curl-Ergebnis lautet error code: 521

Fragen und Grenzen rund um Cloudflare

  • Das Verhalten, dass Cloudflare Offline-Origins nicht erkennt, wird als wahrscheinlich ein Bug im Netzwerk eingeschätzt
  • Auf Grundlage der Cloudflare-Dokumentation zu zero downtime failover wird erwartet, dass es sich wie Browser und curl verhalten sollte
  • Zumindest Offline-Server sollten erkannt werden
  • Noch besser wäre es, wenn Cloudflare wie Safari den Server mit der niedrigsten Latenz auswählen könnte
  • Beim aktuellen Verhalten könnten, wenn man einen Server in den USA und einen in Neuseeland betreibt, 50 % der US-Nutzer Antworten vom neuseeländischen Server erhalten
  • Für Safari-Nutzer kann Cloudflare langsamer sein als der Betrieb ohne Cloudflare
  • In der zugehörigen HN-Diskussion antworteten der CEO und der CTO von Cloudflare
  • Um das Experiment weiter betreiben zu können, wird außerdem gefragt, ob es eine Serverless-Plattform gibt, die HTTPS und Round-Robin-DNS ohne Kosten für drei VPS weltweit unterstützt

1 Kommentare

 
GN⁺ 2024-10-27
Meinungen auf Hacker News
  • Hm, ich habe das zuständige DNS-Team gebeten, zu erklären, was hier passiert.
    Sobald ich eine verlässliche Antwort habe, gebe ich auf HN Bescheid. Es ist ein paar Jahre her, dass ich den Code gesehen habe, und seitdem haben viele Leute weiter daran geändert :-)
    Meine Vermutung ist, dass es mit dem Verhalten zu tun hat, das der Autor im Blog erwähnt: die Affinität zwischen Client-IP und Backend-Server beizubehalten. Die Kernfrage ist: „Sollte man diese Affinität aufbrechen, wenn ein Backend-Server ausfällt?“ Wenn ich mehr weiß, antworte ich auf meinen Kommentar.

    • Unter dem Namen Session Affinity wurden schon wirklich viele Sünden begangen.
    • Update: Eine Änderung wird ausgerollt, damit auch bei kostenlosen Accounts unterbrechungsfreies Failover funktioniert.
  • Eine der frühen Lösungen für dieses Problem waren SRV-DNS-Records. Sie ähneln MX-Records, sollten aber nicht nur für E-Mail, sondern für alle Dienste gelten.
    Bei MX- und SRV-Records kann man eine Liste von Servern und Prioritäten angeben, die der Client ausprobieren soll; SRV hatte außerdem einen Parameter weight für Load Balancing. SRV schrieb jedoch vor, nur dann verwendet zu werden, wenn der jeweilige Protokollstandard die Nutzung von SRV ausdrücklich festlegt – im Grunde, um den politischen Kampf zu vermeiden, praktisch alle Standardprotokolle abzufangen und alle Clients dazu zu bringen, SRV abzufragen. Dadurch können HTTP-Clients SRV technisch nicht nutzen. Auch später, bei HTTP/2 und den nachfolgenden HTTP-Standards, wurde SRV wegen fragwürdiger Argumentation u. a. von Google nicht in die neuen HTTP-Protokolle aufgenommen. Für neue Entwicklungen ist SRV praktisch tot und scheint nur noch in einigen älteren Standards verwendet zu werden.
    Die neue Lösung fürs Load Balancing scheinen HTTPS- und SVCB-DNS-Records zu sein. Soweit ich es verstehe, wurde das von Leuten standardisiert, die zusätzliche Parameter in DNS unterbringen wollten, um den TLS-1.3-Handshake schneller zu starten und Roundtrips zu reduzieren. Der Record-Typ SVCB ist wie HTTPS, aber in einer allgemeinen Form wie SRV. HTTPS und SVCB haben den Prioritätsparameter von SRV und MX, aber nicht den weight-Parameter von SRV. Der Standard ist veröffentlicht, und einige Browser scheinen Unterstützung dafür zu haben, aber nicht alle haben sie aktiviert. Man muss sehen, was Browser in naher Zukunft tatsächlich tun.

    • Ein weiterer großer Vorteil von HTTPS-Records ist, dass sie am Domain-Apex eine saubere CNAME-ähnliche Delegation ermöglichen.
      Man braucht keine CNAME-Flattening-Tricks, die bei CDNs, die GeoDNS zusammen mit oder statt Anycast nutzen, Routing-Probleme verursachen können. Wenn ihr schon einmal gesehen habt, dass eine Plattform empfiehlt, statt der Apex-Domain die Subdomain www zu verwenden, liegt es daran; und es ist auch einer der Gründe, warum Akamai die Standardisierung von HTTPS-Records vorangetrieben hat, da Akamai GeoDNS verwendet.
    • Ich hoffe wirklich, dass sich SRV- oder MX-artige Records für HTTP ordentlich durchsetzen.
      Dass solche Records fehlen, schmerzt besonders, weil Leute häufig Websites am Domain-Apex hosten möchten. Allerdings kann es schwierig sein, MX-artige Records sicher zu verwenden, wenn man sich nicht auf DNSSEC verlassen kann.
  • Bei DNS-Load-Balancing gibt es wirklich unschöne Grenzfälle. Ich hatte es mit einem Go-HTTP/2-Client zu tun, der Round-Robin-DNS nutzt, und es gab Probleme.
    Der Go-HTTP/2-Client verwendet den ersten erreichbaren Server dauerhaft weiter und löst DNS nicht erneut auf. Dadurch kann es passieren, dass Clients neue Server nicht entdecken, wenn man dem Pool Server hinzufügt.
    Ein besonders pathologischer Fall ist: Alle Backends fallen aus, dann kommt ein einziges erstes Backend wieder hoch, und alle Clients bleiben an diesem Server kleben und wechseln nicht mehr. Selbst wenn die anderen Server wieder hochkommen, sind sie bereits mit dem ersten Server verbunden, sodass kaum neue Clients neue Verbindungen aufbauen.
    Bei grpc-go tritt ein ähnliches Problem auf. Der gRPC-DNS-Resolver löst nur dann neu auf, wenn eine Backend-Verbindung getrennt wird. Deshalb können gRPC-Clients sich auf einen Host konzentrieren und dort bleiben. Es gibt auch den Vorschlag, serverseitig MAX_CONNECTION_AGE zu setzen, um Clients nach einer gewissen Zeit regelmäßig zu trennen und sie dazu zu bringen, DNS erneut aufzulösen.
    Ich wünschte, es gäbe einen besseren Standardansatz für Service Discovery. Am Ende scheint das Beste, was man tun kann, ein requestbasierter Load Balancer über eine virtuelle IP zu sein, der Health Checks durchführt. Aber auch das verschiebt das Problem nur auf das System, das die virtuelle IP implementiert. Offenbar geht man davon aus, dass das Routingsystem im Vergleich zu den Backends relativ statisch ist, und daraus entsteht der Vorteil.
    Ich frage mich, wie man das auf Bare Metal macht. Ich weiß, dass es bei AWS/GCP usw. interne Load Balancer gibt, aber ich frage mich, worin der Trick bei der Implementierung besteht. Empfehlungen für passende Blogposts oder Whitepaper wären ebenfalls willkommen.

    • Ich bin kein DNS-Experte, aber sollte man nicht nach Ablauf der TTL erneut auflösen?
  • Es hieß: „Was passiert, wenn ein Server offline ist? Nehmen wir an, wir stoppen den US-Server: service nginx stop“, aber so sollte man nicht testen.
    Der Client sieht eine verweigerte Verbindung und geht zur nächsten IP weiter. In der Praxis kann es aber sein, dass der Server überhaupt nicht antwortet oder eine Verbindung annimmt und dann schweigt.
    Dann ist man auf Client-Timeouts angewiesen, und Round-Robin-DNS, das die Zuverlässigkeit erhöhen soll, wirkt plötzlich deutlich weniger attraktiv.

    • Stimmt. Man kann testen, indem man die physische Maschine oder VM mit dieser IP ausschaltet oder das Kabel zieht.
      Den Dienst zu stoppen ist eine geplante Maßnahme; in diesem Fall kann man zuerst DNS aktualisieren und es darüber abwickeln.
    • SIG_STOP oder ein DROP mit ip/nftables ist ein deutlich realistischeres Testverfahren.
  • „Wie Sie sehen, erkennen alle Clients das korrekt und wählen einen Ersatzserver“ ist der unschöne Kernpunkt. Die Zuverlässigkeit wird auf Client-Seite entschieden
    Zum Beispiel verhielt sich systemd-resolved früher einmal angeblich möglichst technisch korrekt und gab immer die niedrigste IP-Adresse zurück. Die Logik war: DNS Round Robin ist nicht sauber definiert, also ist es auch nicht falsch, immer die niedrigste IP zurückzugeben. Nach dem Aufruhr wurde das geändert, aber soweit ich weiß, war Debian 11 an dieses Verhalten gebunden oder blieb es noch lange Zeit
    Außerdem habe ich es oft mit Anwendungen zu tun, deren Retry-Verhalten miserabel ist oder ganz fehlt. Sie verhalten sich nach dem Motto: „Eine Verbindung wurde abgelehnt, also brechen wir alles ab, beenden den Vorgang und versuchen es nie wieder.“ Dann verbrennen 20–30 % der gesamten Requests
    Wenn es keine andere Option gibt, ist es eine akzeptable Lösung. Wie der Artikel sagt: Hat man einen guten HTTP-Client wie einen Browser, bei dem einige Retries konfiguriert sind, dann kann DNS Round Robin gut dafür sein, einen echten Load Balancer mit Health Checks usw. zu finden, und eine Erfolgsquote von 100 % liefern
    Aber DNS Round Robin ist kein Load Balancer, und ein Load Balancer ist besser

    • Umgekehrt ist DNS-Load-Balancing sehr effektiv, wenn man die Clients kontrolliert und ihr Verhalten garantieren kann
      An einem früheren Arbeitsplatz hatten wir interne DNS-Server mit Hunderten Millionen Records und 60 Sekunden TTL; sie wurden für ein maßgeschneidertes internes Routing-System verwendet, das eingehende Verbindungen von Kunden zu den richtigen Ressourcen innerhalb des Netzwerks leitete. Das funktionierte tatsächlich hervorragend. Routing-Änderungen waren so einfach wie DDNS-Updates, und per NOTIFY wurden Änderungen an alle untergeordneten Server gepusht, sodass die durchschnittliche Verzögerung bis zur vollständigen Übernahme unter 60 Sekunden lag. Dadurch ließen sich komplexere Tools leicht bauen, einschließlich eines Control Panels, mit dem man per Knopfdruck alles von einem einzelnen Server bis zu einem ganzen Rechenzentrum aus dem Dienst nehmen konnte
      Dieses System hatte definitiv auch raue Kanten, aber für diese Art von System war es schnell, leicht zu inspizieren und relativ kugelsicher
    • Man legt die Zuverlässigkeit damit in die Hände des Clients oder irgendeines dahinterliegenden cachenden DNS-Resolvers
      Dasselbe gilt für Failover. Wenn eine Region ausfällt: Soll der Traffic gleichmäßig auf andere Regionen verteilt werden, oder soll er in die nächstgelegene Nachbarregion strömen? Wenn dieses Verhalten wichtig ist, muss man die Kontrolle über das Traffic-Management behalten und darf sie nicht an andere abgeben
    • Auch der Aussage „Wenn es keine andere Option gibt, ist es eine akzeptable Lösung“ kann ich schwer zustimmen
      Heutzutage gibt es andere Lösungen, die man wählen sollte, lange bevor man bei „es gibt keine andere Option“ ankommt
  • Bei der Formulierung „die Last auf mehrere Server verteilen und automatisch erkennen, welche Server offline sind, um einen Online-Server auszuwählen“ würde ich vorsichtig an der automatischen Offline-Erkennung durch DNS herummäkeln: Round-Robin-DNS im Grundzustand taugt nur zur Lastverteilung
    Solange man keine intelligente Logik in den Client einbaut, passiert hinsichtlich der Erkennung des Verfügbarkeitszustands nichts automatisch. Die Einleitung des Artikels sagt das bis zu einem gewissen Grad, aber ich musste sie mehrfach lesen, um die Bedeutung zu erfassen. Fairerweise kann das auch an meinem Verständnis liegen. Nachdem ich den Rest des Artikels gelesen hatte, ging es dort vollständig um diese intelligente Logik
    Wenn der vom Browser gewählte 1/N-Server-Record nicht verfügbar ist, findet auf Protokollebene keine automatische Wiederherstellung und kein Retry statt
    Dazu noch „verwandter Spaß“: Java-DNS-TTL [1] und das Verhalten von .equals() [2] nicht vergessen
    [1] https://stackoverflow.com/questions/1256556/how-to-make-java...
    [2] https://news.ycombinator.com/item?id=21765788 (vor 5 Jahren, 168 Kommentare)

    • Route53 handhabt das, indem es Server aus der DNS-Antwort entfernt, wenn sie nicht healthy sind, und alle Antworten mit einer sehr niedrigen TTL ausliefert
      Es gibt zwar Clients, die TTLs ignorieren, aber sie sind ziemlich selten
    • Ein bisschen Eigenwerbung: Das ist ein freies Open-Source-Projekt, das Failover für Round-Robin-DNS bereitstellt und von NLnet unterstützt wird: https://codeberg.org/FedericoCeratto/rrdnsd
  • Wenn ein Server ausfällt, bleiben die weltweit verteilten und gecachten IP-Adressen erhalten, und man kann nicht verhindern, dass Nutzer auf diese Adressen zugreifen
    https://www.cloudflare.com/learning/dns/glossary/round-robin...

    • Es ist eine Überlegung wert, unnötige Zwischenschichten zu überspringen
      Auch Load Balancing hat Kosten, und es gibt zudem das Problem, dass Load Balancer Verbindungen auf subtile oder offensichtliche Weise kaputtmachen. Bei manchen Anbietern war die Verfügbarkeit des Load Balancers sogar schlechter als die unserer Hosts
      Wenn man die Clients kontrolliert, ist es ebenfalls sinnvoll, über die DNS-API der Plattform eine Liste von IPs abzurufen, sie passend zu mischen und durchzugehen. Noch besser ist es, wenn man für den Fall, dass DNS ausfällt, ein paar stabil zugewiesene IPs in das Client-Binary einbauen kann. DNS ist aber normalerweise nicht kaputt und eignet sich gut für betriebliche Änderungen, ohne bei jeder Aktualisierung des Clusters eine neue Konfiguration oder ein neues Binary ausrollen zu müssen
      Wenn der Client ein Browser ist, ist das Standardverhalten ziemlich in Ordnung. Normalerweise werden IPs der Reihe nach verwendet, was problematisch sein kann [1], aber ansonsten ist das Retry-Verhalten gut. Bei einer abgelehnten Verbindung wird sofort eine andere IP versucht, und bei einem Timeout werden zumindest einige andere IPs ausprobiert. Ideal ist das nicht; für Browser würde man daher einen Load Balancer verwenden, und wenn möglich zumindest für den initialen Seitenaufruf. Für WebSockets und Ähnliches könnte man DNS-Round-Robin und eine halbwegs intelligente JS-Client-Logik nutzen. Trotzdem ist es durchaus möglich, DNS-Round-Robin für eine ganze Website zu verwenden
      Wenn der Client weder ein Browser ist noch von mir kontrolliert wird, bleibt nur, Glück zu wünschen
      Ich stimme zu 100 % zu, dass man gelegentlich davon ausgehen muss, dass jemand beim Bau eines DNS-Caching-Resolvers das TTL-Feld nicht als Sekunden, sondern als Tage interpretiert. Clients hinter einem solchen Resolver bekommen Probleme, wenn DNS aktualisiert wird. Wenn der Load Balancer jedoch hinter einem DNS-Namen liegt und irgendwann dessen Adresse geändert werden muss, wird man dieses Problem dann erleben – und dann ohne Erfahrung damit
      [1] Einer der RFCs schlägt vor, dass OS-APIs Antworten nach Präfixübereinstimmung sortieren sollten. Das kann sinnvoll sein, wenn IP-Präfixe hierarchisch sind und als Proxy dafür dienen, den Server mit der geringsten Netzwerkdistanz auszuwählen. In der Realität liegen numerisch benachbarte /24-Netze jedoch oft nicht netzwerktechnisch beieinander. Wenn die Serveradressen weit verstreut sind, kann man beobachten, dass der Traffic einiger Client-IPs zu Server-IPs tendiert, die numerisch ähnlich sind
    • Die im Artikel getesteten Clients verhielten sich korrekt und wählten einen der erreichbaren Server aus
      Natürlich wird es immer jemanden geben, der lokales DNS falsch konfiguriert oder einen schlechten Client verwendet. Für Menschen mit kaputten Setups muss man entweder Ausfälle in Kauf nehmen oder die IP einem anderen Server im selben Rechenzentrum neu zuweisen
    • Der heute übliche Ansatz ist, eine relativ niedrige TTL zu verwenden und den DNS-Server Health Checks für die Pool-Mitglieder durchführen zu lassen
  • Hallo. Ich bin der CTO von Cloudflare. Wir haben eine Änderung für alle kostenlosen Cloudflare-Konten ausgerollt, sodass sie sich wie kostenpflichtige Konten verhalten
    Das hier beschriebene Problem wurde behoben, und Zero Downtime Failover sollte nun bei allen Kontotypen funktionieren. Könnten Sie es noch einmal testen?
    Danke, dass Sie das in einem Beitrag aufbereitet haben. Ich freue mich, dass wir dieses Verhalten für alle ändern konnten

    • Ich habe es erneut getestet, und es funktioniert sehr gut
      Ich werde den Artikel entsprechend aktualisieren. Danke, dass ihr es auch für kostenlose Konten aktiviert habt; ein großartiges Ergebnis
  • Die dunkle Remix-Version davon ist Fast-Flux-Hosting, eine Methode, die viele Bulletproof-Hosting-Anbieter nutzen
    https://unit42.paloaltonetworks.com/fast-flux-101/

  • Es könnte erwähnenswert sein, dass Zero Downtime Failover eine Funktion ab Pro ist
    Soweit ich mich erinnere, war das auch früher so dokumentiert, als die Dokumentation zum Schutz von Origin-Servern noch nach Tarifstufen unterteilt war. Daher können sich Verhalten oder Retries anders darstellen