Как справиться с лог-штормом “Too Many Open Files”
Если ваш HTTP-сервер встречает вас ошибкой 599: Network Connection Timed out, значит, дела так себе. Первым делом стоит проверить, вообще ли сервер доступен и можете ли вы зайти на него по SSH. А дальше в бой вступает его лог-файл. Скорее всего, там вы увидите что-то в духе:
HTTP: Accept error: accept tcp [::]:<port_number>: accept4: too many open files
Можно задуматься: какое отношение “слишком много открытых файлов” имеет к ошибке accept в HTTP? На самом деле объяснение довольно занятное. В Debian каждый сокет считается открытым файлом. Вот вам и связь! Поскольку каждый сокет — это файл, а сервер превысил лимит на количество открытых файлов, он просто не в состоянии принимать новые TCP-подключения.
# команда, которая считает количество открытых файлов сервисом lsof -u <user-running-the-service> | wc -l
Если это продовый сервер и вы не до конца понимаете, что происходит, кроме того, что очевидно слишком много открытых соединений, — быстрый и не самый изящный способ временно решить проблему — перезапустить сервис. Поскольку это TCP-сервер, все существующие соединения закроются, и активные клиенты попытаются переподключиться. Такой рестарт очищает “зависшие” сокеты, которые почему-то не были освобождены. Но это лишь временная мера. В этой заметке как раз речь пойдёт о том, как всё исправить по-настоящему.
Как это вообще исправить?
Короткий ответ — поднять лимит открытых файлов для сервиса с помощью ulimit. А подробности — ниже.
Постоянное решение состоит из двух частей:
- Убедиться, что сервер поддерживает большее число открытых файлов.
- Проследить, чтобы сервер корректно очищал “протухшие” сокеты.
Первая часть почти наверняка избавит вас от ошибок 599. А вторая нужна, чтобы количество открытых сокетов не росло бесконтрольно из-за того, что сервер не закрывает их после использования. Этот второй пункт уже зависит от конкретной реализации сервера и никак не привязан к настройкам системы.
Что такое ulimit
Как гласит man:
“The ulimit builtin is used to set the resource usage limits of the
shell and any processes spawned by it.”
По сути, это команда, которая задаёт ограничения на использование ресурсов в рамках сессии: количество процессов на пользователя, загрузку CPU, лимит открытых файловых дескрипторов и прочее.
Проверяем текущие лимиты
Есть несколько способов узнать, какие лимиты сейчас действуют.
Проверить лимиты конкретного процесса
1.Найдите его PID:
ps -afx | grep <process_name>
2.Возьмите PID из вывода и запустите:
cat /proc/<PID>/limits
Примерный вывод:
Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 15671 15671 processes Max open files 1024 524288 files Max locked memory 65536 65536 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 15671 15671 signals Max msgqueue size 819200 819200 bytes Max nice priority 0 0 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us
Этот файл перечисляет все лимиты, которые нам могут быть интересны. В контексте ошибки выше важен Max open files.
Проверить лимит в текущей оболочке
Если вы вошли на сервер по терминалу и хотите посмотреть лимиты для пользователя, пригодится команда:
ulimit -a
Пример:
core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 15671 max locked memory (kbytes, -l) 65536 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 15671 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
Что такое Soft Limit и Hard Limit?
У каждого ресурсного ограничения есть две границы:
Hard Limit. Это верхняя граница для soft limit. Значение soft limit не может её превышать. И если hard limit уже установлен для пользователя, изменить его нельзя.
Soft Limit. Это ограничение ресурса, которое задаётся пользователю или программе внутри сессии. Пользователь или процесс не могут выйти за пределы своего soft limit. Именно этот лимит и нужно увеличить, чтобы устранить ошибку.
Настройка ресурсных лимитов в bash-скриптах — собственно, фикc
В качестве примера здесь используется run-скрипт для сервиса под runit. Если вы не знакомы с runit, автор отсылает к своей отдельной заметке — но для понимания примера это не критично.
Чтобы задать лимит на количество открытых файлов для сервиса runit, достаточно вызвать ulimit перед запуском бинаря, который обслуживает сам сервис. Пример скрипта:
# /etc/service/dhcp-client/run
#!/bin/sh
exec 2>&1 # redirect output of stderr to stdout
ulimit -n 409600
exec /sbin/dhclient -4 -v -i -d --no-pid \
-lf /var/lib/dhcp/dhclient.eth1.leases \
-I eth1
Когда вы выставляете ulimit в большее значение, увеличивается число доступных открытых файлов — а значит, и лимит открытых сокетов растёт.
Если сервер запускается под отдельным системным пользователем, можно поднять лимит для него вручную:
su <user> — shell /bin/bash — command "ulimit -n <limit>"
Если же нужно задать общий лимит для пользователя или группы по умолчанию, правят файл /etc/security/limits.conf:
$ cat /etc/security/limits.conf # <domain> <type> <item> <value> * soft core <value> * hard nofile <value> @<group> hard nproc <value> <user> soft nproc <value>
Такой подход даёт глобальные настройки, которые будут применяться при следующем входе пользователя в систему.
Основные выводы:
- Soft limit — это фактический лимит, который действует для сессии или процесса.
- Soft limit не может быть выше, чем соответствующий hard limit.
- Общие лимиты для пользователя, группы или даже для всех сразу можно задать в
/etc/security/limits.conf. - Hard limit, однажды установленный для пользователя, можно только снизить — поднять его обратно уже нельзя.
Надеюсь, эта заметка оказалась для вас такой же полезной, как автору было интересно её писать. Если захотите что-то обсудить — пишите.