VPN-релей перестаёт принимать SSH: лимиты файловых дескрипторов
Трафик через туннель продолжается, а socat-релей за ним отказывает в новых сеансах. Проследите путь, затем прочитайте унаследованный лимит на живом PID.
Рукопожатие WireGuard всё ещё завершается, peer всё ещё показывает недавний endpoint, и всё же новый сеанс SSH через релей за ним висит без баннера. Это сочетание уводит от туннеля к процессу, который принимает socket. Релей вроде socat стоит на пути приложения со своим бюджетом дескрипторов, своими дочерними процессами и своими унаследованными лимитами. Прогулка ниже начинается у клиента и останавливается на первом компоненте, который не может открыть ещё один socket.
Проследите путь, прежде чем менять число
Проследите один неудачный запрос вместо рассуждений о туннеле целиком. Клиент пишет в туннель, демон VPN расшифровывает и маршрутизирует, а релей принимает на другой стороне. Каждый шаг расходует дескрипторы, и тот, который обычно заканчивается, — последний.
Начните с запроса, который вы можете воспроизвести, и измерьте его время. Если рукопожатие TCP завершается, но баннер приложения не приходит, релей принял socket и затем застрял в дочернем процессе или на своей восходящей ноге. Если само рукопожатие никогда не завершается, причина — маршрутизация, брандмауэр или состояние слушателя, а не потолок дескрипторов. Запишите, какую из двух форм вы наблюдали, потому что они указывают на разные процессы.
Посчитайте и прыжки, которые дублируют работу. Разветвляющийся слушатель передаёт каждый принятый socket дочернему процессу, который хранит свою копию, поэтому реальная сумма — свойство всего дерева, а не только родителя.
Прочитайте лимит, который унаследовал релей
Единственный важный лимит принадлежит работающему процессу, и его задало то, что запустило этот процесс. Ввод ulimit -n в оболочке описывает оболочку. Спросите менеджер служб, что он применил, затем подтвердите на PID.
systemctl show -p MainPID -p LimitNOFILE vpn-relay.service
cat /proc/$(systemctl show -p MainPID --value vpn-relay.service)/limits
Файл limits печатает мягкий и жёсткий потолки так, как их видит процесс. Низкое мягкое значение под щедрым жёстким означает, что демону разрешено поднять собственный потолок, и некоторые делают это при запуске; равные низкие значения означают, что источник — файл unit.
Drop-in загружаются в лексикографическом порядке, и более поздние файлы побеждают, поэтому systemctl cat vpn-relay.service — авторитетный вид того, что применяется. Редактирование файла, который менеджер никогда не читает, — самая частая причина того, что поднятое число не даёт эффекта.
Отделите устойчивую параллельность от удерживаемого состояния
Два процесса могут упереться в один потолок по несвязанным причинам, и средства различаются. Спрос, отслеживающий полезные параллельные сеансы, — вопрос ёмкости. Рост, который продолжается после окончания работы, — утечка или набор сеансов, которые никто не закрыл.
Измерьте в покое, на обычном пике и ещё раз через несколько минут после остановки нагрузки. Подсчёт открытых записей с ls /proc/PID/fd | wc -l против мягкого лимита покажет разрыв, а число на единицу-две ниже потолка — это жёсткая остановка, а не медленный путь.
Когда число не хочет падать, смотрите на состояния socket, а не на суммы. Дескрипторы в CLOSE_WAIT означают, что peer закрыл, а ваша сторона — никогда, и это дефект жизненного цикла приложения, который больший лимит лишь откладывает. Ограничьте и модель детей: разветвляющийся релей без предела параллельных детей исчерпывает дескрипторы, слоты процессов и память вместе.
Проверьте новое значение на живом процессе
После правки unit перезагрузите менеджер и перезапустите службу, поскольку одна лишь перезагрузка никогда не заменяет лимиты, уже унаследованные работающим процессом. Проверьте на процессе, который действительно обслуживает трафик.
systemctl daemon-reload
systemctl restart vpn-relay.service
awk '/Max open files/ {print}' /proc/$(systemctl show -p MainPID --value vpn-relay.service)/limits
ss -tnp state established '( sport = :2222 )' | wc -l
Строка awk возвращает новую пару; если она всё ещё показывает старую, отредактированный вами drop-in — не тот, что загружается. Число ss — ваша проверка нагрузки, поэтому откройте ожидаемое количество сеансов и наблюдайте за ростом. Число, которое продолжает расти после закрытия этих сеансов, означает, что лимит никогда не был всей проблемой.
С любым крупным повышением связана одна оговорка. Приложения, опрашивающие через select(), не могут следить за дескрипторами выше FD_SETSIZE, что составляет 1024 в типичных сборках, поэтому потолок, поднятый выше, может давать отказы, которые выглядят несвязанными. Справочник по выполнению systemd несёт то же предупреждение для запускаемых им служб.
Держите перезапуск подальше от доступности туннеля
Перезапуск релея сбрасывает его активные сеансы, и это отдельное событие от падения VPN. Упорядочьте изменение так, чтобы туннель оставался поднятым, пока релей проходит цикл, и предупредите каждого, чей долгий сеанс его пересекает.
Откат — это предыдущий файл unit плюс ещё один перезапуск, и этот файл остаётся на месте, пока новое значение не переживёт рабочий день. Затем замкните цикл запросом, который изначально не удался: один новый сеанс SSH через релей, одна передача, достаточно длинная, чтобы иметь значение, и число дескрипторов, которое возвращается к уровню покоя.