# теория · шаг 2 из 4
Сколько воркеров ставить
Коротко
Чисел, которые реально настраивают, всего два, и оба живут в самом верху
конфига, вне блока http.
worker_processes auto- по воркеру на ядро. Трогают руками в двух случаях: сервер делят с другими прожорливыми сервисами или nginx работает в контейнере.worker_connections- сколько СОКЕТОВ держит один воркер. Не клиентов: у прокси каждый клиент занимает два, поэтому потолок вдвое ниже наивного.- Потолок упирается в лимит открытых файлов процесса. Два независимых ограничения, поднимать надо оба.
- Крутить эти числа имеет смысл только после того, как увидел упёршуюся метрику. Иначе это гадание.
Если worker_connections и nofile - знакомая пара, листай до «Теперь сам».
Сколько клиентов выдержит вот этот конфиг
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'
Max open files 1024 1024 files
Тысяча двадцать четыре против четырёх тысяч заявленных. То есть настоящий
потолок здесь - не 4096 на воркер, а 1024, и при подходе к нему в error_log
появится:
worker_connections exceed open file resource limit: 1024
Лимит поднимается в двух местах, и они независимы:
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,
куда и пишется добавка:
[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, но полагаться на это без проверки не стоит: слишком
многое зависит от версии и от того, как запущен контейнер.
Как понять, что упёрся
Включи страницу статуса:
location = /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
}
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.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий