- В 99% случаев возникает при большом количестве соединений, запросов к статическим файлам и кэшированных объектов.
- Поэтому 99% процентов решений в интернете сводятся к увеличению значения worker_rlimit_nofile в nginx.conf.
- Тем неожиданнее для нас было столкнуться с этой ошибкой, имея в worker_rlimit_nofile значение, близкое к семизначному.
- Одновременно (или даже раньше) выявилась другая проблема: nginx -s reload перестал перечитывать конфигурацию.
Как мы разбирались:
- В первой консоли включили протоколирование системных вызовов, которые выполняет мастер-процесс Nginx'a:
strace -tt -o/tmp/strace-nginx-master.log -p $(pgrep -f 'nginx: master')
nginx -s reload
Что мы увидели:
- Мастер-процесс успешно получает сигнал и открывает /etc/nginx/nginx.conf.
- Затем он начинает по очереди открывать файлы, на которые nginx.conf ссылается через include.
- Открытие файлов успешно происходит до тех пор, пока не открывается файл с дескриптором 1023.
- Следующий за ним файл не открывается: open возвращает ошибку EMFILE (Too many open files).
- Эту ошибку Nginx записывает в журнал и перечитывание настроек прекращает.
Проверяем файловые лимиты Nginx'a:
for pid in $(pgrep nginx); do
cat "/proc/$pid/cmdline"
echo
egrep --color=never 'files|Limit' "/proc/$pid/limits";
echo -n "Currently open files: "
ls -1 "/proc/$pid/fd" | wc -l
echo
done
Результат:
- Все процессы nginx, КРОМЕ мастера (воркеры и кэш-менеджер), имеют soft и hard limit, равные указанному в worker_rlimit_nofile.
- Мастер-процесс имеет soft limit = 1k, hard limit = 4k.
Вывод:
- Мастер-процесс настраивает лимиты для подчинённых процессов, но не для себя (сапожник без сапог).
- Назначаемый ему лимит стал срабатывать, когда количество проектов на сервере приблизилось к тысяче (для каждого проекта deploy-система создаёт отдельный файл в /etc/nginx/sites-available).
- Таким образом, осталось найти место, где этот лимит может быть задан.
Решение:
- Классический способ через * - nofile unlimited (экстремальный вариант для теста, в production так не пишите!) в /etc/secutity/limits.conf не сработал. Почему? Потому что systemd.
- Ищем и находим новый, единственно правильный способ в https://www.freedesktop.org/software/systemd/man/systemd.exec.html
- Создаём файл /etc/systemd/system/nginx.service.d/Ulimit.conf со следующим содержимым:
[Service] LimitNOFILE=16384
systemctl daemon-reload
systemctl restart nginx
- Снова проверяем файловые лимиты мастер-процесса и видим, что они увеличились.
- Делаем nginx -s и получаем перечитывание настроек без ошибок.