1. 1
  2. 2
  3. 3
  4. 4

# теория · шаг 2 из 4

Сколько воркеров ставить

Коротко

Чисел, которые реально настраивают, всего два, и оба живут в самом верху конфига, вне блока http.

  • worker_processes auto - по воркеру на ядро. Трогают руками в двух случаях: сервер делят с другими прожорливыми сервисами или nginx работает в контейнере.
  • worker_connections - сколько СОКЕТОВ держит один воркер. Не клиентов: у прокси каждый клиент занимает два, поэтому потолок вдвое ниже наивного.
  • Потолок упирается в лимит открытых файлов процесса. Два независимых ограничения, поднимать надо оба.
  • Крутить эти числа имеет смысл только после того, как увидел упёршуюся метрику. Иначе это гадание.

Если worker_connections и nofile - знакомая пара, листай до «Теперь сам».

Сколько клиентов выдержит вот этот конфиг

/etc/nginx/nginx.confглавный контекст
worker_processes 4;

events {
    worker_connections 4096;
}

Наивный ответ: 4 × 4096 = 16 384 клиента.

Правильный ответ: зависит от того, чем занят nginx. Если он отдаёт файлы с диска - да, около шестнадцати тысяч. Если он работает обратным прокси - вдвое меньше, примерно восемь тысяч. Почему так - дальше.

На что это похоже

Call-центр, у оператора на пульте сорок линий.

Пока оператор просто отвечает на вопросы, сорок линий - это сорок клиентов. Но если по каждому звонку он сам набирает бухгалтерию и держит обе трубки, то на одного клиента уходит две линии, и клиентов помещается двадцать.

worker_connections - это линии на пульте, а не люди на другом конце. Считать надо соединения, и в режиме прокси их на каждого клиента два: одно с клиентом, второе с бэкендом.

Механизм: что считается соединением

Слот worker_connections тратится не только на клиентов. В счёт идут:

  • соединение с клиентом - всегда;
  • соединение с бэкендом - когда nginx проксирует;
  • слушающие сокеты (listen) - по одному на каждый, немного, но они есть;
  • соединения, которые клиент держит открытыми после ответа (keepalive), - они ещё живы и слот занимают.

Последний пункт неочевиден и на практике съедает больше всего: браузер удерживает соединение секунды после загрузки страницы, чтобы не устанавливать его заново.

Правило

worker_processes auto и worker_connections в несколько тысяч закрывают почти все случаи.

Потолок клиентов - это worker_processes × worker_connections, делённое на два, если nginx проксирует.

Числа трогают после метрики, а не до. Если active connections болтается на сотне при потолке в шестнадцать тысяч, тюнинг этих строк не даст ничего, и проблема где-то ещё.

Разбор: считаем потолок целиком

Возьмём боевой случай: nginx проксирует магазин, worker_processes auto на 8-ядерной машине, worker_connections 4096.

слотов всего:      8 × 4096 = 32 768
на клиента нужно:  2 (клиент + бэкенд)
потолок клиентов:  32 768 ÷ 2 = 16 384

Теперь второе ограничение. Каждое соединение - это открытый файловый дескриптор, а их число ограничено на процесс:

$  команда
cat /proc/$(pgrep -f 'nginx: worker' | head -1)/limits | grep 'open files'
↳  выводcat /proc/PID/limits | grep 'open files'
Max open files            1024                 1024                 files

Тысяча двадцать четыре против четырёх тысяч заявленных. То есть настоящий потолок здесь - не 4096 на воркер, а 1024, и при подходе к нему в error_log появится:

worker_connections exceed open file resource limit: 1024

Лимит поднимается в двух местах, и они независимы:

/etc/nginx/nginx.confглавный контекст
worker_rlimit_nofile 8192;

плюс LimitNOFILE=8192 в юните systemd. Поднять только одно - значит не поднять ничего: systemd режет процесс раньше, чем nginx успевает попросить.

Что такое юнит systemd

Юнит systemd - текстовый файл, описывающий службу: чем её запускать, когда перезапускать и какие лимиты ядра ей выставить. Юнит nginx лежит в /lib/systemd/system/nginx.service, но править его руками не надо - обновление пакета перезапишет файл. Свои строки кладут отдельным довеском:

