MAATRIX / Блог / Ошибка Too many open files

Ошибка Too many open files

Ошибка Too many open files

MAATRIX

В логах приложения или веб-сервера всплывает Too many open files, соединения обрываются, сервис перестаёт принимать запросы. Ошибка означает, что процесс упёрся в лимит открытых файловых дескрипторов — а в Linux дескрипторами считаются не только файлы, но и сокеты, соединения, пайпы. Ниже пошаговое решение проблемы: как правильно поднять лимит (и почему это делается в трёх местах), а затем убедиться, что дело не в утечке дескрипторов, которую простым повышением лимита не вылечить.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Быстрое решение: смотрим и поднимаем лимит

Сначала узнайте текущий лимит и то, сколько дескрипторов реально открыто. Для интерактивной сессии лимит показывает ulimit, а число открытых по системе — /proc/sys/fs/file-nr:

# мягкий и жёсткий лимит текущей сессии
ulimit -Sn
ulimit -Hn
# сколько дескрипторов открыто в системе / максимум
cat /proc/sys/fs/file-nr
# лимит и использование конкретного процесса
cat /proc/PID/limits | grep 'open files'
ls /proc/PID/fd | wc -l

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

Почему одного ulimit мало

Главная ловушка этой ошибки в том, что люди меняют ulimit в своей сессии, перезапускают сервис — и ничего не меняется. Причина: лимит применяется на разных уровнях, и для сервисов, запущенных через systemd, значение из вашего шелла вообще не действует. Нужно поднять лимит во всех местах, где он задаётся, иначе самый низкий из них и станет потолком.

Уровней три. Первый — общесистемный максимум ядра (fs.file-max в sysctl), верхняя граница для всей системы. Второй — лимиты для пользователей (/etc/security/limits.conf), применяются при входе пользователя. Третий — лимиты systemd-юнита (LimitNOFILE), именно они действуют для демонов вроде Nginx, MySQL, вашего приложения. Ошибка почти всегда в том, что подняли один уровень, а забыли про тот, что реально ограничивает сервис. Разберём каждый.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.

Заказать VPS для нагрузки

Поднимаем лимит правильно во всех местах

Начнём с общесистемного максимума и пользовательских лимитов. fs.file-max задаёт потолок для всей системы, limits.conf — для пользователей при логине:

# общесистемный максимум ядра
sysctl -w fs.file-max=2097152
echo 'fs.file-max=2097152' >> /etc/sysctl.conf
# лимиты пользователей в /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535

Теперь ключевое для сервисов — лимит в systemd. Для демона он задаётся в его юните, и именно это чаще всего забывают:

# правим юнит через override, не трогая оригинал
systemctl edit имя_сервиса
# в открывшийся файл вписать:
[Service]
LimitNOFILE=65535
# применить
systemctl daemon-reload
systemctl restart имя_сервиса
# проверить, что применилось
cat /proc/$(pgrep -f имя)/limits | grep 'open files'

Последняя команда — обязательная проверка: она показывает лимит уже работающего процесса. Если там всё ещё 1024, значит, systemd-override не применился, и искать надо здесь. Плюс у некоторых сервисов (Nginx) есть собственная директива лимита в их конфиге — её тоже стоит выставить.

Почему именно проверка на работающем процессе так важна, стоит подчеркнуть отдельно. Значения в конфигах и в limits.conf — это то, что вы задали, а /proc/PID/limits — то, что реально действует прямо сейчас для конкретного демона. Между ними бывает разрыв: забыли daemon-reload, отредактировали не тот юнит, сервис запущен не через systemd, а из скрипта со своим окружением. Пока живой процесс показывает старый лимит, любые правки в файлах бесполезны, и время уходит впустую. Поэтому золотое правило при этой ошибке — не верить конфигу на слово, а всегда сверяться с тем, что видит сам процесс, и только по этому числу судить, применилось изменение или нет.

Убеждаемся, что это не утечка

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

# наблюдаем за ростом дескрипторов процесса
watch -n 5 'ls /proc/PID/fd | wc -l'
# что именно открыто — файлы, сокеты, соединения
ls -l /proc/PID/fd | awk '{print $NF}' | sort | uniq -c | sort -rn | head

Если счётчик всё время растёт и не падает даже в затишье, а среди открытого много одинаковых сокетов или файлов — это утечка в коде: соединения к базе без закрытия, незакрытые файлы, висящие сетевые соединения. Здесь лимит не лечит, а лишь маскирует. Настоящее решение — исправить приложение, чтобы оно освобождало дескрипторы. Повышение лимита в этом случае — временная мера, дающая время на починку кода.

Типовые виновники и их настройка

Разберём, кто чаще всего упирается в лимит. Веб-серверы под нагрузкой: каждое клиентское соединение и каждый апстрим — дескриптор, при тысячах одновременных запросов дефолтных 1024 мало. У Nginx есть директива worker_connections и worker_rlimit_nofile — их поднимают вместе с системным лимитом. Базы данных: MySQL держит открытыми файлы таблиц и соединения, параметр open_files_limit надо согласовать с systemd-лимитом сервиса.

Приложения с пулами соединений тоже частые гости этой ошибки, особенно при неверно настроенном пуле, который открывает больше соединений, чем закрывает. Общий принцип: у каждого такого сервиса есть свой параметр лимита, и он не должен превышать то, что разрешено на уровне ОС и systemd. Согласуйте цепочку целиком — от ядра до конфига приложения, — чтобы узким местом не оказался забытый уровень.

Профилактика и достаточные ресурсы

Чтобы ошибка не возвращалась, заложите адекватные лимиты сразу и следите за дескрипторами. Для нагруженных сервисов поднимите nofile до десятков тысяч на всех уровнях с самого начала. Включите в мониторинг число открытых дескрипторов — рост к потолку предупредит о проблеме заранее, будь то честная нагрузка или утечка. Регулярно проверяйте, что после обновлений сервисов лимиты в юнитах сохранились.

Высокая нагрузка с тысячами соединений требует и достаточных ресурсов под ней. У MAATRIX VPS и выделенные серверы для нагруженных проектов доступны в локациях RU, US и UK, с полным root-доступом и оплатой из России картой или криптой — вы свободно настраиваете лимиты под свою задачу. Когда сервер рассчитан на нужное число соединений, а лимиты выставлены согласованно на всех уровнях, Too many open files перестаёт появляться в логах.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.

Заказать VPS для нагрузки

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Частые вопросы

Почему после изменения ulimit ничего не поменялось?

Потому что для сервисов, запущенных через systemd, значение из вашего шелла не действует. Лимит демона задаётся в его юните через LimitNOFILE. Меняйте его там (systemctl edit), затем daemon-reload и рестарт сервиса.

В каких местах поднимать лимит?

В трёх: общесистемный fs.file-max (sysctl), пользовательский в /etc/security/limits.conf и LimitNOFILE в systemd-юните сервиса. Действует самый низкий из них, поэтому поднимать нужно все и проверять по /proc/PID/limits.

Как понять, что это утечка, а не нагрузка?

Наблюдайте за числом открытых дескрипторов процесса (ls /proc/PID/fd | wc -l). При честной нагрузке оно колеблется с трафиком, при утечке — монотонно растёт и не падает в затишье. Утечку лечит только исправление кода.

Что считается открытым файлом в Linux?

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

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.