Что было. Публичный сервис раздачи статики около тысячи запросов в секунду. В пике в error.log: open() failed (24: Too many open files). Хуже того, try_files расценил невозможность открыть файл как его отсутствие и отдал пустой ответ, который CDN закэшировала на долгий срок. У одного рабочего процесса оказалось около 65 тысяч открытых дескрипторов при заявленном worker_rlimit_nofile 300000: лимит на уровне ОС был ниже заявленного в nginx.
Откуда берётся ограничение. Каждое соединение занимает минимум два дескриптора (клиентский сокет и файл или соединение с бэкендом), плюс open_file_cache держит открытые файлы. Лимиты стоят на трёх уровнях, и работает самый низкий:
- Ядро:
fs.file-max(общесистемный максимум) иfs.nr_open(максимум на процесс). - Процесс:
ulimit -nдля пользователя nginx. Для сервиса под systemd этоLimitNOFILE=в юните, а не/etc/security/limits.conf, который на systemd-службы не действует. - nginx:
worker_rlimit_nofile(сколько сам nginx попросит у ОС) иworker_connections.
Как настроить.
# /etc/systemd/system/nginx.service.d/override.conf
[Service]
LimitNOFILE=65536
systemctl daemon-reload && systemctl restart nginx (не reload: лимит применяется к новому процессу).
# nginx.conf
worker_processes auto;
worker_rlimit_nofile 65536;
events { worker_connections 16384; }
Правило из обсуждения: worker_rlimit_nofile примерно в два раза больше worker_connections. Проверка фактического лимита: cat /proc/$(pgrep -o nginx)/limits | grep "open files" для мастера и для рабочего процесса.
Про кэш открытых файлов. open_file_cache max=1000000 на миллион записей был частью проблемы: кэш сам держит дескрипторы. Разумно max=10000 inactive=30s и подбирать по замерам. open_file_cache_errors on безопасен: он кэширует только «файл не найден», а не системные ошибки.
Чтобы ошибка не превращалась в пустой 200. В обсуждении предложили не полагаться на try_files для различения «нет файла» и «система не смогла открыть»: при системной ошибке возвращать 503 с Cache-Control: no-store, чтобы CDN не запомнила мусор. Простейший вариант: error_page 500 502 503 504 /50x.html; с add_header Cache-Control no-store always; в этом location.
Признаки заранее. Растущее число дескрипторов у рабочих процессов (ls /proc/<pid>/fd | wc -l) и предупреждения worker_connections are not enough в логе. Мониторить оба.
Директива описана в документации nginx.