Die 10 häufigsten Fehler bei der NGINX-Konfiguration vermeiden

NGINX | 22. Februar 2022

Wenn wir NGINX-Benutzern bei Problemen helfen, stoßen wir häufig auf dieselben Konfigurationsfehler, die uns bereits immer wieder in den Konfigurationen anderer Benutzer begegnet sind – manchmal sogar in Konfigurationen, die von anderen NGINX-Ingenieuren erstellt wurden! In diesem Blogbeitrag betrachten wir 10 der häufigsten Fehler und erklären, was daran falsch ist und wie sie sich beheben lassen.

  1. Nicht genügend Dateideskriptoren pro Worker
  2. Die error_log-Direktive off directive
  3. Keepalive-Verbindungen zu Upstream-Servern nicht aktivieren
  4. Vergessen, wie die Vererbung von Direktiven funktioniert
  5. Die proxy_buffering-Direktive off directive
  6. Unsachgemäße Verwendung der if-Direktive
  7. Übermäßig viele Integritätsprüfungen
  8. Ungesicherter Zugriff auf Metriken
  9. Verwendung von ip_hash, wenn der gesamte Datenverkehr aus demselben /24-CIDR-Block stammt
  10. Upstream-Gruppen nicht nutzen

Fehler 1: Nicht genügend Dateideskriptoren pro Worker

Die worker_connections-Direktive legt die maximale Anzahl gleichzeitiger Verbindungen fest, die ein NGINX-Worker-Prozess offen halten kann (der Standardwert ist 512). Alle Arten von Verbindungen (zum Beispiel Verbindungen zu Proxy-Servern) werden auf diesen Höchstwert angerechnet, nicht nur Clientverbindungen. Dabei ist jedoch zu beachten, dass es letztlich noch eine weitere Begrenzung für die Anzahl gleichzeitiger Verbindungen pro Worker gibt: die vom Betriebssystem festgelegte maximale Anzahl an Dateideskriptoren (FDs), die jedem Prozess zugewiesen werden. In modernen UNIX-Distributionen liegt der Standardwert bei 1024.

Außer für die kleinsten NGINX-Bereitstellungen ist ein Limit von 512 Verbindungen pro Worker wahrscheinlich zu niedrig. Tatsächlich erhöht die standardmäßige Datei nginx.conf dieses Limit auf 1024. Diese Datei stellen wir mit den Binärdateien von NGINX Open Source und NGINX Plus bereit.

Ein häufiger Konfigurationsfehler besteht darin, den Grenzwert für Dateideskriptoren nicht auf mindestens das Doppelte des Werts von worker_connections zu erhöhen. Zur Behebung muss dieser Wert mit der Direktive worker_rlimit_nofile im Hauptkontext der Konfiguration festgelegt werden.

Aus folgendem Grund werden mehr FDs benötigt: Jede Verbindung von einem NGINX-Worker-Prozess zu einem Client oder Upstream-Server belegt einen FD. Wenn NGINX als Webserver fungiert, verwendet es einen FD für die Clientverbindung und einen FD pro bereitgestellter Datei, also mindestens zwei FDs pro Client (die meisten Webseiten bestehen jedoch aus vielen Dateien). Als Proxyserver verwendet NGINX jeweils einen FD für die Verbindung zum Client und zum Upstream-Server sowie möglicherweise einen dritten FD für die Datei, in der die Antwort des Servers vorübergehend gespeichert wird. Als Caching-Server verhält sich NGINX bei zwischengespeicherten Antworten wie ein Webserver und bei leerem oder abgelaufenem Cache wie ein Proxyserver.

NGINX verwendet außerdem einen FD pro Protokolldatei sowie einige FDs für die Kommunikation mit dem Masterprozess. Im Vergleich zur Anzahl der für Verbindungen und Dateien verwendeten FDs fallen diese Zahlen jedoch in der Regel kaum ins Gewicht.

UNIX bietet mehrere Möglichkeiten, die Anzahl der FDs pro Prozess festzulegen:

  • Der Befehl „ulimit“, wenn Sie NGINX über eine Shell starten
  • Die Variablen im init-Skript oder im systemd-Dienstmanifest, wenn Sie NGINX als Dienst starten
  • Die Datei /etc/security/limits.conf file

Welche Methode Sie verwenden sollten, hängt jedoch davon ab, wie Sie NGINX starten, während worker_rlimit_nofile unabhängig davon funktioniert, wie Sie NGINX starten.

