Erschöpfung ephemerer Ports in NGINX und NGINX Plus überwinden

NGINX | 19. April 2016

NGINX und NGINX Plus sind äußerst leistungsstarke HTTP, TCP- und UDP-Load-Balancer. Sie können große Anfragespitzen sehr effizient weiterleiten und eine große Anzahl gleichzeitiger Verbindungen aufrechterhalten. Aufgrund dieser Eigenschaften sind NGINX und NGINX Plus jedoch besonders anfällig für die Erschöpfung ephemerer Ports – einen Zustand, in dem keine neuen Verbindungen hergestellt werden können, weil dem Betriebssystem die für neue lokale Sockets vorgesehenen Portnummern ausgegangen sind. (Die Erschöpfung ephemerer Ports betrifft beide Produkte, der Kürze halber beziehen wir uns im weiteren Verlauf dieses Blogbeitrags jedoch nur noch auf NGINX Plus.)

In diesem Blogbeitrag betrachten wir die Komponenten einer TCP-Verbindung und wie deren Inhalte vor dem Verbindungsaufbau festgelegt werden. Anschließend zeigen wir, wie Sie feststellen können, ob NGINX Plus von einer Erschöpfung der ephemeren Ports betroffen ist. Abschließend erläutern wir Strategien, mit denen sich diese Einschränkungen sowohl durch Anpassungen am Linux-Kernel als auch durch NGINX Plus-Direktiven überwinden lassen.

Ein kurzer Überblick über Netzwerk-Sockets

Wenn eine Verbindung über TCP hergestellt wird, wird sowohl auf dem lokalen als auch auf dem entfernten Host ein Socket erstellt. Diese Sockets werden anschließend zu einem Socket-Paar verbunden, das durch ein eindeutiges 4‑Tupel aus der lokalen IP-Adresse und dem lokalen Port sowie der entfernten IP-Adresse und dem entfernten Port beschrieben wird.

Die Remote-IP-Adresse und der Remote-Port gehören zur Serverseite der Verbindung und müssen vom Client ermittelt werden, bevor dieser die Verbindung überhaupt initiieren kann. In den meisten Fällen wählt der Client automatisch aus, welche lokale IP-Adresse für die Verbindung verwendet wird. Manchmal wird sie jedoch von der Software festgelegt, die die Verbindung herstellt. Schließlich wird der lokale Port nach dem Zufallsprinzip aus einem vom Betriebssystem bereitgestellten Bereich ausgewählt. Der Port ist dem Client nur für die Dauer der Verbindung zugeordnet und wird daher als temporär bezeichnet. Nach dem Beenden der Verbindung kann der temporäre Port wiederverwendet werden.

Erschöpfung ephemerer Ports erkennen

Wie in der Einleitung erwähnt, ist NGINX Plus naturgemäß von der Erschöpfung ephemerer Ports und den daraus resultierenden Problemen betroffen. Wenn NGINX Plus eine Anfrage an einen Upstream-Server weiterleitet, fungiert es bei dem oben beschriebenen Prozess der Socket-Erstellung als Client. Standardmäßig bindet es den Socket für die weitergeleitete Anfrage automatisch an eine lokale IP-Adresse und einen auf dem ausführenden Host verfügbaren ephemeren Port. Ist die Verbindungsrate so hoch, dass neu eingerichtete Sockets schneller in einen Wartezustand versetzt werden, als bestehende offene Sockets geschlossen werden, sind die verfügbaren Ports schließlich erschöpft und es können keine neuen Sockets mehr erstellt werden. Dies führt sowohl beim Betriebssystem als auch bei NGINX Plus zu Fehlern. Hier sehen Sie ein Beispiel für den daraus resultierenden Fehler im Fehlerprotokoll von NGINX Plus.

2016/03/18 09:08:37 [crit] 1888#1888: *13 connect() to 10.2.2.77:8081 failed (99: Cannot assign requested address) while connecting to upstream, client: 10.2.2.42, server: , request: "GET / HTTP/1.1", upstream: "http://10.2.2.77:8081/", host: "10.2.2.77"