$  команда
sudo systemctl edit nginx

Команда открывает пустой файл /etc/systemd/system/nginx.service.d/override.conf, куда и пишется добавка:

/etc/systemd/system/nginx.service.d/override.confглавный
[Service]
LimitNOFILE=8192

После правки нужен sudo systemctl daemon-reload и полный sudo systemctl restart nginx: лимиты ядра выдаются процессу при старте, и reload их не меняет.

Разбор: где формула врёт

Из первого разбора легко унести «auto всегда правильно». Вот случай, где auto даёт заведомо плохое число.

nginx запущен в контейнере с ограничением в половину ядра на машине с 64 ядрами. auto считает ядра хоста, потому что видит их в /proc/cpuinfo:

worker_processes auto  →  64 воркера
доступно процессорного времени  →  0,5 ядра

Шестьдесят четыре процесса делят половину ядра. Каждый со своим набором соединений, каждому нужна память под буферы, и все они переключаются друг с другом. Итог хуже, чем от одного воркера.

Проверить, что видит контейнер, можно прямо из него:

$  команда
docker exec lab nproc
docker exec lab cat /sys/fs/cgroup/cpu.max

Если nproc показывает 64, а cpu.max говорит 50000 100000 (то есть полъядра), число воркеров надо ставить руками. Свежие сборки nginx умеют смотреть на квоту cgroup, но полагаться на это без проверки не стоит: слишком многое зависит от версии и от того, как запущен контейнер.

Как понять, что упёрся

Включи страницу статуса:

/etc/nginx/conf.d/shop.local.confhttp server
location = /nginx_status {
    stub_status;
    allow 127.0.0.1;
    deny all;
}
$  команда
curl -s http://127.0.0.1/nginx_status
↳  выводcurl -s http://127.0.0.1/nginx_status
Active connections: 291
server accepts handled requests
 16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106

Смотреть надо на две вещи. Active connections против потолка - если подобралось, пора поднимать. И второе число в средней строке (handled) против первого (accepts): если handled меньше, значит соединения отбрасывались, и это и есть упёршийся потолок.

Что ломается без этого

worker_connections are not enough в error_log - слоты кончились. Клиенты в этот момент получают отказ или таймаут.

too many open files - кончились дескрипторы. То же самое, но упёрлось раньше, в системный лимит.

«В контейнере nginx медленнее, чем на голой машине» - почти всегда worker_processes auto, насчитавший ядра хоста.

Зачем это в работе

Первое - разбор отказов под нагрузкой. Когда сайт начинает отбрасывать соединения, stub_status за секунду отвечает, упёрлись слоты или нет, и это разводит две совершенно разные ветки расследования: настройки nginx против проблем бэкенда.

Второе - переезд в контейнеры. Конфиг, годами работавший на железе, в контейнере с квотой ведёт себя иначе, и worker_processes auto - первое, что стоит проверить, а не последнее.

Теперь сам

nginx проксирует API. worker_processes auto на 4 ядрах, worker_connections 2048, worker_rlimit_nofile не задан, системный лимит 1024. Сколько одновременных клиентов реально выдержит сервер?

Ответ. Заявленный потолок - 4 × 2048 = 8192 слота, то есть 4096 клиентов в режиме прокси. Но каждый воркер упрётся в 1024 дескриптора раньше, чем в 2048 слотов, поэтому реально это 4 × 1024 = 4096 слотов и около двух тысяч клиентов. Причём задолго до этого числа в error_log посыплется worker_connections exceed open file resource limit. Чинится поднятием worker_rlimit_nofile И LimitNOFILE в юните systemd - одного мало.

Главное

worker_processes auto, worker_connections в несколько тысяч. Потолок клиентов - произведение этих чисел, а у прокси вдвое меньше: каждый клиент занимает два соединения. Поверх лежит второе, независимое ограничение - лимит открытых файлов, и поднимать его надо и в nginx, и в systemd. Крутить любое из этих чисел стоит только после того, как увидел упёршуюся метрику в stub_status.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.