Привет, Habr!

Я уже какое-то время живу в мире «маленьких» проектов: пет-проекты, внутренние админки, сервисы для пары команд или одного отдела. И у них почти всегда одна и та же проблема: мониторинга либо нет совсем, либо «на коленке».

  • Где-то проверяется только nginx 200.

  • Где-то есть пара shell-скриптов с curl, которые периодически кто-то запускает руками.

  • Где-то есть сторонние админки, но никто до конца не понимает, что там за дашборды и как они на самом деле помогают.

В какой-то момент я поймал себя на том, что в третий раз пишу похожий набор проверок (API, страницы, базы, очереди, TLS, Docker…) и снова открываю это все в голых логах или простых HTML-страничках.

В итоге я сел и сделал отдельный проект — мониторинг-демон на Python + Next.js-дашборд, который:

  • постоянно гоняет health-checks (API, страницы, сервера, сети, базы, очереди, Docker, чувствительные пути),

  • пишет результат в JSON-снапшоты в папку output/,

  • а Next.js-frontend эти снапшоты читает и рисует живые панели.

Ниже расскажу, что он умеет, зачем я это вообще сделал и — самое важное для меня — это услышать фидбек от более опытных разработчиков про архитектуру, код и задумку в целом.


Зачем я это написал и кому это может быть полезно

Мой кейс довольно простой:

  • Есть несколько внутренних и пет-проектов.

  • Они крутятся в обычных условиях: VPS, Docker, базы, очереди, немного внешних API.

  • Настраивать полноценный стек мониторинга ради них — тяжеловато и по времени, и по поддержке.

  • Но и жить вообще без мониторинга уже страшновато — особенно когда появляются прод-клиенты.

Хотелось:

  1. Один легкий демон, который можно просто положить рядом с проектом, сконфигурировать через файл и забыть.

  2. Минимум зависимостей от инфраструктуры: все хранится в файловой системе, нет обязательных внешних сервисов/БД.

  3. Нормальный дашборд, а не «глянь логи, там где-то ERROR, надеюсь, найдешь сам».

  4. Немного DevOps-фич, чтобы не только смотреть, но и надёжно крутить чужие процессы.

Проект в итоге получился не игрушечным: кода и логики там уже ощутимо, поэтому мне и хочется взгляда со стороны.


Что умеет демон: проверки и стримы

Health-checks

Основной цикл крутится в main.py и прогоняет разные типы проверок. Результаты собираются в общий output/report_<timestamp>.json и, при желании, в TXT-отчет.

Поддерживаются:

  • API: методы, заголовки/авторизация (Bearer), таймауты, базовая валидация JSON (по ключам), превью ответа, при желании — сохранение полного ответа в файл.

  • Страницы: статус + цепочки редиректов, тайтл/мета, must_contain / must_not_contain, предупреждения по скорости, простые security-подсказки (HTTPS, HSTS, gzip), попытки найти robots.txt и sitemap.

  • Сервер: CPU/память/диск с порогами, отдельные mount-точки, аптайм в читаемом виде, базовая сетевая статистика.

  • Версии: Python/Node-зависимости — сравнение pip list с PyPI + npm ls с npm-registry, отмечаются апдейты/мажорные апдейты.

  • Логи: хвост нужных файлов, подсчет ERROR / WARNING / CRITICAL, сбор последних строк с ошибками, обработка недоступных/отсутствующих файлов.

  • DNS/WHOIS: запрос нужных типов записей, базовый WHOIS, грубый статус домена (ok / истекает / истек).

  • Сеть: проверка портов, TCP-payload/ожидаемый ответ, SMTP (EHLO/STARTTLS/LOGIN/NOOP), разбор TLS-сертификата (issuer, SAN, сколько дней до истечения, флаг warn/expired).

  • Чувствительные пути: перебор базовых URL, список известных файлов/директорий (типа .git, config.php, backup.sql и т.п.), с опцией считать 401/403 как потенциальную проблему.

  • Базы данных: MySQL (aiomysql) и Postgres (asyncpg) — подключение, версия, тестовый запрос с таймингами.

  • Очереди: Redis (PING, info, размер БД, длина очередей), RabbitMQ (пасивное объявление очереди и чтение статистики).

Каждая проверка живёт в своём модуле в checker/, плюс логирует старт/успех/ошибки через общий utils/logger.py.