Die Erschöpfung des Portbereichs führt außerdem zu einem Anstieg der von NGINX Plus und nicht vom Upstream-Server verursachten 500-Fehler. Hier sehen Sie einen Beispieleintrag aus dem Zugriffsprotokoll von NGINX Plus.

10.2.2.42 - - [18/Mar/2016:09:14:20 -0700] "GET / HTTP/1.1" 500 192 "-" "curl/7.35.0"

Um die Anzahl der Sockets im Status „TIME-WAIT“ auf Ihrem NGINX Plus-Server zu überprüfen, führen Sie den folgenden ss-Befehl in der Linux-Shell aus. Das Beispiel zeigt, dass 143 Sockets mit dem Status „TIME-WAIT“ geöffnet sind. Im Beispiel ermitteln wir die Anzahl mithilfe des Befehls wc, der die Anzahl der Zeilen in der Befehlsausgabe ausgibt.

# ss -a | grep TIME-WAIT | wc -l143

Kernel-Optimierung zur Erhöhung der Anzahl verfügbarer kurzlebiger Ports

Eine Möglichkeit, die Erschöpfung kurzlebiger Ports zu reduzieren, bietet die Linux-Kernel-Einstellung net.ipv4.ip_local_port_range. Der Standardbereich reicht in der Regel von 32768 bis 61000.

Wenn Sie feststellen, dass Ihnen die kurzlebigen Ports ausgehen, können Sie den Bereich vom Standardwert auf 1024 bis 65000 ändern und so die Anzahl der verfügbaren kurzlebigen Ports auf praktische Weise verdoppeln. Weitere Informationen zum Ändern von Kernel-Einstellungen finden Sie in unserem Blogbeitrag „NGINX für optimale Leistung konfigurieren“.

Keepalive-Verbindungen in NGINX Plus aktivieren

Eine weitere Möglichkeit, die Erschöpfung kurzlebiger Ports zu vermeiden, besteht darin, Keepalive-Verbindungen zwischen NGINX Plus und Upstream-Servern zu aktivieren. Bei der einfachsten Implementierung von HTTP öffnet ein Client eine neue Verbindung, sendet die Anfrage, liest die Antwort und schließt anschließend die Verbindung, um die zugehörigen Ressourcen freizugeben.

Eine Keepalive-Verbindung bleibt geöffnet, nachdem der Client die Antwort gelesen hat, sodass sie für nachfolgende Anfragen wiederverwendet werden kann.

Verwenden Sie die Direktive „keepalive“, um Keepalive-Verbindungen von NGINX Plus zu Upstream-Servern zu aktivieren und die maximale Anzahl inaktiver Keepalive-Verbindungen zu Upstream-Servern festzulegen, die im Cache jedes Worker-Prozesses vorgehalten werden. Wird diese Anzahl überschritten, werden die am längsten nicht verwendeten Verbindungen geschlossen. Ohne Keepalive-Verbindungen entsteht zusätzlicher Overhead, und sowohl Verbindungen als auch kurzlebige Ports werden ineffizient genutzt.

Die folgende Beispielkonfiguration weist NGINX Plus an, mindestens 128 Keepalive-Verbindungen zu den Servern aufrechtzuerhalten, die im upstream-Block namens backend definiert sind.

upstream backend {    server 10.0.0.100:1234;
    server 10.0.0.101:1234;
    keepalive 128;
}

Wenn Sie Keepalive-Verbindungen zu Ihren Upstream-Servern aktivieren, müssen Sie außerdem mit der Direktive proxy_http_version festlegen, dass NGINX Plus HTTP-Version 1.1 verwendet, und mit der Direktive proxy_set_header alle Header namens Connection entfernen. Beide Direktiven können in den Konfigurationsblöcken http, server oder location platziert werden.

proxy_http_version 1.1; proxy_set_header Connection "";

Dynamisches Binden von Verbindungen an eine definierte Liste lokaler IP-Adressen

