Ottimizzazione delle Prestazioni di Nginx: Ottimizza i Processi Worker e le Connessioni
Regola i processi worker di Nginx, le connessioni worker, i limiti dei file, il comportamento keepalive e la capacità upstream senza fare supposizioni.
Ottimizzazione delle Prestazioni di Nginx: Ottimizza i Processi Worker e le Connessioni
L'ottimizzazione delle prestazioni di Nginx spesso inizia con due impostazioni: i processi worker e le connessioni worker. Queste impostazioni determinano quante richieste il tuo server può gestire contemporaneamente, quindi piccoli errori possono manifestarsi come pagine lente, download bloccati o errori di connessione durante i picchi di traffico.
La buona notizia è che non devi fare supposizioni a caso. Puoi ottimizzare Nginx adattando il suo modello worker alla tua CPU, ai limiti dei file, al modello di traffico e al comportamento dell'applicazione upstream.
Come i Worker di Nginx Gestiscono il Traffico
Nginx utilizza un processo master e uno o più processi worker. Il processo master legge la configurazione, avvia i worker e gestisce i ricaricamenti. I worker si occupano della gestione effettiva delle richieste.
Ogni worker può gestire molte connessioni perché Nginx utilizza un modello basato su eventi. Questo è diverso dai server web più vecchi che spesso necessitavano di un processo o thread per richiesta. Un worker di Nginx può mantenere migliaia di connessioni keepalive inattive se il sistema operativo lo consente.
Le due direttive principali sono solitamente inserite in /etc/nginx/nginx.conf:
worker_processes auto;
events {
worker_connections 1024;
}
worker_processes auto; dice a Nginx di creare un worker per ogni core CPU disponibile. Per la maggior parte dei server Linux moderni, questo è il punto di partenza corretto. Evita di codificare un valore che diventa errato quando ridimensioni una macchina virtuale.
worker_connections imposta il numero massimo di connessioni simultanee che ogni worker può aprire. Il limite superiore approssimativo è:
worker_processes * worker_connections
Se hai 4 worker e 4096 connessioni worker, il massimo teorico è 16.384 connessioni. Nella vita reale, il numero utilizzabile è inferiore perché il traffico di proxy inverso può utilizzare sia connessioni lato client che upstream.
Ad esempio, se Nginx proxy il traffico verso un'app Node.js, una richiesta utente può consumare una connessione client più una connessione upstream. Ciò significa che 16.384 connessioni aperte potrebbero supportare circa 8.000 richieste proxy attive, a seconda del keepalive e dei tempi delle richieste.
Scelta dei Valori per Processi Worker e Connessioni Worker
Inizia con worker_processes auto a meno che tu non abbia un motivo specifico per non farlo. Impostare manualmente questo valore più alto del numero di CPU raramente aiuta. Può aumentare il context switching e peggiorare le prestazioni sotto carico.
Poi regola worker_connections in base alla concorrenza prevista. Uno strumento interno tranquillo può andare bene con 1024. Un sito web pubblico dietro un bilanciatore di carico può aver bisogno di 4096, 8192 o più.
Una base pratica per molti server di produzione è simile a questa:
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
multi_accept on;
}
worker_rlimit_nofile aumenta il limite dei descrittori di file disponibili per i worker di Nginx. Questo è importante perché ogni socket di rete utilizza un descrittore di file. Se il limite del sistema operativo rimane basso, aumentare worker_connections da solo non aiuterà.
Dovresti anche controllare il limite del gestore dei servizi. Sui sistemi systemd, Nginx potrebbe aver bisogno di un override come:
[Service]
LimitNOFILE=65535
Dopo aver modificato i limiti di systemd, ricarica systemd e riavvia Nginx. Per un riferimento più ampio sui comandi, consulta Comandi di controllo del servizio Nginx.
Fai attenzione con multi_accept on. Permette a un worker di accettare quante più nuove connessioni possibile dopo aver ricevuto una notifica di prontezza. Questo può aiutare durante i picchi, ma non è magico. Se la tua app upstream è lenta, accettare connessioni più velocemente potrebbe solo riempire le code più rapidamente.
Limiti del Sistema Operativo che Influenzano Nginx
Le impostazioni di Nginx si basano sui limiti di Linux. Se questi limiti sono troppo piccoli, Nginx raggiungerà un tetto anche quando la sua configurazione sembra generosa.
Controlla queste aree durante l'ottimizzazione:
- Limite dei file aperti per il processo Nginx
- Impostazioni del backlog di rete del kernel
- Disponibilità di porte effimere per traffico proxy intenso
- Comportamento keepalive upstream
- Valori di timeout inattivo del bilanciatore di carico
Il limite dei file aperti è il blocco più comune. Se Nginx registra messaggi come worker_connections are not enough o too many open files, devi guardare sia i limiti di Nginx che quelli di systemd.
Le impostazioni di backlog sono importanti quando molti client si connettono contemporaneamente. Se la coda di accettazione del kernel si riempie, gli utenti potrebbero vedere timeout di connessione anche se l'utilizzo della CPU sembra normale. Valori come net.core.somaxconn e net.ipv4.tcp_max_syn_backlog vengono spesso rivisti durante l'ottimizzazione per traffico elevato.
Non copiare grandi valori del kernel da esempi casuali senza testare. Un piccolo team che gestisce un server API non ha bisogno delle stesse impostazioni di un nodo edge CDN. Ottimizza per passi, misura e prendi appunti.
C'è un altro dettaglio che mette in difficoltà le persone: il limite di connessione di Nginx non è l'unico limite di connessione nel percorso. Un bilanciatore di carico cloud ha timeout di inattività. Un runtime container può avere limiti di traduzione degli indirizzi di rete. L'app backend può avere il proprio pool di worker o pool di connessioni al database. Se Nginx può accettare 20.000 connessioni ma l'app può elaborare solo 200 richieste concorrenti, gli utenti aspetteranno comunque.
Ecco perché una modifica all'ottimizzazione delle connessioni dovrebbe includere un rapido controllo end-to-end. Esegui un piccolo test di carico da un host esterno al server, osserva le connessioni attive di Nginx e osserva anche il backend. Se la latenza del backend aumenta bruscamente mentre Nginx rimane calmo, il proxy sta facendo il suo lavoro e il prossimo limite è dietro di esso.
Ottimizzazione per Carichi di Lavoro di Proxy Inverso
Molti server Nginx agiscono come proxy inversi davanti ai server applicativi. In quel ruolo, il comportamento upstream è importante tanto quanto la capacità di Nginx.
Usa il keepalive upstream quando Nginx parla ripetutamente con lo stesso pool backend:
upstream app_backend {
server 127.0.0.1:3000;
keepalive 32;
}
server {
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://app_backend;
}
}
Questo riduce il costo di apertura di nuove connessioni backend. È particolarmente utile quando la tua app riceve molte richieste piccole, come chiamate API da un dashboard.
Controlla anche i tuoi valori di timeout. Timeout proxy molto lunghi possono mantenere occupate le connessioni worker dopo che i client scompaiono o le applicazioni smettono di rispondere. Timeout molto brevi possono interrompere richieste lente legittime. Abbina i valori di timeout al carico di lavoro invece di usarne uno predefinito ovunque.
Uno scenario pratico: il tuo sito è veloce per la maggior parte del giorno ma rallenta durante un invio di newsletter. La CPU è solo al 35%, ma i log di Nginx mostrano avvisi di connessione. Questo punta lontano dalla CPU grezza e verso i limiti di connessione, i descrittori di file o l'accoppiamento upstream. Aumentare le connessioni worker può aiutare, ma solo se l'app e il sistema operativo possono supportare il carico extra.
Un altro scenario comune è un'app dashboard che effettua molte piccole chiamate API da ogni scheda del browser. Dieci persone possono creare centinaia di richieste brevi. In questo caso, il keepalive upstream spesso è più importante che semplicemente aumentare worker_connections, perché la configurazione TCP ripetuta verso il backend diventa un overhead inutile.
Per un servizio di download di file, la storia è diversa. Un piccolo numero di utenti può mantenere le connessioni aperte per molto tempo durante il download di file grandi. Potresti aver bisogno di abbastanza connessioni worker per trasferimenti di lunga durata, ma dovresti anche controllare sendfile, throughput del disco, throughput di rete e comportamento del timeout client.
Per app WebSocket o long-polling, le connessioni inattive sono normali. Un numero elevato di Waiting non è automaticamente negativo. La domanda è se quelle connessioni inattive lasciano abbastanza capacità per nuove richieste e se l'uso della memoria rimane prevedibile.
Leggere stub_status Durante l'Ottimizzazione
Il modulo stub_status ti offre una visione rapida del comportamento delle connessioni:
Active connections: 291
server accepts handled requests
1162447 1162447 4496426
Reading: 6 Writing: 17 Waiting: 268
Reading significa che Nginx sta leggendo le intestazioni delle richieste. Un numero elevato sostenuto può indicare client lenti, intestazioni grandi o un modello di attacco. Writing significa che Nginx sta inviando risposte. Questo può aumentare quando i client sono lenti a ricevere dati o quando le risposte sono grandi. Waiting significa connessioni keepalive inattive. Questo numero può essere alto su siti sani.
I contatori accepts e handled dovrebbero solitamente muoversi insieme. Se le connessioni accettate aumentano ma quelle gestite sono in ritardo o compaiono errori, controlla i limiti dei worker e i limiti dei descrittori di file. Controlla anche se il kernel sta scartando le connessioni prima che Nginx possa gestirle.
Questi contatori sono basilari, ma sono utili perché separano la pressione delle connessioni dalla pressione della CPU. Se le connessioni attive sono basse e la CPU è alta, il problema probabilmente non è worker_connections. Se le connessioni attive sono alte e la CPU è bassa, i limiti di connessione, il comportamento keepalive, l'accoppiamento upstream o i client lenti diventano più probabili.
Una Configurazione di Base Sicura
Per un piccolo server di produzione, preferirei iniziare in modo conservativo e misurare:
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
multi_accept on;
}
http {
keepalive_timeout 30s;
keepalive_requests 1000;
}
Questa non è una configurazione universale migliore. È un punto di partenza ragionevole per molti carichi di lavoro normali di proxy inverso. Una VM molto piccola potrebbe aver bisogno di meno. Un proxy edge occupato potrebbe aver bisogno di molto di più. La parte importante è che worker_connections e worker_rlimit_nofile siano allineati.
Dopo aver applicato una base, confronta le metriche prima e dopo durante traffico simile. Non giudicare una modifica di ottimizzazione da un minuto fortunato dopo il ricaricamento. Guarda la latenza p95 o p99, il tasso di errore, CPU, memoria e l'accoppiamento backend per un tempo sufficiente a vedere il comportamento reale.
Errori che Rendono Casuale l'Ottimizzazione delle Connessioni
Il primo errore è contare le richieste invece delle connessioni. Un browser può riutilizzare una connessione per più richieste, e HTTP/2 può multiplexare molte richieste su una connessione. Un client lento può anche tenere una connessione aperta mentre fa pochissimo lavoro utile. Ciò significa che "riceviamo solo 500 richieste al secondo" non ti dice quante connessioni Nginx necessita.
Il secondo errore è dimenticare le connessioni upstream. Se Nginx serve file statici, la maggior parte delle connessioni sono lato client. Se Nginx proxy verso un'app, le richieste attive spesso necessitano anche di socket backend. Se usi il keepalive verso l'upstream, alcune connessioni backend rimangono aperte per il riutilizzo. Questo è positivo, ma consuma comunque descrittori di file su entrambi i lati.
Il terzo errore è aumentare i limiti di Nginx senza controllare l'applicazione. Supponiamo che Nginx ora possa accettare 12.000 connessioni simultanee, ma l'applicazione ha 16 processi worker e un pool di database di 50 connessioni. Nginx accetterà più lavoro di quanto l'app possa completare. Gli utenti potrebbero vedere meno errori di connessione immediati, ma la latenza può peggiorare perché le richieste aspettano più a lungo nelle code.
Il quarto errore è usare timeout keepalive lunghi ovunque. Il keepalive è utile perché evita la configurazione TCP e TLS ripetuta. Ma un timeout molto lungo può lasciare molti socket inattivi aperti dopo un picco di traffico. Su un proxy edge ricco di memoria questo può andare bene. Su una VM piccola può soffocare il lavoro attivo. Se vedi un enorme conteggio Waiting e un basso riutilizzo delle richieste, prova un keepalive_timeout più breve e misura di nuovo.
Esempi di Risoluzione dei Problemi
Se il log degli errori dice worker_connections are not enough, controlla il valore configurato, il numero di worker e il limite dei file del processo:
grep -R "worker_connections\\|worker_processes\\|worker_rlimit_nofile" /etc/nginx/nginx.conf /etc/nginx/conf.d
cat /proc/$(pgrep -o nginx)/limits | grep "open files"
Il comando pgrep -o nginx di solito trova il processo Nginx più vecchio, che spesso è il master. Su alcuni sistemi potresti preferire systemctl status nginx per vedere il PID principale.
Se il log degli errori dice too many open files, non aumentare solo worker_connections. Il processo sta raggiungendo il suo limite di descrittori. Aggiungi o regola LimitNOFILE per il servizio systemd, ricarica systemd e riavvia Nginx in modo che il nuovo limite venga effettivamente applicato:
sudo systemctl edit nginx
sudo systemctl daemon-reload
sudo systemctl restart nginx
Se gli utenti vedono timeout ma Nginx ha CPU libera e nessun avviso di connessione, guarda dietro Nginx. Controlla il tempo di risposta upstream nei log di accesso. Controlla il pool di worker dell'app. Controlla le connessioni al database. Un proxy inverso può accettare traffico senza problemi mentre il vero collo di bottiglia è un backend saturo.
Se un picco causa reset di connessione prima che le richieste appaiano nei log di accesso, il problema potrebbe essere precedente alla gestione delle richieste di Nginx. Guarda le impostazioni del backlog del kernel, i log del bilanciatore di carico, le tabelle di stato del firewall e la protezione SYN flood. Nginx non può registrare una richiesta che non ha mai ricevuto.
Come Testare le Modifiche in Sicurezza
Non ottimizzare mai la produzione modificando e sperando. Prima testa la sintassi:
sudo nginx -t
Poi ricarica Nginx in modo che le connessioni attive vengano gestite con garbo:
sudo systemctl reload nginx
Controlla il log degli errori dopo ogni modifica:
sudo tail -f /var/log/nginx/error.log
Dovresti anche monitorare la latenza delle richieste, i tassi di 4xx e 5xx, le connessioni attive, CPU, memoria e il tempo di risposta upstream. Una modifica di ottimizzazione che aumenta la capacità di connessione ma aumenta la latenza dell'applicazione potrebbe non essere un vero guadagno.
Per passaggi di validazione più approfonditi, consulta test delle configurazioni Nginx.
Quando Chiamare uno Specialista
Chiama un ingegnere DevOps esperto o uno specialista di prestazioni web quando gli errori di Nginx continuano dopo l'ottimizzazione di base, quando i picchi di traffico influenzano le entrate o quando stai modificando le impostazioni di rete del kernel su un sistema di produzione. Lo stesso vale se stai ottimizzando Nginx davanti a flussi di pagamento, sistemi di login o API critiche.
L'aiuto professionale è utile anche quando il collo di bottiglia non è chiaro. Nginx potrebbe sembrare il problema quando il vero problema è un database lento, un pool di app upstream esaurito, un livello di terminazione TLS sovraccarico o una mancata corrispondenza del timeout del bilanciatore di carico.
Il punto chiave è semplice: ottimizza i processi worker per abbinare la CPU, ottimizza le connessioni worker per abbinare la concorrenza e assicurati che i limiti dei file Linux supportino entrambi. Cambia un livello alla volta, testa la configurazione prima di ricaricare e misura il traffico reale invece di fidarti dei massimi teorici.