lenec ru

← все посты

nginx tuning: оптимизация для высоких нагрузок на практике

13K

Дефолтный 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.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.