Lastverteilung für DNS-Traffic mit NGINX und NGINX Plus

NGINX | 14. April 2016

NGINX Plus R9 bietet erstmals die Möglichkeit, UDP-Datenverkehr per Reverse-Proxy weiterzuleiten und per Lastenausgleich zu verteilen – eine bedeutende Erweiterung der Layer-4-Lastenausgleichsfunktionen von NGINX Plus.

Dieser Blogbeitrag beleuchtet die Herausforderungen beim Betrieb eines DNS-Servers in einer modernen Anwendungsinfrastruktur und zeigt, wie sowohl NGINX Open Source als auch NGINX Plus den UDP- und TCP-Datenverkehr effektiv und effizient per Load Balancing verteilen können. (Aktive Zustandsprüfungen von Anwendungen sind ausschließlich in NGINX Plus verfügbar, ansonsten gelten die Informationen in diesem Blog jedoch gleichermaßen für NGINX Open Source. Der Kürze halber verwenden wir im weiteren Verlauf des Beitrags nur noch die Bezeichnung NGINX Plus.)

[Redaktion – Einen Überblick über alle neuen Funktionen in NGINX Plus R9 finden Sie unter „Ankündigung von NGINX Plus R9“ in unserem Blog.]

Warum sollte UDP-Datenverkehr lastverteilt werden?

Im Gegensatz zu TCP garantiert UDP konzeptbedingt keine Ende-zu-Ende-Übertragung von Daten. Es ist vergleichbar mit dem Versenden einer Nachricht per Brieftaube: Man weiß zwar mit Sicherheit, dass die Nachricht abgeschickt wurde, kann aber nicht sicher sein, dass sie angekommen ist. Dieser „verbindungslose“ Ansatz bietet mehrere Vorteile – insbesondere eine geringere Latenz als TCP, da die kleineren Einzelnachrichten von UDP weniger Bandbreite beanspruchen und kein Handshake zum Aufbau einer Verbindung erforderlich ist. UDP überlässt es dem Anwendungsentwickler, Zeitüberschreitungen und andere Probleme auf Netzwerkebene zu erkennen. Doch was bedeutet das für DNS?

Wie verschiedene andere UDP-basierte Protokolle verwendet DNS einen Anfrage-Antwort-Datenfluss. Beispielsweise fragt ein DNS-Client die zu einem Hostnamen gehörende IP-Adresse ab und erhält eine Antwort. Trifft innerhalb eines bestimmten Zeitlimits keine Antwort ein, sendet der DNS-Client dieselbe Anfrage an einen „Backup“-DNS-Server. Das Warten bis zum Ablauf des Zeitlimits, bevor eine Anfrage erneut gesendet wird, kann jedoch einen normalerweise äußerst schnellen Vorgang (im Millisekundenbereich) in einen sehr langsamen Vorgang (im Sekundenbereich) verwandeln.

Die Auswirkungen eines DNS-Ausfalls können durch DNS-Lastverteilung verringert werden
Nach einer Zeitüberschreitung wiederholen Clients fehlgeschlagene DNS-Abfragen über einen Backup-Server

Durch den Einsatz von NGINX Plus als Proxy und zum Lastenausgleich für DNS-Datenverkehr treten bei Clients seltener Zeitüberschreitungen auf. Wenn sich mehrere DNS-Server hinter dem NGINX Plus-Load-Balancer befinden, kommt es für Clients nur dann zu einer Zeitüberschreitung, wenn zwischen dem Client und NGINX Plus eine Netzwerkpartition besteht. Probleme mit den DNS-Servern selbst bleiben für den Client unbemerkt, wenn NGINX Plus Anwendungsintegritätsprüfungen verwendet. Durch die Überwachung der Verfügbarkeit und Antwortzeit jedes Servers verhindert NGINX Plus, dass Clientanfragen an einen fehlerhaften Server gesendet werden.

DNS ist nicht nur ein UDP-Protokoll