Es gibt außerdem eine systemweite Begrenzung für die Anzahl der Dateideskriptoren (FDs), die Sie mit dem Betriebssystembefehl sysctl fs.file-max festlegen können. Sie ist normalerweise hoch genug. Dennoch sollten Sie überprüfen, ob die maximale Anzahl an Dateideskriptoren, die alle NGINX-Worker-Prozesse zusammen verwenden könnten (worker_rlimit_nofile * worker_processes), deutlich unter fs.file‑max liegt. Wenn NGINX aus irgendeinem Grund alle verfügbaren Dateideskriptoren belegt, beispielsweise während eines DoS-Angriffs, ist es nicht einmal mehr möglich, sich am Rechner anzumelden, um das Problem zu beheben.

Fehler 2: Die error_log-Direktive off Directive

Ein häufiger Irrtum ist die Annahme, dass die Direktive error_log off die Protokollierung deaktiviert. Im Gegensatz zur Direktive access_log akzeptiert error_log jedoch keinen Parameter off. Wenn Sie die Direktive error_log off in die Konfiguration aufnehmen, erstellt NGINX im Standardverzeichnis für NGINX-Konfigurationsdateien (normalerweise /etc/nginx) eine Fehlerprotokolldatei namens off.

Wir raten davon ab, das Fehlerprotokoll zu deaktivieren, da es bei der Behebung von Problemen mit NGINX eine unverzichtbare Informationsquelle darstellt. Wenn der Speicherplatz jedoch so begrenzt ist, dass die protokollierten Daten möglicherweise den gesamten verfügbaren Speicherplatz belegen könnten, kann es sinnvoll sein, die Fehlerprotokollierung zu deaktivieren. Fügen Sie dazu im Hauptkonfigurationskontext die folgende Direktive ein:

error_log /dev/null emerg;

Beachten Sie, dass diese Direktive erst wirksam wird, nachdem NGINX die Konfiguration gelesen und validiert hat. Daher protokolliert NGINX bei jedem Start oder Neuladen der Konfiguration möglicherweise so lange am Standardspeicherort für Fehlerprotokolle (normalerweise /var/log/nginx/error.log), bis die Konfiguration validiert wurde. Um das Protokollverzeichnis zu ändern, geben Sie den Parameter -e <error_log_location> im Befehl nginx an.

Fehler 3: Keepalive-Verbindungen zu Upstream-Servern nicht aktivieren

Standardmäßig öffnet NGINX für jede neue eingehende Anfrage eine neue Verbindung zu einem Upstream-Server (Backend-Server). Dies ist zwar sicher, aber ineffizient, da NGINX und der Server drei Pakete austauschen müssen, um eine Verbindung herzustellen, und drei oder vier, um sie zu beenden.

Bei hohem Datenverkehr kann das Öffnen einer neuen Verbindung für jede Anfrage die Systemressourcen erschöpfen und dazu führen, dass überhaupt keine Verbindungen mehr geöffnet werden können. Der Grund dafür ist, dass für jede Verbindung das 4-Tupel aus Quelladresse, Quellport, Zieladresse und Zielport eindeutig sein muss. Bei Verbindungen von NGINX zu einem Upstream-Server sind drei dieser Elemente (das erste, dritte und vierte) fest vorgegeben, sodass nur der Quellport variabel ist. Wenn eine Verbindung geschlossen wird, verbleibt der Linux-Socket zwei Minuten lang im Zustand TIME‑WAIT. Bei hohem Datenverkehr steigt dadurch die Wahrscheinlichkeit, dass der Pool verfügbarer Quellports erschöpft wird. In diesem Fall kann NGINX keine neuen Verbindungen zu Upstream-Servern öffnen.

Die Lösung besteht darin, Keepalive-Verbindungen zwischen NGINX und den Upstream-Servern zu aktivieren. Statt nach Abschluss einer Anfrage geschlossen zu werden, bleibt die Verbindung für weitere Anfragen geöffnet. Dadurch sinkt nicht nur das Risiko, dass die Quellports ausgehen, sondern es verbessert sich auch die Leistung.

