Optimierung der Nginx-Worker-Prozesse für maximale Leistung: Ein praktischer Leitfaden

Optimieren Sie Ihren Nginx-Server für hohes Verkehrsaufkommen mit diesem praktischen Leitfaden zur Konfiguration der wichtigsten Leistungsdirektiven. Erfahren Sie die besten Praktiken für die Einstellung von `worker_processes` entsprechend der CPU-Kerne, maximieren Sie die Parallelität mit `worker_connections` und stellen Sie die Einhaltung der zugrunde liegenden Betriebssystem-Grenzen für Dateideskriptoren (`ulimit`) sicher. Dieser Artikel bietet umsetzbare Konfigurationsbeispiele und wesentliche Tuning-Tipps, um die Latenz zu minimieren und den Durchsatz Ihres Servers drastisch zu erhöhen.

Optimierung der Nginx-Worker-Prozesse für maximale Leistung: Ein praktischer Leitfaden

Nginx kann eine große Anzahl gleichzeitiger Verbindungen mit einem kleinen Prozess-Fußabdruck bewältigen, aber nur, wenn seine Worker-Grenzen mit der darunterliegenden Maschine übereinstimmen. Die beiden Einstellungen, die zuerst in Betracht gezogen werden, sind worker_processes und worker_connections. Sie sind nützlich, aber auch leicht zu übertreiben. Beide auf riesige Zahlen zu setzen, schafft keine freie Kapazität. Es kann den Engpass lediglich zu Dateideskriptoren, Speicher, Upstream-Servern oder dem Netzwerkstack verschieben.

Das praktische Ziel ist es, Nginx genügend Worker zu geben, um die vorhandenen CPU-Kerne zu nutzen, genügend Verbindungsslots für den tatsächlichen Datenverkehr und ausreichende Betriebssystemgrenzen, um während normaler Lastspitzen nicht an die Decke zu stoßen.

Verständnis der Nginx-Worker-Architektur

Nginx arbeitet nach einem Master-Worker-Modell. Der Master-Prozess ist für das Lesen und Validieren der Konfiguration, das Binden an Ports und die Verwaltung der Worker-Prozesse verantwortlich. Er führt nicht-kritische Aufgaben wie die Überwachung von Systemressourcen und das Neustarten von Workern bei Bedarf aus.

Worker-Prozesse sind diejenigen, die die Hauptarbeit leisten. Diese Prozesse sind (bei der Standard-Nginx-Kompilierung) single-threaded und verwenden nicht-blockierende Systemaufrufe. Jeder Worker bearbeitet tausende gleichzeitiger Verbindungen effizient mittels einer Ereignisschleife, sodass ein Prozess mehrere Anfragen ohne Blockierung verwalten kann – der Schlüssel zur Leistung von Nginx.

Eine ordnungsgemäße Optimierung erfordert ein Gleichgewicht zwischen der Anzahl der Worker (gebunden an CPU-Ressourcen) und der maximalen Anzahl von Verbindungen, die jeder Worker verarbeiten kann.

Konfiguration von worker_processes: Der CPU-Kern-Faktor

Die Direktive worker_processes legt fest, wie viele Worker-Prozesse Nginx starten soll. Diese Einstellung beeinflusst direkt, wie Nginx die CPU-Ressourcen Ihres Servers nutzt.

Best Practice: Worker an Kerne anpassen

Die gängigste und am meisten empfohlene Best Practice ist es, die Anzahl der Worker-Prozesse gleich der Anzahl der verfügbaren CPU-Kerne auf Ihrem Server zu setzen. Dies stellt sicher, dass jeder Kern effizient genutzt wird, ohne übermäßigen Overhead durch Kontextwechsel zu verursachen.

Wenn die Anzahl der Worker die Anzahl der Kerne übersteigt, muss das Betriebssystem häufig den CPU-Fokus zwischen konkurrierenden Nginx-Prozessen wechseln (Kontextwechsel), was Latenz verursacht und die Gesamtleistung verringert.

Verwendung der auto-Direktive

Für moderne Versionen von Nginx (1.3.8 und später) ist der einfachste und effektivste Ansatz die Verwendung des Parameters auto. Nginx erkennt automatisch die Anzahl der verfügbaren CPU-Kerne und setzt die Worker-Prozesse entsprechend.

# Empfohlene Einstellung für die meisten Bereitstellungen
worker_processes auto;

Manuelle Konfiguration

Wenn Sie manuelle Kontrolle benötigen oder eine ältere Version verwenden, können Sie die genaue Anzahl der Worker angeben. Sie können die Anzahl der Kerne mit Systemdienstprogrammen ermitteln:

# Anzahl der CPU-Kerne ermitteln
grep processor /proc/cpuinfo | wc -l

Wenn das System 8 Kerne hat, würde die Konfiguration wie folgt aussehen:

