Когда мы только начинали делать PingZen, перед нами стоял вопрос: что должен уметь «идеальный» мониторинг? Проверять HTTP - мало. Нужны и TCP-порты, и UDP, и ICMP, и DNS, и даже проверки для cron-задач, работающих за NAT. Но самое интересное - возможность проверять не просто доступность, а реальные сценарии работы сайта: может ли пользователь залогиниться, добавить товар в корзину, отправить форму обратной связи.

Сегодня я расскажу, как мы реализовали поддержку 22 протоколов в PingZen, и покажу самую мощную фичу - Transaction (Playwright), которая умеет эмулировать поведение живого пользователя (пусть и не совсем готовую для широкого использования). А главное - объясню, зачем вообще нужно столько разных проверок и как они устроены под капотом.

Зачем так много протоколов?

Казалось бы, HTTP и пинга достаточно для 90% задач. Но остаются 10%, где стандартные подходы не работают. Вот несколько реальных кейсов от наших пользователей:

  • Системный администратор должен убедиться, что DNS-сервер отвечает корректно, а почтовый сервер принимает соединения по SMTP.

  • Владелец интернет-магазина хочет проверять не только открытие главной страницы, но и возможность оформить заказ.

  • DevOps-инженер мониторит cron-задачи, которые работают за NAT и не имеют публичного IP.

Мы решили, что PingZen должен закрывать все эти сценарии «из коробки». В итоге получилось 22 протокола, и их количество растёт по мере появления запросов от пользователей.

22 протокола
22 протокола

Архитектура: как мы добавляем новые протоколы

В основе PingZen лежит модульная система «проверяльщиков» (checkers). Каждый протокол - это отдельный класс, реализующий общий интерфейс BaseHealthChecker. Вот как это выглядит в коде (упрощённо):

# base_health_checker.py
class BaseHealthChecker(ABC):
    @abstractmethod
    async def check(self, monitor: Monitor) -> CheckResult:
        pass

# http_checker.py
class HttpChecker(BaseHealthChecker):
    async def check(self, monitor: Monitor) -> CheckResult:
        # асинхронный HTTP-запрос (используем httpx)
        ...

# tcp_checker.py
class TcpChecker(BaseHealthChecker):
    async def check(self, monitor: Monitor) -> CheckResult:
        # попытка открыть TCP-соед��нение через asyncio.open_connection
        ...

Объект monitor содержит цель (хост, порт, URL), таймаут, параметры проверки и т.д. Это позволяет передавать в чекер всю необходимую информацию.

Благодаря единому интерфейсу легко добавлять новые протоколы - просто пишем класс и регистрируем его в фабрике. При этом все проверки выполняются асинхронно (asyncio), поэтому мы можем одновременно запускать сотни проверок без создания лишних потоков.

Все результаты проверок пишутся в PostgreSQL (основная логика) и в VictoriaMetrics (метрики для графиков). А для координации фоновых воркеров мы используем Redis (детали реализации можно посмотреть в предыдущей статье, а про кастомные Lua-скрипты для атомарности операций я расскажу в следующий раз).

Теперь давайте пройдёмся по группам протоколов и посмотрим, что именно мы реализовали.

Группа 1: Транспортные протоколы (ICMP, TCP, UDP)

ICMP (ping)

Самый базовый уровень проверки. Мы не стали изобретать велосипед и использовать сырые сокеты - вместо этого просто вызываем системный ping через asyncio.create_subprocess_exec. Это надёжно, просто и не требует прав CAP_NET_RAW.

Когда это нужно: проверить, жив ли сервер в принципе, даже если веб-сервер упал. Например, пинговать роутер или железку за NAT.

TCP-port check

Проверка, открыт ли конкретный TCP-порт. Мы используем asyncio.open_connection с таймаутом. Чекер может также ожидать определённый баннер (приветственное сообщение) - например, для SSH можно проверить наличие строки "SSH". Отправлять какие-либо данные после открытия соединения мы пока не умеем, только читаем.

Когда это нужно:

  • Убедиться, что SSH (22) доступен.

  • Проверить, слушает ли MySQL свой порт (3306).

  • Мониторить доступность RabbitMQ (5672), Redis (6379) и т.д.

UDP (сырой)

UDP-проверка сложнее, потому что протокол не гарантирует доставку. Мы отправляем пакет (можно задать произвольные данные или использовать готовые шаблоны, например для DNS) и ждём ответ. Если ответ пришёл - сервис жив. Если нет - ошибка фиксируется, но повторные попытки выполняются уже на уровне системы через механизм confirmation_threshold (сколько неудач подряд нужно для статуса DOWN). В самом чекере ретраев нет.

