Мониторинг VPN-сервера без ночных алертов: как узлы чинят себя сами через panel API
Мониторинг VPN-сервера не обязан будить ночью. Self-healing собирается из трёх слоёв: systemd с Restart=on-failure поднимает упавший процесс, watchdog ловит зависшие, а внешний healthcheck дёргает panel API и переподключает отвалившийся узел. Человек разбирает только то, что автоматика не осилила, и читает утренний дайджест.
Мониторинг VPN-сервера обычно значит ночные алерты. Разбираю self-healing по слоям: systemd, watchdog и panel API сами перезапускают упавшие узлы, плюс ловушки статусов панели.

Мониторинг VPN-сервера у меня раньше выглядел так: телефон вибрирует в 04:12, я одним глазом читаю "node down", лезу под одеялом в панель, тыкаю "перезапустить", и через минуту всё зелёное. Проблема не в минуте работы, а в том, что я после этого час не могу уснуть. Так вот, за последнее время я перевёл узлы на self-healing: теперь они чинят себя сами, а я утром читаю отчёт вместо ночного алерта. Ниже - как это устроено по слоям и где меня чуть не сожрали ловушки статусов панели.
Сразу оговорюсь честно: часть цифр по своему стенду я ещё сверяю и не хочу светить лишнего про инфру, поэтому конкретику по количеству узлов и точным метрикам оставляю за скобками. А вот механика восстановления - штука общая, её и разберём.
Что такое self-healing инфраструктура простыми словами
Self-healing инфраструктура - это система, которая сама замечает падение сервиса и восстанавливает его без человека. Вместо цепочки "упало - алерт - разбудили инженера - руками перезапустил" отказ обрабатывается автоматически: демон видит, что процесс умер, и поднимает его заново. Человек нужен только когда автоматика сдалась, а не на каждый чих.
Идея не новая - в SRE это называют auto-remediation и считают главным способом снизить toil, ту самую рутину, которая жрёт время и нервы дежурного. По материалам про on-call за 2026 год (rootly, incident.io) ночные пейджи не просто портят сон: от постоянного потока алертов инженер привыкает к ним и рискует пропустить действительно важный сигнал. Поэтому чем больше типовых отказов чинится само, тем надёжнее реагируешь на нетиповые.
Для VPN-узла "починить себя" распадается на три уровня, и они не заменяют, а дополняют друг друга: перезапуск процесса, отлов зависаний и оживление всей ноды через panel API. Пойдём снизу вверх.
Как автоматически перезапустить упавший сервис через systemd
Самый дешёвый уровень автовосстановления - это Restart=on-failure в systemd-юните. С ним systemd сам поднимает процесс, если тот вышел с ненулевым кодом или его прибил сигнал. Ноль настроек мониторинга, ноль скриптов: демон, который и так следит за сервисом, просто не даёт ему остаться лежать.
Юнит для xray или любого VPN-демона в минимальном виде выглядит так:
[Service]
Restart=on-failure
RestartSec=5
StartLimitIntervalSec=60
StartLimitBurst=5RestartSec=5 - это пауза перед перезапуском, чтобы не молотить рестартами в цикле. А вот следующие две строки важнее, чем кажется. StartLimitBurst задаёт, сколько раз можно перезапуститься за окно StartLimitIntervalSec, и если сервис падает чаще - systemd перестаёт его поднимать и оставляет в состоянии failed. По умолчанию порог - 5 рестартов: как пишет freshman.tech, "если сервис падает больше пяти раз, ему уже не дадут стартовать".
Звучит контринтуитивно: мы же хотим, чтобы чинилось само, зачем стоп-кран? Затем, что бесконечный рестарт-луп - это не self-healing, а маскировка бага. Если xray падает из-за битого конфига, systemd будет героически поднимать его 24/7, панель будет мигать, а реальная причина спрячется за зелёным статусом. Стоп-лимит - это способ сказать "я сдался, зови человека", и это правильное поведение.
Когда systemd не спасёт: watchdog для зависших процессов
Обычный Restart=on-failure ловит только смерть процесса. Но VPN-демон умеет зависать живым: процесс есть, порт слушает, а трафик не ходит. Тут exit-based рестарт бесполезен - формально всё живо. Для таких случаев в systemd есть watchdog.
Watchdog в systemd - это liveness-проверка: сервис обязан периодически слать systemd сигнал WATCHDOG=1 через sd_notify, и если за интервал WatchdogSec сигнала не было, systemd считает процесс зависшим, убивает его и перезапускает. Рекомендуют слать пинг примерно раз в половину WatchdogSec, чтобы был запас по времени. В юните это WatchdogSec=30s плюс Restart=on-watchdog (или on-failure, который покрывает и watchdog-таймаут, и обычные падения).
Есть нюанс: watchdog требует, чтобы сам демон умел слать heartbeat. Не все умеют. Обходной путь - обёртка вроде healthdog, которая гоняет внешний healthcheck-скрипт и сама "гладит" systemd-watchdog за приложение, не трогая его код. То есть healthcheck можно навесить снаружи: пингуем реальный VPN-порт, ходит трафик - гладим watchdog, не ходит - молчим, и systemd перезапускает ноду. Это уже мостик к следующему уровню.
Мониторинг VPN-сервера и panel API: оживляем всю ноду
Systemd чинит процесс на одной машине, но VPN обычно живёт как связка "центральная панель + несколько узлов", и падать может связь между ними. Здесь в игру входит panel API automation: внешний healthcheck ходит по HTTP в API панели, проверяет статус узла и, если тот отвалился, дёргает эндпоинт переподключения - оживляет всю ноду, а не только процесс.
Возьмём Marzban - популярную опенсорсную панель для управления VPN на базе Xray-core (VLESS, VMess, Trojan, Reality). Marzban умеет мультисервер: одна панель рулит несколькими узлами-нодами. Ноды общаются с панелью по REST (включается параметром SERVICE_PROTOCOL: "rest" в версиях 0.4.4 и выше), и панель отслеживает их статус. Значит, всё это можно автоматизировать снаружи: скрипт по крону раз в минуту опрашивает API, видит "нода не подключена" - и вызывает переподключение, не дожидаясь, пока я проснусь.
Схема автовосстановления сервера получается многослойной:
healthcheck по крону -> API панели: статус узла?
узел up -> тихо выходим
узел down -> вызвать reconnect ноды через API
API молчит -> systemd уже перезапускает панель локально
Каждый слой ловит свой класс отказов: systemd - смерть процесса, watchdog - зависание, panel API - разрыв связи панель-нода. Вместе они закрывают процентов девяносто типовых ночных инцидентов, ради которых меня раньше будили. Кстати, весь этот парк скриптов и крон-обёрток у меня живёт прямо на VPS - про то, почему мои агенты и автоматизация переехали на сервер, я писал в отдельной статье, а про перенос части крона в облачные routines - вот тут.
Какие ловушки у статусов панели при авто-восстановлении
Главная ловушка - доверять статусу панели как истине. Панель показывает "нода up", потому что процесс запущен и хендшейк прошёл, но это не значит, что через неё ходит трафик. У того же Marzban-node есть известная болячка с постоянно перезапускающимся Xray на узле (issue #75 в репозитории): процесс флапает, панель при этом может показывать бодрое зелёное. Если твой авто-restart смотрит только на флажок панели, ты будешь "чинить" то, что и так формально живо, и не заметишь настоящую поломку.
Отсюда правила, которые я вынес:
- Проверять не статус в панели, а реальный симптом - что через порт узла реально идёт трафик. Флажок в UI это производная, а не диагноз.
- Держать стоп-лимит на рестартах (тот самый
StartLimitBurst). Флапающий узел, который перезапускается по кругу, надо не лечить, а эскалировать человеку. - Логировать каждое авто-восстановление. Если скрипт молча оживляет одну и ту же ноду по десять раз за ночь, это не успех автоматики - это симптом, который автоматика прячет.
- Не глушить алерты полностью. Self-healing не отменяет мониторинг, он меняет его роль: не "разбуди меня", а "покажи утром, что чинилось и как часто".
Последний пункт - самый неочевидный. Соблазн после автоматизации выключить все уведомления огромный, но тогда деградация накапливается втихую. Правильнее оставить дайджест: список того, что за ночь упало и поднялось само. Если в нём каждый день один и тот же узел - это уже не "чинит себя сам", а сломанная железка, которой нужен ты.
Тишина вместо ночных алертов - и что осталось человеку
Итог простой: мониторинг VPN-сервера не обязан будить тебя ночью. Три слоя - systemd для смерти процесса, watchdog для зависаний, panel API для разрыва связи - закрывают большинство типовых падений сами. Человек остаётся на верхнем этаже: разбирать то, что автоматика не осилила, и читать дайджест, чтобы поймать медленную деградацию.
Не буду врать, что это волшебная кнопка. Self-healing легко превращается в машину по сокрытию багов, если забыть про стоп-лимиты и проверять статус вместо симптома. Но когда слои настроены честно, ночные пейджи из потока превращаются в редкое исключение - а именно на исключениях и надо быть свежим, а не выжатым после десятого подъёма за неделю. Мне это вернуло сон, а инфре - предсказуемость. Такой размен я подписываю не глядя.
Частые вопросы
Опасно ли ставить Restart=always для VPN-сервиса?
Да, если без ограничителя. Restart=always поднимает процесс даже при битом конфиге и уходит в бесконечный цикл. Ставьте StartLimitBurst и StartLimitIntervalSec: после нескольких неудачных рестартов подряд (по умолчанию около 5) systemd оставит сервис в failed и позовёт человека - это лучше, чем маскировать баг.
Чем watchdog в systemd отличается от Restart=on-failure?
Restart=on-failure реагирует только на смерть процесса - ненулевой код выхода или сигнал. Watchdog ловит зависшие процессы, которые формально живы: сервис шлёт systemd сигнал WATCHDOG=1 через sd_notify, и если пинг не пришёл за WatchdogSec, systemd перезапускает его сам.
Можно ли полностью отключить алерты после настройки self-healing?
Нет. Self-healing не отменяет мониторинг, а меняет его роль: вместо ночного пейджа - утренний дайджест того, что упало и поднялось само. Если один и тот же узел чинится каждую ночь, это сломанная железка, а не успех автоматики.