Obwohl der überwiegende Teil des DNS-Datenverkehrs über UDP abgewickelt wird, gibt es gängige DNS-Vorgänge, die TCP verwenden. DNS nutzt UDP für kleine Nachrichten (bis zu 512 Byte), TCP hingegen für Vorgänge, die größere Nachrichten erfordern (oder voraussichtlich erfordern werden). In der Vergangenheit wurde TCP bei DNS nur für Zonenübertragungen von einem autoritativen primären Nameserver zu seinen sekundären Nameservern verwendet. Mit der zunehmenden Verbreitung von Containern und unveränderlicher Infrastruktur wird DNS jedoch durch die Verwendung von SRV-Einträgen immer häufiger als primärer Mechanismus zur Diensterkennung eingesetzt.

DNS-SRV-Einträge wurden ursprünglich für Voice-over-IP-Telefone (VoIP) eingeführt, die SIP verwenden, um ihre Server zu finden. Sie können jedoch für jede Art von Dienst verwendet werden. SRV-Einträge enthalten allerdings wesentlich mehr Informationen als die meisten anderen DNS-Eintragstypen. Daher passen nur etwa 10 SRV-Einträge in die standardmäßige UDP-Antwort mit 512 Byte, im Gegensatz zu etwa 30 A-Einträgen. Wenn eine DNS-Antwort die Grenze von 512 Byte überschreitet, werden die ersten 512 Byte zurückgegeben, die Antwort wird jedoch als „abgeschnitten“ gekennzeichnet. Ein DNS-Client kann dann entweder versuchen, die abgeschnittene Antwort bestmöglich zu verarbeiten, oder dieselbe Anfrage erneut über TCP senden.

Das bedeutet, dass NGINX Plus beim Lastenausgleich für DNS-Server in einer modernen Netzwerkinfrastruktur mit einer Mischung aus UDP- und TCP-Datenverkehr rechnen muss.

DNS in einer Microservices-Umgebung

Die folgende Abbildung zeigt eine vereinfachte Darstellung einer Microservices-Umgebung mit zwei Load Balancern. Der Frontend-Load-Balancer leitet Anfragen von den öffentlichen Clients der Anwendung weiter, wählt dabei die am besten geeignete Microservice-Instanz aus und übernimmt viele weitere Funktionen, auf die wir hier nicht näher eingehen. Wir konzentrieren uns auf den DNS-Load-Balancer, der sich zwischen der Microservices-Umgebung und den DNS-Servern befindet, die den Microservices Informationen zur Serviceerkennung bereitstellen.

Die UDP-Lastverteilungsfunktion von NGINX und NGINX Plus macht beide zur idealen Lösung für die DNS-Lastverteilung in einer Microservices-Umgebung mit hoher Verfügbarkeit und Skalierbarkeit
NGINX Plus verteilt die Last auf DNS-Server in einer Microservices-Umgebung

Grundlegenden DNS-Lastenausgleich konfigurieren

NGINX Plus implementiert den Layer-4-Lastenausgleich im Stream-Modul. Daher wird der UDP- und TCP-Lastenausgleich im stream-Block konfiguriert, wie im folgenden Ausschnitt dargestellt.

Warnung: Sie können dieses Konfigurationsfragment nicht einfach als neue Datei im Verzeichnis /etc/nginx/conf.d hinzufügen. Dies führt zu einem Validierungsfehler („Die stream-Direktive ist hier nicht zulässig“), da die standardmäßige NGINX Plus-Konfigurationsdatei nginx.conf den Inhalt der Dateien im Verzeichnis conf.d in den http-Block einbindet. Die einfachste Lösung besteht darin, den vollständigen stream-Block direkt in die Datei nginx.conf einzufügen.

stream {    upstream dns_servers {
        server 192.168.136.130:53;
        server 192.168.136.131:53;
    }

    server {
        listen 53  udp;
        listen 53; #tcp
        proxy_pass dns_servers;
        error_log  /var/log/nginx/dns.log info;
    }
}

Zunächst definieren wir die Upstream-Gruppe der DNS-Server. Die server-Direktiven geben die Portnummer an, auf der unsere Upstream-Server lauschen: 53 (der allgemein bekannte Port für DNS).