Когда это нужно:

  • DNS-серверы (запрос типа A).

  • Игровые сервера (если поддерживают кастомные UDP-запросы).

  • VoIP (SIP).

  • Собственные UDP-сервисы.

Группа 2: Прикладные протоколы (HTTP, DNS, почта, FTP)

HTTP/HTTPS

Самый популярный тип проверки. Мы поддерживаем:

  • Проверку кода ответа (200, 301, 404 и т.д.).

  • Поиск подстроки в теле (например, убедиться, что на странице есть слово «каталог»).

  • Проверку SSL-сертификата (срок действия, цепочка).

  • Поддержку HTTP-методов (GET, POST, HEAD) и отправку кастомных заголовков.

  • Следование редиректам (или запрет).

Технически: используем асинхронный HTTP-клиент httpx.

DNS

Мы проверяем не просто резолвинг домена, а можем запрашивать конкретные типы записей: A, AAAA, CNAME, MX, TXT, NS, SOA, PTR. Ожидаем определённый ответ (например, IP-адрес или наличие записи). Для этог�� используем библиотеку dnspython с асинхронным резолвером - она умеет работать как с системными, так и с кастомными DNS-серверами.

Когда это нужно:

  • Убедиться, что домен делегирован правильно.

  • Проверить, что MX-записи указывают на ваш почтовый сервер.

  • Мониторить изменения в TXT-записях (например, для проверки SPF/DKIM).

Почтовые протоколы (SMTP, POP3, IMAP)

Мы реализовали простые проверки «порт открыт и сервис отвечает» (handshake). Никакой отправки писем или логина — только установка соединения и чтение приветственного баннера. Этого достаточно для 90% задач.

Технически: используем asyncio для установки соединения и чтения приветственного баннера.

FTP

Проверка доступности FTP-сервера: установка соединения, чтение баннера. Опционально можно проверить логин, если пользователь предоставил учётные данные (но в базовой версии только handshake). SFTP не поддерживается, так как это отдельный протокол поверх SSH.

Базы данных

Для MySQL, PostgreSQL и Redis мы не реализовывали отдельные протоколы. Проверка сводится к TCP-порту: убеждаемся, что порт открыт. Для Redis можно отправить команду PING через TCP-чекер, но это не автоматизировано в интерфейсе.

Группа 3: Специализированные протоколы

Heartbeat (push-мониторинг)

Это не совсем классический протокол, а скорее метод: мы даём пользователю уникальный URL, на который его скрипты должны регулярно присылать HTTP-запрос. Если запросов нет дольше заданного интервала + льготный период - объявляем DOWN.

Когда это нужно:

  • Мониторинг cron-задач.

  • П��оверка, что фоновый воркер жив.

  • Устройства за NAT, которые не имеют публичного IP, но могут инициировать соединения наружу.

Технически: принимаем POST/GET, сохраняем время последнего пинга в PostgreSQL (таблица heartbeat_pings), воркер каждые 30 секунд проверяет просрочки. Статистика считается SQL-запросами через stats.py. В отличие от обычных мониторов, у heartbeat нет настраиваемого порога подтверждения сбоя (confirmationthreshold) - статус DOWN фиксируется сразу при первом пропуске (хотя количество последовательных пропусков мы отслеживаем для статистики).

Transaction (Playwright) - протокол №22

Самая мощная наша фича (и самая сырая). Это не просто проверка доступности, а полноценная эмуляция действий пользователя в браузере. Внутри мы используем Playwright - библиотеку от Microsoft, которая умеет управлять Chromium, Firefox и WebKit программно.

Зачем это нужно?
Представьте, что ваш интернет-магазин работает, главная страница открывается, но форма входа сломана из-за ошибки в JavaScript. Обычный HTTP-мониторинг покажет 200 OK, и вы узнаете о проблеме только когда пользователи начнут жаловаться. Transaction же пройдёт по шагам:

  1. Откроет страницу логина.

  2. Введёт тестовый логин/пароль.

  3. Нажмёт кнопку «Войти».

  4. Проверит, что после входа появился аватар пользователя.

Обратите внимание: количество ретраев настраивается не в самом чекере, а через механизм confirmation_threshold на уровне монитора.

Как настроить монитор в PingZen: пошагово для каждого протокола

Создание монитора в PingZen занимает не больше минуты, но для разных протоколов есть свои нюансы. Давайте пройдёмся по ключевым типам и посмотрим, какие параметры нужно указать.

Общая структура создания

  1. Нажимаете «Создать монитор» в дашборде.

  2. Выбираете тип протокола из списка (все 22 доступны, кроме Transaction - он в разработке).

  3. Заполняете базовые поля:

    • Название - чтобы не путаться.

    • Интервал проверки - как часто «стучаться» (от 60 секунд).

    • Таймаут - сколько ждать ответа.

    • Каналы уведомлений - куда слать алерты (Telegram, Slack, email и т.д.).

    • Пороги срабатывания - сколько неудач подряд нужно для статуса DOWN (confirmation_threshold) и сколько успешных для восстановления (recovery_threshold). Это помогает отсеять случайные сбои.

  4. Дальше - специфика протокола.

HTTP/HTTPS

Самый популярный тип. Помимо URL, можно настроить:

  • Метод запроса: GET, POST, PUT, HEAD и т.д.

  • Ожидаемый код ответа: например, 200 или 301.

  • Поиск подстроки: проверяем, что на странице есть определённый текст (например, «каталог» или «success»).

  • Следовать редиректам: включить/выключить.

  • Проверка SSL: можно отключить для тестовых стендов или, наоборот, проверять срок действия сертификата.

  • Заголовки: добавить свои (например, User-Agent или Authorization).

Пример: проверка API с JSON-ответом. Указываем метод POST, ожидаем код 200 и ищем в теле фразу "status":"ok".

TCP-порт

Всё просто: хост и порт. Можно также указать, нужно ли ожидать определённый баннер (например, для SSH можно проверить наличие строки SSH-2.0-OpenSSH). Отправлять данные после открытия соединения мы пока не умеем - только читаем.

Пример: проверка SSH-сервера: хост example.com, порт 22, ожидаем баннер с подстрокой SSH.

UDP (сырой)

Здесь возможностей больше:

  • Хост и порт.

  • Тип сервиса: можно выбрать готовый шаблон - DNS, NTP, SNMP или вручную задать пакет в hex

  • Ожидаемый ответ: если нужно, можно указать подстроку в ответе.

Пример для DNS: выбираем шаблон DNS, указываем тип записи A для домена example.com - и монитор сам сформирует правильный пакет и проверит ответ.

DNS

  • Домен: например, example.com.

  • Тип записи: A, AAAA, MX, TXT, SOA и другие.

  • Ожидаемый ответ: можно указать конкретный IP (для A-записи) или просто проверять, что запись существует.

Пример: проверяем, что MX-запись для вашего домена указывает на mail.example.com.

Heartbeat (push-мониторинг)

Здесь всё чуть иначе:

  • Интервал ожидания: как часто ваш скрипт должен «пинать» нас.

  • Льготный период (grace): дополнительное время после интервала, прежде чем бить тревогу. Это спасает от ложных срабатываний, если задача иногда выполняется чуть дольше.

После создания вы получаете уникальный URL, на который нужно отправлять запросы из ваших скриптов (примеры для Bash, Python, curl - мы сразу показываем в интерфейсе). Важно: slug в URL должен быть не короче 8 символов, например gitlab-ci-build или ci-deploy-job.

Остальные протоколы (SMTP, FTP и др.)

Для них настройка минимальна: обычно достаточно хоста и порта. Для почтовых - только handshake, для FTP - без SFTP.

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

Мы стремились к балансу между гибкостью и простотой. Пользователь не обязан разбираться в тонкостях протоколов - для большинства случаев есть разумные значения по умолчанию. Но если нужно что-то специфическое (например, отправить свой UDP-пакет), PingZen это позволяет.

Все настройки сохраняются, и вы можете в любой момент их изменить. История проверок и графики доступны сразу после создания монитора.

Как мы тестируем все эти протоколы

Для каждого протокола у нас есть юнит-тесты и интеграционные тесты. Например, для HTTP - проверяем, что код ответа парсится правильно, что редиректы работают. Для UDP - есть тесты с заранее записанными дампами пакетов (DNS). Для Transaction - мы запускаем тестовый сайт с известным поведением и проверяем, что сценарии проходят.

Всего у нас более 5100 тестов на бэкенде (включая модуль pinger), и они запускаются в CI при каждом коммите. Это даёт уверенность, что добавление нового протокола не сломает старые.

Заключение

22 протокола - не просто маркетинговая цифра. Это результат нашей работы над тем, чтобы PingZen стал универсальным инструментом для мониторинга. Мы не гонимся за количеством, но стараемся закрыть реальные потребности пользователей.

Transaction (Playwright) - наш флагманский протокол, который мы активно дорабатываем. Когда он будет готов, вы сможете проверять не только доступность, но и корректность работы интерфейса.

А если не хватит какой-то функции - пишите в комментарии или в наш Telegram-чат @rassadaRB, мы обязательно учтём пожелания!

P. S. Полная архитектура PingZen, конечно, сложнее: у нас есть планировщик проверок на базе Redis с Lua-скриптами, воркеры, которые распределяют нагрузку, и механизмы отказоустойчивости, позволяющие не терять данные даже при сбоях. Если вам интересно копнуть глубже — пишите в комментариях, я расскажу про внутреннее устройство подробнее в следующих статьях.