So aktivieren Sie Keepalive-Verbindungen:

  • Fügen Sie die Direktive keepalive in jeden upstream{}-Block ein, um die Anzahl der inaktiven Keepalive-Verbindungen zu Upstream-Servern festzulegen, die im Cache jedes Worker-Prozesses vorgehalten werden. Beachten Sie, dass die Direktive keepalive nicht die Gesamtzahl der Verbindungen zu Upstream-Servern begrenzt, die ein NGINX-Worker-Prozess öffnen kann – dies ist ein weitverbreitetes Missverständnis. Daher muss der Parameter für keepalive nicht so groß sein, wie Sie vielleicht denken. Wir empfehlen, den Parameter auf das Doppelte der Anzahl der im upstream{}-Block aufgeführten Server festzulegen. Dieser Wert ist groß genug, damit NGINX Keepalive-Verbindungen zu allen Servern aufrechterhalten kann, aber klein genug, damit die Upstream-Server auch neue eingehende Verbindungen verarbeiten können. Beachten Sie außerdem: Wenn Sie im upstream{}-Block einen Lastverteilungsalgorithmus angeben – mit der Direktive hash, ip_hash, least_conn, least_time oder random –, muss diese Direktive vor der Direktive keepalive stehen. Dies ist eine der seltenen Ausnahmen von der allgemeinen Regel, dass die Reihenfolge der Direktiven in der NGINX-Konfiguration keine Rolle spielt.
  • Fügen Sie in dem location{}-Block, der Anfragen an eine Upstream-Gruppe weiterleitet, zusätzlich zur Direktive proxy_pass die folgenden Direktiven ein:
proxy_http_version 1.1;
proxy_set_header   "Connection" "";
  • Standardmäßig verwendet NGINX HTTP/1.0 für Verbindungen zu Upstream-Servern und fügt daher den an die Server weitergeleiteten Anfragen den Header Connection: close hinzu. Dies führt dazu, dass jede Verbindung nach Abschluss der Anfrage geschlossen wird, obwohl die Direktive keepalive im Block upstream{} vorhanden ist. Die Direktive proxy_http_version weist NGINX an, stattdessen HTTP/1.1 zu verwenden, und die Direktive proxy_set_header entfernt den Wert close aus dem Header Connection.

Fehler 4: Vergessen, wie die Vererbung von Direktiven funktioniert

NGINX-Direktiven werden nach unten beziehungsweise von außen nach innen vererbt: Ein untergeordneter Kontext – also ein Kontext, der in einen anderen Kontext (seinen übergeordneten Kontext) eingebettet ist – übernimmt die Einstellungen der auf der übergeordneten Ebene enthaltenen Direktiven. Beispielsweise übernehmen alle server{}- und location{}-Blöcke im http{}-Kontext die Werte der auf der http-Ebene enthaltenen Direktiven. Eine Direktive in einem server{}-Block wird wiederum von allen darin enthaltenen untergeordneten location{}-Blöcken übernommen. Wenn dieselbe Direktive jedoch sowohl in einem übergeordneten als auch in dessen untergeordnetem Kontext enthalten ist, werden die Werte nicht zusammengeführt. Stattdessen überschreibt der Wert im untergeordneten Kontext den Wert des übergeordneten Kontexts.

Der Fehler besteht darin, diese „Überschreibungsregel“ für Array-Direktiven zu vergessen, die nicht nur in mehreren Kontexten, sondern auch mehrfach innerhalb eines bestimmten Kontexts angegeben werden können. Beispiele hierfür sind proxy_set_header und add_header – das „add“ im Namen der zweiten Direktive macht es besonders leicht, die Überschreibungsregel zu vergessen.

Wie die Vererbung funktioniert, lässt sich anhand dieses Beispiels für add_header veranschaulichen:

http {    
    add_header X-HTTP-LEVEL-HEADER 1;
    add_header X-ANOTHER-HTTP-LEVEL-HEADER 1;

    server {
        listen 8080;
        location / {
            return 200 "OK";
        } 
    }

    server {
        listen 8081;
        add_header X-SERVER-LEVEL-HEADER 1;

        location / {
            return 200 "OK";
        }

        location /test {
            add_header X-LOCATION-LEVEL-HEADER 1;
            return 200 "OK";
        }

        location /correct {
            add_header X-HTTP-LEVEL-HEADER 1;
            add_header X-ANOTHER-HTTP-LEVEL-HEADER 1;

            add_header X-SERVER-LEVEL-HEADER 1;
            add_header X-LOCATION-LEVEL-HEADER 1;
            return 200 "OK";
        } 
    }
}

Für den Server, der Port 8080 überwacht, gibt es weder im add_header-Direktiven im server{}- noch im location{}-Block. Daher ist die Vererbung unkompliziert, und wir sehen die beiden im http{}-Kontext definierten Header:

% curl -i localhost:8080
HTTP/1.1 200 OK
Server: nginx/1.21.5
Date: Mon, 21 Feb 2022 10:12:15 GMT
Content-Type: text/plain
Content-Length: 2
Connection: keep-alive
X-HTTP-LEVEL-HEADER: 1 X-ANOTHER-HTTP-LEVEL-HEADER: 1
OK

Für den Server, der Port 8081 überwacht, gibt es eine add_header-Direktive im server{}-Block, jedoch nicht in dessen untergeordnetem location-Block / . Der im server{}-Block definierte Header überschreibt die beiden im http{}-Kontext definierten Header:

% curl -i localhost:8081
HTTP/1.1 200 OK
Server: nginx/1.21.5
Date: Mon, 21 Feb 2022 10:12:20 GMT
Content-Type: text/plain
Content-Length: 2
Connection: keep-alive
X-SERVER-LEVEL-HEADER: 1
OK

Im untergeordneten location-Block /test gibt es eine add_header-Direktive, die sowohl den Header aus dem übergeordneten server{}-Block als auch die beiden Header aus dem http{}-Kontext überschreibt:

% curl -i localhost:8081/test
HTTP/1.1 200 OK
Server: nginx/1.21.5
Date: Mon, 21 Feb 2022 10:12:25 GMT
Content-Type: text/plain
Content-Length: 2
Connection: keep-alive
X-LOCATION-LEVEL-HEADER: 1
OK

Wenn ein location{}-Block sowohl die in den übergeordneten Kontexten definierten Header als auch die lokal definierten Header beibehalten soll, müssen wir die übergeordneten Header innerhalb des location{}-Blocks erneut definieren. Genau das haben wir im location-Block /correct getan:

% curl -i localhost:8081/correct
HTTP/1.1 200 OK
Server: nginx/1.21.5
Date: Mon, 21 Feb 2022 10:12:30 GMT
Content-Type: text/plain
Content-Length: 2
Connection: keep-alive
X-HTTP-LEVEL-HEADER: 1 X-ANOTHER-HTTP-LEVEL-HEADER: 1 X-SERVER-LEVEL-HEADER: 1 X-LOCATION-LEVEL-HEADER: 1
OK

Fehler 5: Die proxy_buffering-Direktive off Directive

Die Proxy-Pufferung ist in NGINX standardmäßig aktiviert (die Direktive „proxy_buffering“ ist auf „on“ gesetzt). Bei der Proxy-Pufferung speichert NGINX die vom Server eingehende Antwort zunächst in internen Puffern und beginnt erst dann mit der Übertragung der Daten an den Client, wenn die gesamte Antwort gepuffert wurde. Die Pufferung trägt dazu bei, die Leistung bei langsamen Clients zu optimieren: Da NGINX die Antwort so lange puffert, bis der Client sie vollständig abgerufen hat, kann der Proxy-Server seine Antwort so schnell wie möglich zurückgeben und steht anschließend wieder für die Verarbeitung weiterer Anfragen zur Verfügung.

Wenn die Proxy-Pufferung deaktiviert ist, puffert NGINX nur den ersten Teil der Antwort eines Servers, bevor es mit der Übertragung an den Client beginnt. Dafür wird ein Puffer verwendet, der standardmäßig eine Speicherseite groß ist (je nach Betriebssystem 4 KB oder 8 KB). Dies reicht in der Regel gerade für den Antwort-Header aus. Anschließend sendet NGINX die Antwort synchron beim Empfang an den Client, sodass der Server untätig warten muss, bis NGINX das nächste Antwortsegment annehmen kann.

Daher sind wir überrascht, wie häufig wir in Konfigurationen proxy_buffering auf off gesetzt sehen. Möglicherweise soll damit die von Clients wahrgenommene Latenz verringert werden, doch die Wirkung ist vernachlässigbar, während die Nachteile zahlreich sind: Bei deaktivierter Proxy-Pufferung funktionieren weder die Ratenbegrenzung noch das Caching, selbst wenn sie konfiguriert sind, die Leistung leidet und so weiter.

Es gibt nur wenige Anwendungsfälle, in denen es sinnvoll sein kann, die Proxy-Pufferung zu deaktivieren (z. B. bei Long Polling). Daher raten wir dringend davon ab, die Standardeinstellung zu ändern. Weitere Informationen finden Sie im NGINX Plus Admin Guide.

Fehler 6: Unsachgemäße Verwendung der if-Direktive

Die if-Direktive ist schwierig zu verwenden, insbesondere in location{}-Blöcken. Sie verhält sich häufig nicht wie erwartet und kann sogar Segmentierungsfehler verursachen. Tatsächlich ist sie so problematisch, dass es im NGINX-Wiki einen Artikel mit dem Titel „If is Evil“ gibt. Dort finden Sie eine ausführliche Erläuterung der Probleme und erfahren, wie Sie diese vermeiden können.

Im Allgemeinen sind die einzigen Direktiven, die Sie innerhalb eines if{}-Blocks immer sicher verwenden können, return und rewrite. Im folgenden Beispiel wird if verwendet, um Anfragen zu erkennen, die den X‑Test-Header enthalten (wobei dies eine beliebige Bedingung sein kann, die Sie prüfen möchten). NGINX gibt den Fehler 430 (Request Header Fields Too Large) zurück, fängt ihn an der benannten Location @error_430 ab und leitet die Anfrage per Proxy an die Upstream-Gruppe namens b weiter.

location / {    
    error_page 430 = @error_430;
    if ($http_x_test) {
        return 430; 
    }

    proxy_pass http://a;
}

location @error_430 {
    proxy_pass b;
}

Bei dieser und vielen anderen Verwendungen von „if“ lässt sich die Direktive häufig ganz vermeiden. Im folgenden Beispiel setzt der X‑Test-Header, sofern er in der Anfrage enthalten ist, im map{}-Block die Variable $upstream_name auf b, und die Anfrage wird an die Upstream-Gruppe mit diesem Namen weitergeleitet.

map $http_x_test $upstream_name {    
    default "b";
    ""      "a";
}

# ...

location / {
    proxy_pass http://$upstream_name;
}

Fehler 7: Übermäßige Integritätsprüfungen

Es ist durchaus üblich, mehrere virtuelle Server so zu konfigurieren, dass sie Anfragen an dieselbe Upstream-Gruppe weiterleiten (mit anderen Worten: dieselbe proxy_pass-Direktive in mehrere server{}-Blöcke aufzunehmen). Der Fehler besteht in diesem Fall darin, in jeden health_check-Direktive in jeden server{}-Block aufzunehmen. Dadurch werden die Upstream-Server lediglich stärker belastet, ohne dass zusätzliche Informationen gewonnen werden.

Auch wenn es offensichtlich erscheinen mag, besteht die Lösung darin, nur einen einzigen Health Check pro upstream{}-Block zu definieren. Hier definieren wir den Health Check für die Upstream-Gruppe namens b in einer speziellen benannten Location, einschließlich geeigneter Einstellungen für Zeitüberschreitungen und Header.

location / {    
    proxy_set_header Host $host;
    proxy_set_header "Connection" "";
    proxy_http_version 1.1;
    proxy_pass http://b;
}

location @health_check {
    health_check;
    proxy_connect_timeout 2s;
    proxy_read_timeout 3s;
    proxy_set_header Host example.com;
    proxy_pass http://b;
}

Bei komplexen Konfigurationen lässt sich die Verwaltung weiter vereinfachen, indem alle Speicherorte für Integritätsprüfungen zusammen mit der NGINX Plus API und dem Dashboard in einem einzigen virtuellen Server gruppiert werden, wie in diesem Beispiel.

server {	
        listen 8080;
 
	location / {
	    # …
 	}
 
	location @health_check_b {
	    health_check;
	    proxy_connect_timeout 2s;
	    proxy_read_timeout 3s;
	    proxy_set_header Host example.com;
	    proxy_pass http://b;
	}
 
	location @health_check_c {
	    health_check;
	    proxy_connect_timeout 2s;
	    proxy_read_timeout 3s;
	    proxy_set_header Host api.example.com;
	    proxy_pass http://c;
	}
 
	location /api {
	    api write=on;
	    # directives limiting access to the API (see 'Mistake 8' below)
	}
 
	location = /dashboard.html {
	    root   /usr/share/nginx/html;
	}
}

Weitere Informationen zu Integritätsprüfungen für HTTP-, TCP-, UDP- und gRPC-Server finden Sie im NGINX Plus Admin Guide.

Fehler 8: Ungesicherter Zugriff auf Metriken

Grundlegende Metriken zum Betrieb von NGINX sind über das Stub Status-Modul verfügbar. Mit NGINX Plus können Sie außerdem einen wesentlich umfangreicheren Satz von Metriken über die NGINX Plus API erfassen. Aktivieren Sie die Erfassung von Metriken, indem Sie die Direktive stub_status bzw. api in einen server{}- oder location{}-Block aufnehmen. Dieser wird dadurch zur URL, über die Sie anschließend auf die Metriken zugreifen können. (Für die NGINX Plus API müssen Sie außerdem gemeinsam genutzte Speicherzonen für die NGINX-Entitäten konfigurieren – virtuelle Server, Upstream-Gruppen, Caches usw. –, für die Sie Metriken erfassen möchten. Eine Anleitung finden Sie im NGINX Plus-Administratorhandbuch.)