Der server{}-Block definiert, wie NGINX Plus eingehenden DNS-Datenverkehr verarbeitet. Die beiden listen-Direktiven weisen NGINX Plus an, Port 53 sowohl für UDP- als auch für TCP-Datenverkehr zu überwachen. TCP ist das standardmäßige Layer-4-Protokoll für das Stream-Modul. Daher geben wir es im Gegensatz zu UDP nicht explizit als Parameter an.

Die proxy_pass-Direktive weist NGINX Plus an, wie der eingehende Datenverkehr verarbeitet werden soll. Hier leiten wir diesen Datenverkehr per Proxy an die Upstream-Gruppe dns_servers weiter. NGINX Plus verwendet beim Weiterleiten von UDP-Clientanfragen an Upstream-Server automatisch UDP (und TCP bei TCP-Clientanfragen), sodass das Layer-4-Protokoll in der Upstream-Gruppe nicht explizit angegeben werden muss.

Im Stream-Modul gibt es keine access_log-Direktive, da NGINX Plus die Nutzdaten von TCP-Segmenten oder UDP-Datagrammen nicht untersucht (anders als bei HTTP-Paketen). Wir können jedoch den Parameter info der Direktive error_log verwenden, um die Verarbeitung von Verbindungen und Proxy-Ereignisse zu protokollieren.

[Redaktion – Die Zugriffsprotokollierung wurde im Stream-Modul nach der Veröffentlichung dieses Blogbeitrags aktiviert, und zwar in NGINX Open Source 1.11.4 und NGINX Plus R11.]

Optimierung für Hochverfügbarkeit

Um die Verfügbarkeit unserer DNS-Server zu verbessern, können wir einige weitere Direktiven hinzufügen und aktive Zustandsprüfungen auf Anwendungsebene konfigurieren.

Anzahl der Proxy-Antworten und Zeitüberschreitungsdauer konfigurieren

Die erste zusätzliche Direktive lautet „proxy_responses“. Sie gibt an, wie viele Antworten NGINX Plus für jede über den Proxy weitergeleitete UDP-Anfrage erwartet. In unserem Fall wartet NGINX Plus nach dem Empfang einer einzigen Antwort nicht mehr auf weitere Antworten. Dadurch werden der für diese Sitzung verwendete Arbeitsspeicher und Socket freigegeben.

Die zweite zusätzliche Direktive, proxy_timeout, legt fest, wie lange NGINX Plus auf eine Antwort vom Server wartet (hier reduzieren wir den Standardwert von 10 Minuten auf 1 Sekunde). Wenn NGINX Plus innerhalb dieses Zeitraums keine Antwort erhält, versucht es den nächsten Server in der Upstream-Gruppe und markiert den nicht antwortenden Upstream-Server für einen festgelegten Zeitraum (standardmäßig 10 Sekunden) als nicht verfügbar, damit bei anderen Clients während dieser Zeit keine durch eine Zeitüberschreitung verursachte Verzögerung auftritt.

server {    listen 53  udp;
    listen 53; #tcp
    proxy_pass      dns_servers;
    error_log       /var/log/nginx/dns.log info;
    proxy_responses 1;
    proxy_timeout 1s;
}

Wir können auch ändern, wie lange ein Server als nicht verfügbar markiert wird, indem wir die Option fail_timeout in die Direktive server der Upstream-Gruppe aufnehmen. Mit der folgenden Einstellung markiert NGINX Plus ausgefallene Upstream-Server 60 Sekunden lang als nicht verfügbar:

upstream dns_servers {    server 192.168.136.130:53 fail_timeout=60s;
    server 192.168.136.131:53 fail_timeout=60s;
}

Auf diese Weise können wir steuern, wie lange sich die Antwort für einen Client verzögert, wenn einer unserer DNS-Server ausfällt. Wird jedoch eine TCP-Anfrage an einen ausgefallenen DNS-Server gesendet, kann NGINX Plus diesen dank der in TCP integrierten Fehlerprüfung automatisch als nicht verfügbar markieren, sodass nachfolgende TCP- oder UDP-Anfragen nicht mehr an diesen Server gesendet werden.

Aktive Integritätsprüfungen konfigurieren

Die Funktion für aktive Zustandsprüfungen in NGINX Plus ist ein zusätzliches und äußerst wertvolles Werkzeug, um die Hochverfügbarkeit jedes Dienstes mit Lastverteilung sicherzustellen, einschließlich DNS. Anstatt darauf zu warten, dass eine tatsächliche TCP-Anfrage eines DNS-Clients fehlschlägt, bevor der DNS-Server als ausgefallen markiert wird, lassen wir NGINX Plus regelmäßig versuchen, eine TCP-Verbindung über Port 53 herzustellen. So wird geprüft, ob der DNS-Server erreichbar ist und ordnungsgemäß funktioniert. Dazu fügen wir die Direktive health_check mit ihrem Parameter port=53 in den Block server{} ein. (Standardmäßig sendet NGINX Plus Zustandsprüfungen an den Port, der durch die Direktive listen angegeben ist, in unserem Fall Port 53. Hier verwenden wir den Parameter also, um den Standardwert explizit festzulegen. Wir könnten jedoch auch einen anderen Port angeben, wenn wir unsere DNS-Server so konfigurieren würden, dass sie auch auf Datenverkehr an diesem Port reagieren.)

Mit UDP können wir noch einen Schritt weiter gehen und eine aktive Zustandsprüfung konfigurieren, die eine echte DNS-Abfrage nach einem bekannten Eintrag durchführt. Beispielsweise könnten wir den folgenden CNAME-Eintrag in der Zonendatei für dieselbe Subdomain anlegen, die auch für die Diensterkennung innerhalb der Microservices-Umgebung verwendet wird.

healthcheck    IN CNAME    healthy.svcs.example.com.

Da UDP ein schlankes Protokoll ist, können wir den Netzwerkverkehr überwachen und die Bytefolge, die eine DNS-Abfrage darstellt, problemlos extrahieren. Anschließend erstellen wir einen match-Konfigurationsblock und übergeben diese Bytefolge als Parameter an die send-Direktive. Die expect-Direktive gibt die Antwort an, die der Server zurückgeben muss, damit er als fehlerfrei gilt.

match dns_lookup {
    send x00x01x00x00x00x01x00x00x00x00x00x00x06x68x65x61 ...;
    expect ~* "healthy.svcs.example.com.";
}

Der Vorteil dieser umfassenden Zustandsprüfung auf Anwendungsebene besteht darin, dass eine echte DNS-Abfrage für Ihre Produktionsdomain Konfigurationsprobleme und beschädigte Daten aufdeckt, die andernfalls in nachgelagerten Systemen zu Problemen führen könnten – selbst wenn Ihr Nameserver ordnungsgemäß läuft.

Das NGINX Plus-Supportteam kann Sie bei der Einrichtung von UDP-Zustandsprüfungen für DNS-Abfragen und andere Protokolle unterstützen.

Der folgende Ausschnitt hebt die zusätzlichen Direktiven hervor, die für aktive Integritätsprüfungen erforderlich sind.

stream {    upstream dns_servers {
        zone dns_mem 64k;
        server 192.168.136.130:53 fail_timeout=60s;
        server 192.168.136.131:53 fail_timeout=60s;
    }

    match dns_lookup { send x00x01x00x00x00x01x00x00x00x00x00x00x06x68x65x61 ...; expect ~* "healthy.svcs.example.com."; }

    server {
        listen 53  udp;
        listen 53; #tcp
        health_check match=dns_lookup interval=20 fails=2 passes=2 udp;
        health_check interval=20 fails=1 passes=2 port=53; #tcp
        proxy_pass      dns_servers;
        error_log       /var/log/nginx/dns.log debug;
        proxy_responses 1;
        proxy_timeout   1s;

    }
}

Die zone-Direktive definiert eine Shared-Memory-Zone namens dns_mem, die die Ergebnisse der Systemdiagnosen (und andere Statusinformationen) allen NGINX Plus-Worker-Prozessen zur Verfügung stellt.

Die match-Direktive wird direkt darüber erläutert.