Streams — данные для UI

Отдельно от периодического отчета крутятся «стримы» в monitoring/ — это фоновые потоки, которые снимают снимки (извините за тавтологию) состояния для конкретных панелей дашборда:

  • Docker stream: собирает список контейнеров/нод/событий через Docker CLI (плюс опционально обогащает статистикой через psutil), пишет output/docker_stream.json.

  • Database stream: периодически гоняет check_databases, подхватывает статусы бэкапов, при желании автоматически делает их (mysqldump / pg_dump), пишет output/database_stream.json.

  • Task manager stream: история CPU/памяти, нагрузка по ядрам, load_avg, топ процессов (pid, user, cmd, CPU, mem), пишет output/task_manager_stream.json.

  • Queue stream: доступность TCP и Redis-PING к очередям/endpoint’ам — output/queue_stream.json.

Идея простая: дашборд вообще не знает, как устроена система. Он просто читает JSON-файлы и показывает их удобным образом.


Supervisor: маленький «процесс-менеджер» внутри

Отдельно я заморочился с супервизором (monitoring/supervisor.py). Хотелось иметь не только «сервис, который бы все проверял», но и отдельный модуль, который держит в живыми другие процессы.

Что он делает:

  • Запускает процессы по конфигу (supervisor.command и supervisor.processes).

  • Умеет:

    • рестартить по выходу с кодом ≠ 0,

    • рестартить даже по exit 0, если так настроить (когда процесс «по бизнес-логике» не должен завершаться),

    • ловить зависания по признакам (долгое отсутствие активности + низкий CPU) и перезапускать.

  • Ограничивает ресурсы:

    • resource_limits через rlimit (память, CPU-секунды),

    • отдельный блок resource_monitoring — следит за реальной памятью/CPU и может рестартнуть при превышениях или потенциальной утечке.

  • Логирует stdout/stderr каждого процесса в:

    • *_latest.json (последние чанки вывода),

    • и, при включенном text-логировании, ещё и в .log.

Плюс есть watchdog-поток, который следит за самим супервизором и пытается перезапустить его в случае зависания/краша.

Для внешнего мира есть health-check API (/health и /supervisor), который отдает статусы процессов, PID’ы, ресурсы, счетчики рестартов — удобно, чтобы проверять сам мониторинг мониторингом :)


Дашборд на Next.js

Фронтенд живет в admin-dashboard/ и работает довольно примитивно:

  • Node 18+, Next.js.

  • Читает JSON-файлы из ../output (относительно корня дашборда).

  • Рендерит панели:

    • общий обзор,

    • Docker,

    • базы,

    • очереди,

    • supervisor,

    • настройки.

Почему так? Потому что цель была сделать минимальный UI без отдельной БД и API-сервера. Файловая система — самый простой контракт: Python пишет JSON, JS читает JSON. Можно положить рядом с приложением хоть на обычный VPS, хоть в контейнер и вмонтировать директории.


Конфигурация: все через config.yaml

Основной конфиг лежит в config.yaml. Там можно настроить:

  • Логи:

    logging: { level: DEBUG, file: monitoring.log, console: true }
    output:  { directory: output, json_format: true, text_format: false }
    
  • Супервизор:

    supervisor:
      enabled: true
      log_directory: output/supervisor
      healthcheck: { enabled: true, host: "127.0.0.1", port: 8130 }
      watchdog:    { enabled: true, check_interval_sec: 5, stale_threshold_sec: 45 }
      command:
        executable: "python"
        args: ["app.py"]
        working_dir: "/path/to/app"
        env: { APP_ENV: "prod" }
        user: "www-data"
        resource_limits: { memory_mb: 512, cpu_seconds: 120 }
        resource_monitoring:
          enabled: true
          sample_interval_sec: 2
          max_memory_mb: 700
          memory_leak_restart_mb: 128
          max_cpu_percent: 90
          network_check_host: "8.8.8.8"
          network_check_timeout_sec: 2
      restart_policy:
        mode: "always"
        restart_delay_seconds: 5
        restart_on_exit_0: true
        max_restarts_per_minute: 10
        hang_timeout_seconds: 60
        hang_cpu_percent_threshold: 3
        restart_on_hang: true
      processes:
        - name: "supervised-task"
          enabled: true
          command: { executable: "node", args: ["server.js"], working_dir: "/path/to/server" }
          restart_policy: { mode: "always", restart_delay_seconds: 5 }
    
  • Stream’ы и проверки: включение/выключение, интервалы, пороги, backup-опции и т.д.

  • Уведомления:

    notifications:
      enabled: true
      common: { tags: ["prod"], retry_attempts: 2 }
      telegram: { enabled: true, bot_token: "...", chat_id: "..." }
      discord:  { enabled: false, webhook_url: "" }
    