# Manuelle Einstellung der Worker-Prozesse auf 8
worker_processes 8;

Tipp: Die Anpassung an die Anzahl der verfügbaren Kerne ist der sicherste Ausgangspunkt. Bei ungewöhnlichen I/O-lastigen Workloads können Sie einen anderen Wert testen, aber messen Sie ihn unter realistischen Verkehrsbedingungen, bevor Sie ihn beibehalten. Für typisches statisches Serving, Proxying und TLS-Terminierung ist auto normalerweise die am wenigsten überraschende Wahl.

Konfiguration von worker_connections: Der Parallelitätsfaktor

Die Direktive worker_connections wird innerhalb des events-Blocks konfiguriert und definiert die maximale Anzahl gleichzeitiger Verbindungen, die ein einzelner Worker-Prozess verarbeiten kann. Dies umfasst Verbindungen zu Clients, Verbindungen zu Upstream-Proxy-Servern und interne Health-Check-Verbindungen.

Berechnung der maximalen Clients

Die theoretische maximale Anzahl gleichzeitiger Client-Verbindungen, die Ihr Nginx-Server verarbeiten kann, wird wie folgt berechnet:

$$\text{Max Clients} = \text{worker_processes} \times \text{worker_connections}$$

Wenn Sie 4 Worker-Prozesse und 10.000 Worker-Verbindungen pro Prozess haben, könnte Nginx theoretisch 40.000 gleichzeitige Verbindungen verarbeiten.

Diese Zahl ist nur eine grobe Obergrenze. Eine proxierte Anfrage kann gleichzeitig eine Client-Verbindung und eine Upstream-Verbindung nutzen. WebSocket- und Long-Polling-Datenverkehr können Slots viel länger belegen als eine normale Seitenanfrage. Keep-Alive-Verbindungen können auch geöffnet bleiben, während sie sehr wenig Arbeit verrichten. Wenn Nginx hauptsächlich statische Dateien ausliefert, liegt die Mathematik näher an der einfachen Formel. Wenn es als Reverse-Proxy fungiert, lassen Sie Spielraum.

Festlegen des Verbindungslimits

Es ist üblich, worker_connections auf einige tausend oder mehr auf stark ausgelasteten Servern zu setzen, vorausgesetzt, Speicher- und Dateideskriptor-Limits unterstützen dies. Kopieren Sie nicht blind einen großen Wert; wählen Sie einen Wert, der der erwarteten Parallelität plus Spielraum für Lastspitzen entspricht.

# Beispielkonfiguration für den events-Block

events {
    # Max gleichzeitige Verbindungen pro Worker-Prozess
    worker_connections 16384;

    # Kann bei Lastspitzen helfen, aber testen Sie die Fairness unter Last.
    multi_accept on;
}

Systemgrenzen (ulimit) Einschränkung

Entscheidend ist, dass die Einstellung worker_connections durch die Betriebssystemgrenze für die Anzahl der offenen Dateideskriptoren (FDs) pro Prozess beschränkt wird, die oft durch die Einstellung ulimit -n gesteuert wird.

Nginx kann nicht mehr Verbindungen öffnen, als das Betriebssystem Dateideskriptoren zulässt. Da jede Verbindung (Client-Socket, Logdatei, Proxy-Socket) einen Dateideskriptor benötigt, ist es entscheidend, dass die Systemgrenze hoch genug gesetzt ist.

Überprüfen und Erhöhen der Dateideskriptor-Limits

  1. Aktuelles Limit überprüfen:

    ulimit -n
    
  2. Limit vorübergehend erhöhen (für die aktuelle Sitzung):

    ulimit -n 65536
    
  3. Limit dauerhaft erhöhen (über /etc/security/limits.conf):

    Fügen Sie die folgenden Zeilen hinzu und ersetzen Sie nginx_user durch den Benutzer, unter dem Nginx läuft (oft www-data oder nginx):

    # /etc/security/limits.conf
    nginx_user soft nofile 65536
    nginx_user hard nofile 65536
    

Warnung: Stellen Sie sicher, dass das Dateideskriptor-Limit pro Prozess für den Nginx-Worker-Benutzer höher ist als worker_connections, mit zusätzlichem Spielraum für Logs, Upstream-Sockets, Cache-Dateien und andere geöffnete Dateien. Systemweite Limits sind ebenfalls wichtig, aber das Limit pro Prozess ist das, das am häufigsten überrascht.

Wenn Nginx von systemd verwaltet wird, reicht /etc/security/limits.conf möglicherweise nicht aus. Viele Distributionen starten Dienste mit Limits aus der Unit-Datei. Überprüfen Sie das aktive Limit mit:

cat /proc/$(pgrep -o nginx)/limits | grep "open files"

Für einen systemd-Override verwenden Sie:

sudo systemctl edit nginx