Die health_check-Direktive verfügt über eine Reihe von Parametern, die Sie an Ihre Umgebung anpassen können. Hier definieren wir jeweils separate Integritätsprüfungen für UDP und TCP. Aufgrund der Unterschiede zwischen UDP und TCP müssen zwei aufeinanderfolgende UDP-Integritätsprüfungen fehlschlagen, bevor der DNS-Server als fehlerhaft eingestuft wird, bei TCP genügt hingegen ein einziger Fehlschlag. Bei beiden Protokollen sind zwei erfolgreiche Antworten erforderlich, bevor ein Server wieder als fehlerfrei eingestuft wird. So wird vermieden, dass Anfragen an einen instabilen Server gesendet werden, dessen Status ständig wechselt.

Ein Vorteil der Definition einer einzigen Upstream-Gruppe von DNS-Servern für UDP- und TCP-Datenverkehr besteht darin, dass der Server bei einer fehlgeschlagenen Integritätsprüfung für eines der beiden Protokolle als fehlerhaft markiert und aus dem Pool für den Lastenausgleich entfernt wird.

Optimierung für Skalierbarkeit

Obwohl bereits der Einsatz von nur zwei Backend-Servern eine effektive Hochverfügbarkeitslösung darstellen kann, ermöglichen Ihnen die Lastverteilungsfunktionen von NGINX Plus, Backend-Server horizontal zu skalieren, ohne dass der Client davon Kenntnis hat.

Die oben beschriebene Beispielumgebung mit Microservices erfordert wahrscheinlich keine Skalierung der Backend-DNS-Server. Ein Internetdienstanbieter, der all seinen Kunden DNS-Dienste bereitstellt, ist jedoch einer konstanten Last und potenziell enormen Lastspitzen ausgesetzt. Daher benötigt er eine große Anzahl von DNS-Servern sowie einen Frontend-Proxy, der den Datenverkehr per Lastverteilung auf diese Server verteilt.

Alle Lastverteilungsalgorithmen von NGINX und NGINX Plus sind sowohl für TCP und UDP als auch für HTTP verfügbar:

  • Round Robin (Standardeinstellung)
  • Generischer Hash und seine konsistente Variante (Ketama-Algorithmus)
  • IP-Hash
  • Wenigste Verbindungen
  • Kürzeste Zeit (nur NGINX Plus)

(Sie können außerdem für alle Algorithmen Gewichtungen konfigurieren, um deren Effizienz noch weiter zu steigern. Weitere Informationen finden Sie im Abschnitt über Gewichtungen im Beitrag „Auswahl einer Lastverteilungsmethode für NGINX Plus“ in unserem Blog.)

Während HTTP-Anfragen hinsichtlich der Last und der Verarbeitungsanforderungen, die sie an die Backend-Server stellen, stark variieren können, erzeugen DNS-Anfragen in der Regel alle die gleiche Last. Aus diesem Grund bieten die Algorithmen Least Connections und Least Time gegenüber Round Robin wahrscheinlich keinen Vorteil. Insbesondere berücksichtigt Least Connections bei der Anzahl der Verbindungen alle UDP-Anfragen, für die NGINX Plus noch auf eine Antwort vom Upstream-Server wartet. Solange die Werte für proxy_responses und proxy_timeout noch nicht erreicht wurden, zählt NGINX Plus weiterhin Verbindungen für Upstream-Server, die ihre Arbeit möglicherweise bereits abgeschlossen haben.

Wenn Sie eine große Anzahl von Clients und ein Protokoll haben, bei dem viel „Dialog“ stattfindet – also mehrere Nachrichten zwischen Client und Server ausgetauscht werden, wie beim Challenge-Response-Ablauf von RADIUS –, ermöglicht die Verwendung eines Quell-IP-Hashs, dass dieser Dialog mit einem einzigen Backend-Server stattfindet. Mit anderen Worten: Dadurch wird Sitzungspersistenz hergestellt, sodass NGINX Plus alle Anfragen eines bestimmten Clients an denselben Server weiterleitet. Das folgende Beispiel konfiguriert den Hash-Load-Balancing-Algorithmus für zwei RADIUS-Authentifizierungsserver, wobei die Quell-IP-Adresse (Client-IP-Adresse), die von der Variable $remote_addr erfasst wird, als Schlüssel dient.

upstream radius_servers {    hash $remote_addr; # Source-IP hash
    server 192.168.136.201:1812;
    server 192.168.136.202:1812;
}

Weitere Informationen

