nginx tuning: оптимизация для высоких нагрузок на практике
Дефолтный nginx обрабатывает тысячи RPS. Но когда трафик растёт, дефолты мешают: соединения к upstream пересоздаются на каждый запрос, буферы маленькие, сжатие отключено. Разберём параметры, которые дают 3-5x прирост без замены железа.
worker_processes и worker_connections
Два параметра, определяющие потолок concurrency:
worker_processes auto; # = количество CPU ядер
worker_rlimit_nofile 65535;
events {
worker_connections 16384;
multi_accept on;
use epoll;
}
Формула: worker_processes × worker_connections = максимум одновременных соединений. При 4 ядрах и 16384 — до 65536. worker_rlimit_nofile должен быть больше worker_connections, потому что каждое проксированное соединение — 2 дескриптора (клиент + upstream). Проверьте системный лимит через ulimit -n и при необходимости поднимите в /etc/security/limits.conf.
Буферы: proxy_buffer_size, client_body_buffer_size
Если буфер мал — данные сбрасываются на диск, что убивает latency:
http {
proxy_buffer_size 16k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
client_body_buffer_size 16k;
client_max_body_size 10m;
large_client_header_buffers 4 16k;
}
Как подбирать:
- API с JSON до 32KB — дефолты достаточны
- Ответы 100KB+ (отчёты, списки) — увеличьте
proxy_buffersдо16 64k - Большие cookies/JWT — поднимите
large_client_header_buffers - Загрузка файлов —
client_body_buffer_size= типичный размер файла
Keepalive к upstream
По умолчанию nginx открывает новое TCP-соединение к backend на каждый запрос. При 10k RPS — 10k handshake в секунду:
upstream backend {
server 10.0.1.10:8080;
server 10.0.1.11:8080;
keepalive 64;
keepalive_requests 1000;
keepalive_timeout 60s;
}
server {
location /api/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
}
}
keepalive 64 — пул idle-соединений на worker. Обязательно proxy_http_version 1.1 и пустой Connection — без этого keepalive не заработает. При 4 workers и keepalive 64 — до 256 постоянных соединений к backend.
Gzip vs Brotli
Сжатие уменьшает трафик на 60-80% для текстовых ответов:
gzip on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_vary on;
gzip_proxied any;
gzip_types text/plain text/css text/javascript
application/json application/javascript
application/xml image/svg+xml;
# Brotli (модуль ngx_brotli)
brotli on;
brotli_comp_level 4;
brotli_types text/plain text/css application/json
application/javascript image/svg+xml;
Brotli даёт на 15-25% лучшее сжатие, но работает только через HTTPS. Стратегия: brotli для браузеров, gzip как fallback. gzip_comp_level 4 — оптимум: уровни 6-9 дают +2-3% сжатия при двойном расходе CPU.
Кэширование: proxy_cache
Кэш снимает нагрузку с backend для повторяющихся запросов:
proxy_cache_path /var/cache/nginx
levels=1:2
keys_zone=app_cache:32m
max_size=1g
inactive=10m
use_temp_path=off;
server {
location /api/catalog {
proxy_pass http://backend;
proxy_cache app_cache;
proxy_cache_key "$request_method|$uri|$args";
proxy_cache_valid 200 5m;
proxy_cache_valid 404 1m;
proxy_cache_bypass $http_x_no_cache;
proxy_cache_use_stale error timeout http_502 http_503;
add_header X-Cache-Status $upstream_cache_status;
}
}
X-Cache-Status показывает HIT/MISS/EXPIRED — незаменим для отладки. proxy_cache_use_stale — если backend упал, отдаём устаревший кэш вместо 502.
Мониторинг: stub_status, логи latency, open_file_cache
Без метрик оптимизация — гадание:
# Базовые счётчики
location /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
}
# Логи с latency
log_format perf '$remote_addr $request_method $uri '
'status=$status rt=$request_time '
'urt=$upstream_response_time '
'cs=$upstream_cache_status';
access_log /var/log/nginx/perf.log perf;
# Кэш метаданных файлов
open_file_cache max=10000 inactive=60s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
$upstream_response_time — время ответа backend. $request_time — полное время запроса. Разница — overhead nginx (обычно <1ms). open_file_cache убирает лишние stat() для серверов со статикой.
Чек-лист перед нагрузочным тестом:
- worker_connections × workers > ожидаемый concurrent
- keepalive к upstream включён, proxy_http_version 1.1
- gzip/brotli для text/* и application/json
- proxy_cache для идемпотентных GET-запросов
- open_file_cache для статики
- логи с $request_time и $upstream_response_time
Четыре рычага — keepalive, буферы, сжатие, кэш — дают кратный прирост throughput. Начните с логов latency, найдите узкое место, крутите один параметр за раз и замеряйте через wrk или vegeta.