Durch die Optimierung des Kernels und die Aktivierung von Keepalive-Verbindungen lässt sich die Verfügbarkeit und Nutzung kurzlebiger Ports wesentlich besser steuern. In bestimmten Situationen reichen diese Änderungen jedoch nicht aus, um einer übermäßigen Nutzung kurzlebiger Ports entgegenzuwirken. In diesem Fall können wir einige Direktiven und Parameter von NGINX Plus verwenden, mit denen sich ein bestimmter Prozentsatz unserer Verbindungen an statische lokale IP-Adressen binden lässt. Dadurch wird die Anzahl der verfügbaren kurzlebigen Ports effektiv mit der Anzahl der in der Konfiguration definierten IP-Adressen multipliziert. Hierzu verwenden wir die Direktiven proxy_bind und split_clients.

In der folgenden Beispielkonfiguration legt die proxy_bind-Direktive im location-Block bei jeder Anfrage die lokale IP-Adresse entsprechend dem Wert der Variablen $split_ip fest. Diese Variable wird dynamisch auf den vom split_clients-Block erzeugten Wert gesetzt, der diesen mithilfe einer Hashfunktion ermittelt.

Der erste Parameter der split_clients-Direktive ist eine Zeichenfolge ("$remote_addr$remote_port"). Diese wird bei jeder Anfrage mithilfe einer MurmurHash2-Funktion gehasht. Der zweite Parameter ($split_ip) ist die Variable, die wir basierend auf dem Hashwert des ersten Parameters dynamisch festlegen.

Die Anweisungen innerhalb der geschweiften Klammern unterteilen die Hash-Tabelle in „Buckets“, die jeweils einen bestimmten Prozentsatz der Hashes enthalten. Hier unterteilen wir die Tabelle in 10 gleich große Buckets. Es können jedoch beliebig viele Buckets erstellt werden, die nicht alle gleich groß sein müssen. (Der Prozentsatz für den letzten Bucket wird immer durch ein Sternchen [*] statt durch eine konkrete Zahl dargestellt, da sich die Anzahl der Hashes möglicherweise nicht gleichmäßig auf die angegebenen Prozentsätze verteilen lässt.)

Der Bereich der möglichen Hashwerte reicht von 0 bis 4294967295, sodass in unserem Fall jeder Bucket etwa 429496700 Werte enthält (10 % der Gesamtmenge): Der erste Bucket enthält die Werte von 0 bis 429496700, der zweite Bucket die Werte von 429496701 bis 858993400 und so weiter. Die Variable $split_ip wird auf die IP-Adresse gesetzt, die dem Bucket zugeordnet ist, der den Hashwert der Zeichenfolge $remote_addr$remote_port enthält. Ein konkretes Beispiel: Der Hashwert 150000000 fällt in den vierten Bucket, sodass die Variable $split_ip in diesem Fall dynamisch auf 10.0.0.213 gesetzt wird.

http {    upstream backend {
        server 10.0.0.100:1234;
        server 10.0.0.101:1234;
 }

    server {
        # ...
        location / {
            # ...
            proxy_pass http://backend;
            proxy_bind $split_ip;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
    }

    split_clients "$remote_addr$remote_port" $split_ip { 10% 10.0.0.210; 10% 10.0.0.211; 10% 10.0.0.212; 10% 10.0.0.213; 10% 10.0.0.214; 10% 10.0.0.215; 10% 10.0.0.216; 10% 10.0.0.217; 10% 10.0.0.218; * 10.0.0.219; }
}

Fazit

Durch die Anpassung der Linux-Kernel-Einstellung für die Anzahl kurzlebiger Ports lassen sich deren Verfügbarkeit und Nutzung effizienter gestalten. Wenn Sie Keepalive-Verbindungen von NGINX Plus zu Ihren Upstream-Servern aktivieren, werden kurzlebige Ports effizienter genutzt. Schließlich können Sie mithilfe der Direktiven split_clients und proxy_bind in Ihrer NGINX Plus-Konfiguration ausgehende Verbindungen dynamisch an eine festgelegte Liste lokaler IP-Adressen binden. Dadurch erhöht sich die Anzahl der für NGINX Plus verfügbaren kurzlebigen Ports erheblich.

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

Über den Autor

Kevin Jones
Kevin JonesSenior-Produktmanager | F5 NGINX

Weitere Blogbeiträge von Kevin Jones

Ä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.