Weitere Informationen zum UDP- und TCP-Load-Balancing finden Sie in den folgenden Ressourcen:

Weitere Informationen zu den anderen großartigen Funktionen in NGINX Plus R9 finden Sie in unserem Blogbeitrag „Ankündigung von NGINX Plus R9“. Sehen Sie sich außerdem unser On-Demand-Webinar „Neuerungen in NGINX Plus R9“ an.

Wenn Sie NGINX Plus ausprobieren möchten, starten Sie noch heute Ihre kostenlose 30-Tage-Testversion oder kontaktieren Sie uns, um Ihre Anwendungsfälle zu besprechen.


Teilen
Schlagwörter: F5 NGINX, Tech, 2016

Über den Autor

Liam Crilly
Liam CrillySenior Director, Produktmanagement

Weitere Blogbeiträge von Liam Crilly

Ähnliche Blogbeiträge

Die Zukunft von NGINX: Zurück zu unseren Open-Source-Wurzeln
NGINX | 23.08.2022

Die Zukunft von NGINX: Zurück zu unseren Open-Source-Wurzeln

Beim NGINX Sprint 2022 besinnen wir uns auf unsere Open-Source-Wurzeln. Erfahren Sie mehr über die Initiativen, die wir im kommenden Jahr verfolgen, und teilen Sie uns mit, was Sie sich wünschen.

Ein Leitfaden zur Auswahl eines Ingress-Controllers, Teil 1: Ermitteln Sie Ihre Anforderungen
NGINX | 08.09.2021

Ein Leitfaden zur Auswahl eines Ingress-Controllers, Teil 1: Ermitteln Sie Ihre Anforderungen

Ein Ingress-Controller kann eines der leistungsstärksten Tools in Ihrem Kubernetes-Stack sein. Erfahren Sie, wie Sie Ihre Ingress-Anforderungen ermitteln, um die beste Option auszuwählen.

Bereitstellung des NGINX Ingress Controllers auf Amazon EKS: So haben wir getestet
NGINX | 16.08.2021

Bereitstellung des NGINX Ingress Controllers auf Amazon EKS: So haben wir getestet

Wir haben kürzlich den Lösungsleitfaden zum NGINX Ingress Controller um Empfehlungen zur Dimensionierung für Amazon Elastic Kubernetes Service (EKS) ergänzt. In diesem Blogbeitrag erläutern wir, wie wir diese Empfehlungen erarbeitet haben, und stellen Ihnen alle Informationen zur Verfügung, die Sie benötigen, um selbst ähnliche Tests durchzuführen.

Protokollierung des Datenverkehrs zwischen NGINX und Upstream-Servern bei CDN77
NGINX | 12.12.2018

Protokollierung des Datenverkehrs zwischen NGINX und Upstream-Servern bei CDN77

Bei der Fehlersuche bei Datenverkehrsproblemen zwischen NGINX und Upstream-Servern konnte CDN77 die benötigten Daten nur aus dem Fehlerprotokoll mit aktivierter Debug-Ausgabe beziehen, was die Leistung beeinträchtigt und riesige Dateien erzeugt. Daher entwickelte das Team ein neues benutzerdefiniertes Modul, das ausschließlich die benötigten Daten protokolliert.

Ankündigung von NGINX Plus R16
NGINX | 05.09.2018

Ankündigung von NGINX Plus R16

NGINX Plus R16 ist eine einheitliche, elastische Ingress- und Egress-Schicht für Anwendungen, die die Funktionen eines Load Balancers, API-Gateways und einer WAF vereint und neue Features wie clusterfähige Ratenbegrenzung, Schlüssel-Wert-Speicher, „Random with Two Choices“, verbessertes UDP-Load-Balancing und vieles mehr bietet.

5 Gründe für den Umstieg auf softwarebasiertes Load Balancing
NGINX | 13.10.2016

5 Gründe für den Umstieg auf softwarebasiertes Load Balancing

Entdecken Sie fünf Gründe für den Wechsel zu einem softwarebasierten Load Balancer wie NGINX Plus, darunter Kosteneinsparungen, die Einführung von DevOps und eine einheitliche Lösung für alle Umgebungen.