Fügen Sie dann hinzu:

[Service]
LimitNOFILE=65536

Laden Sie systemd neu und starten Sie Nginx während eines Wartungsfensters neu:

sudo systemctl daemon-reload
sudo systemctl restart nginx

Erweitertes Tuning und Überwachung

Über die Kern-Direktiven hinaus gibt es einige zusätzliche Überlegungen, die zur Feinabstimmung der Leistung beitragen können:

1. Pinning von Worker-Prozessen

In Hochleistungsumgebungen, insbesondere auf Systemen mit mehreren CPU-Sockeln (NUMA-Architekturen), möchten Sie möglicherweise die Direktive worker_cpu_affinity verwenden. Diese teilt dem Betriebssystem mit, bestimmte Worker-Prozesse auf bestimmte CPUs zu beschränken, was die Leistung verbessern kann, indem sichergestellt wird, dass CPU-Caches heiß bleiben und Speicherlokalitätsprobleme vermieden werden.

Beispiel für ein 8-Kern-System:

worker_processes 8;
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;

Diese Einstellung ist komplex und normalerweise nur in extremen Hochlastsituationen vorteilhaft; worker_processes auto ist für die meisten Bereitstellungen ausreichend.

2. Überwachung von Leistungsmetriken

Nach der Anwendung von Optimierungen ist es entscheidend, die Auswirkungen zu überwachen. Verwenden Sie das Nginx Stub Status-Modul (oder ein Tool wie Prometheus/Grafana), um wichtige Metriken zu verfolgen:

Metrik Beschreibung Optimierungsprüfung
Aktive Verbindungen Gesamtzahl der aktuell bearbeiteten Verbindungen. Sollte unter dem theoretischen Maximum liegen.
Lesen/Schreiben/Warten Verbindungen in verschiedenen Zuständen. Hohe Warten-Zahlen deuten oft auf langlebige HTTP-Keep-Alives (gut) oder unzureichende Verarbeitungsressourcen (schlecht) hin.
Anfragerate Anfragen pro Sekunde. Wird verwendet, um die tatsächliche Leistungsverbesserung nach Konfigurationsänderungen zu messen.

Wenn Sie eine hohe CPU-Auslastung über alle Kerne und hohe Anfrageraten beobachten, sind Ihre worker_processes wahrscheinlich korrekt konfiguriert. Wenn während Spitzenlastzeiten CPU-Kerne im Leerlauf sind, sollten Sie Ihre Konfiguration überprüfen oder nach blockierenden I/O-Operationen außerhalb von Nginx suchen.

3. Strategie bei Verbindungsüberlauf

Wenn der Server das maximale Verbindungslimit erreicht (worker_processes * worker_connections), können neue Verbindungen fehlschlagen oder in Warteschlangen verbleiben, bis sie zeitlich auslaufen. Eine Erhöhung von worker_connections kann nur helfen, wenn Nginx der tatsächliche Engpass ist. Wenn Upstream-Anwendungsserver gesättigt sind, kann eine Erhöhung des Limits den Ausfall noch schlimmer erscheinen lassen, da sich mehr Anfragen hinter langsamen Backends stauen.

Verwenden Sie das Fehlerprotokoll als Signal. Meldungen wie worker_connections are not enough weisen direkt auf Nginx-Limits hin. Ein Anstieg von upstream timed out, connect() failed oder 502/504-Antworten deutet eher auf Backend-Kapazität, Netzwerkprobleme oder Timeout-Einstellungen hin.

Eine vernünftige Startkonfiguration

Für einen kleinen oder mittleren Reverse-Proxy ist dies eine vernünftige Basis:

worker_processes auto;
worker_rlimit_nofile 65536;

events {
    worker_connections 8192;
    multi_accept off;
}

Warum hier multi_accept off? Es ist die konservative Standardeinstellung auf vielen Systemen. Das Einschalten kann einem Worker helfen, eine anstehende Accept-Warteschlange schnell zu leeren, aber unter bestimmten Verkehrsmustern kann es dazu führen, dass ein Worker eine große Menge an Verbindungen an sich reißt, während andere untätig bleiben. Wenn Sie stoßartigen Verkehr und einen getesteten Grund haben, es zu aktivieren, tun Sie dies. Wenn Sie einen Allzweck-Webserver tunen, halten Sie die Basis einfach und messen Sie zuerst.

Wenn der Server viele WebSocket-Verbindungen, Server-Sent Events oder langlebige API-Streams verarbeitet, erhöhen Sie das Verbindungslimit aggressiver und achten Sie genau auf den Speicher. Ein Server mit 20.000 meist untätigen WebSocket-Clients hat ein anderes Profil als ein Server, der 20.000 kurze statische Dateianfragen bearbeitet.

So validieren Sie die Änderung

Bevor Sie die Produktion ändern, erfassen Sie eine kleine Basislinie:

nginx -T | grep -E 'worker_processes|worker_connections|worker_rlimit_nofile'
ss -s
ulimit -n

Überprüfen Sie nach der Änderung, dass Nginx sie tatsächlich geladen hat:

sudo nginx -t
sudo systemctl reload nginx
ps -o pid,comm,nlwp,pcpu,pmem -C nginx
cat /proc/$(pgrep -n nginx)/limits | grep "open files"

Beobachten Sie dann das Verhalten während des tatsächlichen Datenverkehrs. Wenn alle CPU-Kerne ausgelastet sind und die Latenz steigt, leistet Nginx möglicherweise nützliche Arbeit und erreicht die CPU-Kapazität. Wenn die CPU niedrig ist, aber Verbindungen in der Warteschlange stehen oder zeitlich auslaufen, überprüfen Sie Dateideskriptoren, Upstream-Sättigung, DNS-Auflösung, Festplatten-I/O oder Firewall-Limits. Worker-Tuning ist ein Hebel, nicht die ganze Leistungsgeschichte.

Die Zahlen im Kontext lesen

Ein häufiger Fehler ist es, "aktive Verbindungen" mit "aktiven Benutzern" gleichzusetzen. Das ist nicht der Fall. Ein Browser kann mehrere Verbindungen für Assets öffnen. Ein API-Client kann eine Verbindung zwischen Anfragen am Leben halten. Ein WebSocket-Client kann eine Verbindung stundenlang halten, während er fast keinen Datenverkehr sendet. Wenn Sie worker_connections dimensionieren, denken Sie in Begriffen von gleichzeitigen Sockets, nicht von Personen.

Denken Sie bei einem Reverse-Proxy auch an die Upstream-Seite. Wenn 4.000 Clients auf proxyte Antworten warten, hält Nginx möglicherweise auch tausende Upstream-Sockets. Deshalb kann einem Server die Dateideskriptoren ausgehen, bevor die einfache clientseitige Berechnung dies vorhersagt. Dies wird besonders sichtbar, wenn die Upstream-Anwendung langsamer wird: Anfragen bleiben länger offen, die Parallelität steigt, und Nginx beginnt, mehr Sockets zu verbrauchen, obwohl die eingehende Anfragerate sich nicht geändert hat.

Keep-Alive-Einstellungen beeinflussen dies ebenfalls. Lange Keep-Alive-Timeout-Werte reduzieren den Verbindungswechsel, was stark frequentierten Seiten helfen kann, aber sie halten auch untätige Sockets länger offen. Sehr kurze Keep-Alive-Timeout-Werte geben Sockets schneller frei, können aber TLS-Handshakes und Verbindungsaufbau-Overhead erhöhen. Es gibt keinen perfekten Wert; verwenden Sie die Verkehrsform als Leitfaden. Eine öffentliche Website mit vielen kurzen Besuchen benötigt möglicherweise eine andere Balance als eine interne API mit einer kleinen Anzahl persistenter Clients.

Wenn Sie innerhalb eines Containers tunen, überprüfen Sie die Limits innerhalb des Containers und auf Host- oder Orchestriererebene. Ein Kubernetes-Pod, Docker-Container oder systemd-Dienst kann ein niedrigeres nofile-Limit haben als die Host-Shell, die Sie zum Testen verwendet haben. Überprüfen Sie immer den laufenden Nginx-Prozess, nicht nur Ihre Login-Sitzung.

Zusammenfassung der Best Practices

Direktive Empfohlener Wert Begründung
worker_processes auto (oder Kernanzahl) Stellt optimale CPU-Auslastung sicher und minimiert Kontextwechsel-Overhead.
worker_connections Beginnen Sie mit einigen tausend; erhöhen Sie basierend auf gemessener Parallelität Bietet Verbindungsspielraum, ohne andere Engpässe zu verbergen.
OS-Limit (ulimit -n) Höher als der Bedarf pro Worker-Verbindung, mit zusätzlichem Spielraum Stellt Dateideskriptoren für Client-Sockets, Upstream-Sockets, Logs und Cache-Dateien bereit.
multi_accept Vor Aktivierung testen Kann bei Lastspitzen helfen, ist aber nicht automatisch für jede Arbeitslast besser.

Die beste Nginx-Worker-Konfiguration ist normalerweise einfach: worker_processes auto, ein Verbindungslimit, das die tatsächliche Parallelität widerspiegelt, und Dateideskriptor-Limits, die für die Arbeitslast hoch genug sind. Tunen Sie es, überprüfen Sie die aktiven Prozesslimits und behalten Sie das Fehlerprotokoll im Auge. Wenn die Symptome auf den Upstream hinweisen, beheben Sie den Upstream, anstatt Nginx mehr Arbeit akzeptieren zu lassen, als die Anwendung bewältigen kann.