Уведомления: Telegram/Discord

В utils/notifier.py лежит простой, но достаточно гибкий механизм уведомлений:

  • Поддерживаются Telegram и Discord (через webhook).

  • Сообщения — по простым шаблонам {{placeholder}}.

  • Есть общие теги, попытки повторной отправки.

  • Для каждого канала можно задать, на какие события подписываться (api_failures, tls_expiry, server_alerts, system_error и т.п.).

  • Если уведомления выключены — все тихо превращается в no-op, без падений.


Как это запустить

В идеале хочется, чтобы минимальный сценарий был таким:

# создаем виртуальное окружение
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

# правим config.yaml под свои нужды

# запускаем демон
python main.py

И отдельно:

cd admin-dashboard
npm install
npm run dev   # http://localhost:3000

Дальше — смотришь на панели, правишь конфиг, добавляешь новые сервисы и т.д.


Честно о минусах и вопросах

Я хорошо понимаю, что это не замена Prometheus/Zabbix/ELK и прочему взрослому мониторингу. Скорее это:

  • «Мониторинг для одного-двух сервисов на VPS».

  • «Легкий helper к основному стеку, если нужно быстро закрыть базовые риски».

  • «Площадка, чтобы поиграть с идеями supervisor’а/стримов/дашборда».

И вот тут начинается самое интересное: я бы очень хотел услышать мнение более опытных людей.

  1. Нет ли тут очевидных граблей по надёжности/масштабированию, которые я сейчас не вижу?

  2. Насколько вообще такой сервис нужен?

  3. В каких кейсах вы бы его реально поставили?

  4. Что критически мешает использовать его «в бою»?


Фидбек

Я выкладываю этот проект не как «готовый продукт», а как живой учебный/рабочий инструмент, который использую сам и очень хочу допилить до более вменяемого состояния.

Если вы:

  • когда-то уже решали похожую задачу мониторинга «маленьких» сервисов,

  • писали свои демоны/супервизоры,

  • или просто любите разбирать чужой код и архитектуру,

мне будет очень полезно:

  • услышать, что бы вы сделали иначе;

  • какие места у вас сразу вызывают подозрение или желание переписать;

  • какие проверки/стримы вы считаете обязательными, а каких явно не хватает;

  • любые замечания по коду, структуре, именованию, тестам и т.д.

Ссылку на репозиторий я оставлю в конце поста — буду признателен за ишью, PR или просто комментарий здесь под статьей.

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

репо

Комментарии (5)


  1. opsmon
    30.11.2025 16:20

    Полезная статья, спасибо! Делалось под реальные нужды, ощущается. Очень понравилась простота идеи.

    Есть пара мелких идей для улучшения:

    Стоит добавить ротацию файлов и авто-чистку старых JSON, чтобы они не разрастались со временем. Круто иметь чуть более удобный способ подключать свои кастомные проверки , и скриншотов с примерами.


    1. d1sn3y_1337 Автор
      30.11.2025 16:20

      спасибо большое за фидбек! обязательно займусь авто чисткой в ближайшее время


  1. pravosleva
    30.11.2025 16:20

    Добавьте скринов. А то запускать нет времени, а посмотреть охота, что у вас там получилось.


    1. opsmon
      30.11.2025 16:20

      Развернул, в идеале свой UI прикручивать надо. А если так по шурику глянуть + алертинг прикрутить, то легковесный. До 10ти машин думаю идеально пойдет


  1. nikulin_krd
    30.11.2025 16:20

    ТС, хранение горячих данных на диске уже в целом плохая идея с точки зрения масштабирования. Пока метрик и источников мало, все +- работает, но как только нужно будет полезть за историческими данными за месяц или когда источников метрик станет существенно больше например начнутся проблемы с IO. Может все же сделать для горячих данных БД на SQLite в памяти и постепенно сгружать все на диск ?