Ошибка “Too many open files” в Nginx

26 декабря 2018

  • В 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” и получаем перечитывание настроек без ошибок.

← Назад в Блог