Einige der Metriken enthalten vertrauliche Informationen, die für Angriffe auf Ihre Website oder die von NGINX weitergeleiteten Anwendungen genutzt werden können. Ein Fehler, den wir gelegentlich in Benutzerkonfigurationen feststellen, besteht darin, dass der Zugriff auf die entsprechende URL nicht eingeschränkt wird. Im Folgenden betrachten wir einige Möglichkeiten, wie Sie die Metriken schützen können. In den ersten Beispielen verwenden wir stub_status.

Mit der folgenden Konfiguration kann jeder im Internet unter http://example.com/basic_status auf die Messwerte zugreifen.

server {    
    listen 80;
    server_name example.com;

    location = /basic_status {
        stub_status;
    }
}

Metriken mit HTTP-Basisauthentifizierung schützen

Um die Metriken mit einem Passwort über HTTP Basic Authentication zu schützen, fügen Sie die Direktiven auth_basic und auth_basic_user_file hinzu. Die Datei (hier: .htpasswd) enthält die Benutzernamen und Passwörter der Clients, die sich anmelden können, um die Metriken einzusehen:

server {    
    listen 80;
    server_name example.com;

    location = /basic_status {
        auth_basic “closed site”;
        auth_basic_user_file conf.d/.htpasswd;
        stub_status;
    }
}

Metriken mit den Direktiven „allow“ und „deny“ schützen

Wenn Sie nicht möchten, dass sich autorisierte Benutzer anmelden müssen, und Sie die IP-Adressen kennen, von denen aus sie auf die Metriken zugreifen, können Sie alternativ die Direktive allow verwenden. Sie können einzelne IPv4- und IPv6-Adressen sowie CIDR-Bereiche angeben. Die Direktive deny all verhindert den Zugriff von allen anderen Adressen.

server {    
    listen 80;
    server_name example.com;

    location = /basic_status {
        allow 192.168.1.0/24;
        allow 10.1.1.0/16;
        allow 2001:0db8::/32;
        allow 96.1.2.23/32;
        deny  all;
        stub_status;
    }
}

Die beiden Methoden kombinieren

Was aber, wenn wir beide Methoden kombinieren möchten? Wir können Clients von bestimmten Adressen den Zugriff auf die Metriken ohne Passwort erlauben und gleichzeitig von Clients, die von anderen Adressen kommen, eine Anmeldung verlangen. Dazu verwenden wir die Direktive satisfy any. Sie weist NGINX an, Clients den Zugriff zu erlauben, wenn sie sich entweder mit HTTP-Basic-Auth-Anmeldedaten anmelden oder eine vorab genehmigte IP-Adresse verwenden. Für zusätzliche Sicherheit können Sie satisfy auf all setzen, sodass sich auch Personen anmelden müssen, die von bestimmten Adressen kommen.

server {    
    listen 80;
    server_name monitor.example.com;

    location = /basic_status {
        satisfy any;

        auth_basic “closed site”;
        auth_basic_user_file conf.d/.htpasswd;
        allow 192.168.1.0/24;
        allow 10.1.1.0/16;
        allow 2001:0db8::/32;
        allow 96.1.2.23/32;
        deny  all;
        stub_status;
    }
}

Bei NGINX Plus verwenden Sie dieselben Methoden, um den Zugriff auf den NGINX Plus API-Endpunkt (http://monitor.example.com:8080/api/ im folgenden Beispiel) sowie auf das Dashboard zur Überwachung der Live-Aktivitäten unter http://monitor.example.com/dashboard.html zu beschränken.

Diese Konfiguration ermöglicht nur Clients aus dem Netzwerk 96.1.2.23/32 oder von localhost den Zugriff ohne Passwort. Da die Direktiven auf der Ebene server{} definiert sind, gelten für die API und das Dashboard dieselben Einschränkungen. Nebenbei bemerkt bedeutet der Parameter write=on für api, dass diese Clients über die API auch Konfigurationsänderungen vornehmen können.

Weitere Informationen zur Konfiguration der API und des Dashboards finden Sie im NGINX Plus Admin Guide.

server {    
    listen 8080;
    server_name monitor.example.com;
 
    satisfy any;
    auth_basic “closed site”;
    auth_basic_user_file conf.d/.htpasswd;
    allow 127.0.0.1/32;
    allow 96.1.2.23/32;
    deny  all;

    location = /api/ {    
        api write=on;
    }

    location = /dashboard.html {
        root /usr/share/nginx/html;
    }
}

Fehler 9: Verwendung von ip_hash bei Datenverkehr, der vollständig aus demselben /24-CIDR-Block stammt

Der ip_hash-Algorithmus verteilt den Datenverkehr anhand eines Hashwerts der Client-IP-Adresse gleichmäßig auf die Server in einem upstream{}-Block. Als Hash-Schlüssel dienen die ersten drei Oktette einer IPv4-Adresse oder die gesamte IPv6-Adresse. Die Methode stellt Sitzungspersistenz her. Das bedeutet, dass Anfragen eines Clients immer an denselben Server weitergeleitet werden, es sei denn, dieser ist nicht verfügbar.

Angenommen, wir haben NGINX als Reverse-Proxy in einem virtuellen privaten Netzwerk bereitgestellt, das für Hochverfügbarkeit konfiguriert ist. Vor NGINX haben wir verschiedene Firewalls, Router, Layer-4-Load-Balancer und Gateways geschaltet, die Datenverkehr aus unterschiedlichen Quellen (dem internen Netzwerk, Partnernetzwerken, dem Internet usw.) annehmen und zur Weiterleitung per Reverse-Proxy an Upstream-Server an NGINX übergeben. Hier ist die ursprüngliche NGINX-Konfiguration:

http {
    upstream {
        ip_hash;
        server 10.10.20.105:8080;
        server 10.10.20.106:8080;
        server 10.10.20.108:8080;
    }
 
    server {# …}
}

Dabei stellt sich jedoch ein Problem heraus: Alle „abfangenden“ Geräte befinden sich im selben Netzwerk 10.10.0.0/24, sodass es für NGINX so aussieht, als käme der gesamte Datenverkehr von Adressen in diesem CIDR-Bereich. Beachten Sie, dass der ip_hash-Algorithmus die ersten drei Oktette einer IPv4-Adresse hasht. In unserer Bereitstellung sind die ersten drei Oktette – 10.10.0 – bei jedem Client identisch. Daher ist auch der Hash für alle gleich, sodass es keine Grundlage dafür gibt, den Datenverkehr auf verschiedene Server zu verteilen.

Die Lösung besteht darin, stattdessen den hash-Algorithmus mit der Variablen $binary_remote_addr als Hash-Schlüssel zu verwenden. Diese Variable erfasst die vollständige Clientadresse und wandelt sie in eine binäre Darstellung um, die bei einer IPv4-Adresse 4 Byte und bei einer IPv6-Adresse 16 Byte umfasst. Dadurch unterscheidet sich der Hash nun für jedes zwischengeschaltete Gerät, und der Lastenausgleich funktioniert wie erwartet.

Wir verwenden außerdem den Parameter „consistent“, um anstelle der Standardmethode das Ketama-Hashing-Verfahren zu nutzen. Dadurch wird die Anzahl der Schlüssel, die bei einer Änderung der Servergruppe einem anderen Upstream-Server neu zugeordnet werden, erheblich reduziert, was bei Cache-Servern zu einer höheren Cache-Trefferquote führt.

http {    
        upstream {
        hash $binary_remote_addr consistent;
        server 10.10.20.105:8080;
        server 10.10.20.106:8080;
        server 10.10.20.108:8080;
    }

    server {# …}
}

Fehler 10: Upstream-Gruppen nicht nutzen

Angenommen, Sie verwenden NGINX für einen der einfachsten Anwendungsfälle: als Reverse-Proxy für eine einzelne NodeJS-basierte Backend-Anwendung, die Port 3000 überwacht. Eine typische Konfiguration könnte wie folgt aussehen:

http {
    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_set_header Host $host;
            proxy_pass http://localhost:3000/;
        }
    }
}

Ganz einfach, oder? Die proxy_pass-Direktive teilt NGINX mit, wohin Clientanfragen weitergeleitet werden sollen. NGINX muss lediglich den Hostnamen in eine IPv4- oder IPv6-Adresse auflösen. Sobald die Verbindung hergestellt ist, leitet NGINX die Anfragen an diesen Server weiter.

Der Fehler besteht hier in der Annahme, dass es sinnlos sei, einen upstream{}-Block zu erstellen, nur weil es lediglich einen Server gibt und somit keinen Grund, Load Balancing zu konfigurieren. Tatsächlich erschließt ein upstream{}-Block mehrere Funktionen, die die Leistung verbessern, wie diese Konfiguration zeigt:

http {
    upstream node_backend {
        zone upstreams 64K;
        server 127.0.0.1:3000 max_fails=1 fail_timeout=2s;
        keepalive 2;
    }

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_set_header Host $host;
            proxy_pass http://node_backend/;
            proxy_next_upstream error timeout http_500;

        }
    }
}

Die zone-Direktive richtet einen gemeinsam genutzten Speicherbereich ein, in dem alle NGINX-Worker-Prozesse auf dem Host auf Konfigurations- und Statusinformationen zu den Upstream-Servern zugreifen können. Mehrere Upstream-Gruppen können diesen Speicherbereich gemeinsam nutzen. Mit NGINX Plus können Sie über diesen Speicherbereich außerdem die NGINX Plus API verwenden, um die Server in einer Upstream-Gruppe und die Einstellungen einzelner Server zu ändern, ohne NGINX neu zu starten. Weitere Informationen finden Sie im NGINX Plus-Administratorhandbuch.

Die server-Direktive verfügt über mehrere Parameter, mit denen Sie das Serververhalten anpassen können. In diesem Beispiel haben wir die Bedingungen geändert, anhand derer NGINX feststellt, dass ein Server fehlerhaft ist und daher keine Anfragen annehmen kann. Hier wird ein Server als fehlerhaft eingestuft, wenn innerhalb eines Zeitraums von jeweils 2 Sekunden auch nur ein Kommunikationsversuch fehlschlägt (anstatt standardmäßig einmal innerhalb eines Zeitraums von 10 Sekunden).

Wir kombinieren diese Einstellung mit der Direktive proxy_next_upstream, um festzulegen, was NGINX als fehlgeschlagenen Kommunikationsversuch betrachtet. In diesem Fall leitet es Anfragen an den nächsten Server in der Upstream-Gruppe weiter. Zusätzlich zu den standardmäßigen Fehler- und Zeitüberschreitungsbedingungen fügen wir http_500 hinzu, sodass NGINX einen HTTP-Statuscode 500 (Internal Server Error) von einem Upstream-Server als fehlgeschlagenen Versuch betrachtet.

Die keepalive-Direktive legt die Anzahl der inaktiven Keepalive-Verbindungen zu Upstream-Servern fest, die im Cache jedes Worker-Prozesses vorgehalten werden. Die Vorteile haben wir bereits unter Fehler 3: Keepalive-Verbindungen zu Upstream-Servern nicht aktiviert erläutert.

Mit NGINX Plus können Sie zusätzliche Funktionen für Upstream-Gruppen konfigurieren:

  • Wie oben erwähnt, löst NGINX Open Source Serverhostnamen nur einmal beim Start in IP-Adressen auf. Der Parameter resolve der server-Direktive ermöglicht es NGINX Plus, Änderungen an den IP-Adressen zu überwachen, die dem Domänennamen eines Upstream-Servers entsprechen, und die Upstream-Konfiguration automatisch anzupassen, ohne dass ein Neustart erforderlich ist. Der Parameter service ermöglicht NGINX Plus außerdem die Verwendung von DNS-SRV-Einträgen, die Informationen zu Portnummern, Gewichtungen und Prioritäten enthalten. Dies ist in Microservices-Umgebungen von entscheidender Bedeutung, da die Portnummern von Diensten häufig dynamisch zugewiesen werden. Weitere Informationen zur Auflösung von Serveradressen finden Sie in unserem Blogbeitrag DNS für die Diensterkennung mit NGINX und NGINX Plus verwenden.
  • Der Parameter „slow_start“ der server-Direktive ermöglicht es NGINX Plus, die Anzahl der Anfragen, die an einen Server gesendet werden, der seit Kurzem wieder als fehlerfrei gilt und Anfragen annehmen kann, schrittweise zu erhöhen. Dadurch wird ein plötzlicher Ansturm von Anfragen verhindert, der den Server überlasten und zu einem erneuten Ausfall führen könnte.
  • Die queue-Direktive ermöglicht NGINX Plus, Anfragen in eine Warteschlange zu stellen, wenn kein Upstream-Server zur Bearbeitung der Anfrage ausgewählt werden kann, anstatt sofort einen Fehler an den Client zurückzugeben.

Ressourcen

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, 2021, Tech

Über den Autor

Timo Stark
Timo StarkIngenieur für Produktmanagement

Weitere Blogbeiträge von Timo Stark

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