MAATRIX / Блог / Антипаттерн: мониторинг на том же сервере, что и продакшен

Антипаттерн: мониторинг на том же сервере, что и продакшен

MAATRIX

На VPS с рабочим приложением поднимают Prometheus, Grafana, Alertmanager и node_exporter — прямо туда же, в соседних контейнерах docker-compose. Логика на первый взгляд безупречна: зачем платить за второй сервер, если систему наблюдения можно развернуть рядом с продом бесплатно. Расплата приходит один раз, но дорого — в момент, когда падает не отдельный процесс, а сам хост целиком, и вместе с продом молчит именно то, что должно было закричать об этом первым. Разберём, почему так происходит, чем это опасно даже без полного падения сервера, и как развести наблюдателя и наблюдаемое, не разоряясь на дорогую инфраструктуру.

Как это выглядит на практике

Схема настолько типична, что почти у каждого, кто поднимал мониторинг «для себя» или для небольшого проекта, она встречалась хотя бы раз. Один VPS, на нём — прод-стек (например, nginx + приложение + PostgreSQL) и рядом, в том же docker-compose, — весь контур наблюдения:

# docker-compose.yml — прод и мониторинг в одном файле, на одном хосте
services:
  nginx:
    image: nginx:1.27
  app:
    build: ./app
  postgres:
    image: postgres:16

  # тот же хост, тот же docker network, тот же диск
  prometheus:
    image: prom/prometheus:v2.55.0
  grafana:
    image: grafana/grafana:11.2.0
  alertmanager:
    image: prom/alertmanager:v0.27.0
  node-exporter:
    image: prom/node-exporter:v1.8.2

Выглядит аккуратно: один docker compose up -d поднимает и прод, и мониторинг, метрики собираются, дашборды рисуются, алерты в Telegram приходят. Первые недели или даже месяцы всё работает штатно — потому что штатная работа не проверяет как раз тот сценарий, ради которого мониторинг вообще заводили.

Проблема не в том, что такая схема не работает. Она отлично работает для всего, кроме одного класса сбоев — и это как раз тот класс, ради которого систему наблюдения изначально ставили.

Главная проблема: наблюдатель — часть наблюдаемой системы

Мониторинг нужен не для того, чтобы констатировать, что процесс приложения упал (это частный случай), а для того, чтобы зафиксировать инцидент и разбудить дежурного тогда, когда сервис недоступен пользователям. Есть класс сбоев, где недоступным становится не процесс, а хост целиком:

  • пропадает сеть на уровне хостинг-провайдера или дата-центра;
  • отказывает питание физического сервера или сбоит гипервизор;
  • ядро уходит в панику (kernel panic) или зависает под нагрузкой;
  • диск переполняется настолько, что не может писать даже системные логи;
  • OOM-killer убивает процессы пачками, включая критичные для ОС.

В любом из этих случаев виртуальная машина недоступна целиком — не отвечает ни по ssh, ни по http, ни на ping. Если Prometheus, Alertmanager и Grafana физически исполнялись на этой же машине, они не «зафиксировали сбой и не смогли отправить алерт» — они попросту перестали существовать в тот же момент, что и прод. Не было ни записи метрики о падении, ни исходящего запроса к Telegram API, ни зафиксированного времени начала инцидента: последняя точка на графике — это последний успешный scrape перед падением, а дальше пустота, которую некому было даже нарисовать.

# после восстановления упавшего хоста — типичная картина в логах
docker compose ps
# NAME            STATUS
# app             Exited (137) 47 minutes ago
# prometheus      Exited (137) 47 minutes ago
# alertmanager    Exited (137) 47 minutes ago

# 137 = SIGKILL: контейнеры не упали от бага,
# они умерли вместе с хостовой ОС, потому что физически жили на ней же

Формально мониторинг «узнаёт» о произошедшем только после перезапуска — когда Prometheus снова начинает собирать метрики и видит, что целевой узел был недоступен. Это уже постфактум: правило InstanceDown сработает и создаст алерт, но отправлять его будет уже не о чём — сервис к этому моменту либо восстановился сам, либо администратор узнал о проблеме из звонка клиента. Дозорная система, которая докладывает о пожаре после того, как он потушен, не выполняет свою единственную функцию — сообщить вовремя.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Вторая проблема: конкуренция за ресурсы в критичный момент

Даже если хост не падает целиком, совместное размещение вредит по другой причине — менее драматичной, но более частой. Prometheus, Grafana и Alertmanager — не бесплатные в плане ресурсов процессы: скрейпинг метрик по расписанию, компакция TSDB на диске, рендеринг панелей Grafana по запросу, обработка правил алертинга — всё это постоянно потребляет CPU, RAM и дисковый I/O того же самого хоста, на котором крутится прод.

В спокойные периоды это незаметно — ресурсов хватает всем. Проблема проявляется именно в моменты пиковой нагрузки: когда на сайт приходит наплыв трафика, когда приложение начинает активно писать в базу, когда диск занят массовым логированием — то есть именно тогда, когда мониторинг особенно важен, потому что растёт риск деградации сервиса. В этот же момент:

  • Prometheus конкурирует за CPU и I/O с прод-приложением при очередном scrape или при compaction чанков TSDB на диске;
  • Grafana съедает память при рендеринге тяжёлых дашбордов, если кто-то в этот момент их открыл;
  • при нехватке RAM ядро может выбрать жертвой для OOM-killer как раз процесс мониторинга — и тогда сбор метрик прекратится молча, без явного алерта об этом;
  • общий диск и общая сетевая карта — одна и та же физическая (или виртуальная) шина для прод-трафика и трафика метрик.

Ограничения ресурсов на уровне контейнеров (mem_limit, cpus в docker-compose или cgroups) снижают риск того, что мониторинг «съест» всё и уронит прод, — подробнее о том, как выставлять такие лимиты, в статье про лимиты CPU и памяти в Docker. Но лимиты не устраняют саму конкуренцию за общий диск и общую сеть — они только делают её более предсказуемой. Если оба процесса физически используют одно и то же железо, полностью независимыми они быть не могут в принципе.

Почему инженеры всё равно так делают

Это не история про халатность. Решение почти всегда рациональное в моменте: второй сервер — это дополнительная строка расходов, дополнительная сетевая настройка (VPN или белые IP между хостами), лишняя точка, которую нужно поддерживать. На старте проекта, когда команда торопится запуститься, самый быстрый путь — дописать в существующий docker-compose ещё несколько сервисов и не поднимать отдельную инфраструктуру ради «просто мониторинга».

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

Правильная схема: мониторинг вне периметра прода

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

# prometheus.yml на ВЫНЕСЕННОМ сервере мониторинга —
# опрашивает прод по сети, а не живёт с ним на одной машине
scrape_configs:
  - job_name: 'prod-app'
    scrape_interval: 15s
    scheme: https
    static_configs:
      - targets: ['prod.example.com:9100']
    tls_config:
      insecure_skip_verify: false

Сеть между сервером мониторинга и продом стоит закрыть — либо через VPN (WireGuard между хостами), либо ограничив доступ к порту экспортёра списком IP через firewall, чтобы метрики не торчали в открытый интернет. Alertmanager при такой схеме тоже переезжает на сервер мониторинга — если упадёт прод, отправлять алерт будет кому.

Второй обязательный элемент — внешний дозор за самим мониторингом, dead man's switch. Сервер мониторинга тоже может упасть, и тогда прод останется без наблюдения молча, точно так же, как в исходном антипаттерне, только на уровень выше. Решение — внешний сервис, которому мониторинг сам периодически отправляет сигнал «я жив»:

# crontab на сервере мониторинга
* * * * * curl -fsS -m 10 --retry 3 https://hc-ping.com/xxxxxxxx-xxxx-xxxx > /dev/null

Если пинг не пришёл вовремя, внешний сервис сам инициирует алерт на резервный канал — независимо от того, что именно случилось с сервером мониторинга. Настройка такой связки на примере healthchecks.io разобрана в статье про мониторинг cron-задач через healthchecks.io; тот же принцип работает и как дозор за живостью всего стека наблюдения, а не только отдельных задач по расписанию. Ровно к такой схеме — вынесенный мониторинг плюс внешний дозор — в итоге пришли и в разборе реального инцидента, где мониторинг стоял на том же сервере и умер вместе с ним: проблема там оказалась не в конфигурации правил, а именно в архитектурном размещении.

Важно и то, что сам факт вынесения мониторинга не гарантирует, что вы узнаете о проблеме раньше пользователей, — у отдельно стоящего мониторинга остаются собственные слепые зоны (задержка опроса, неполный охват проверок), которые разобраны отдельно в статье про миф «мониторинг — я узнаю первым». Разнесение решает конкретную проблему единой точки отказа, а не заменяет собой все остальные практики эксплуатации.

Как вынести дёшево, если бюджет ограничен

Разнесение не обязано быть дорогим. Для мониторинга не нужен мощный сервер — небольшого VPS с 1-2 vCPU и парой гигабайт RAM обычно достаточно для Prometheus, Grafana и Alertmanager на нагрузке в несколько десятков целей скрейпинга; для более крупной инфраструктуры ресурсы стоит считать отдельно, ориентируясь на число метрик и глубину хранения.

Минимальный бюджетный вариант, если второй полноценный сервер пока не по карману:

УровеньЧто даётСтоимость
Ничего не менятьМониторинг падает вместе с продом0, но цена простоя — незамеченный инцидент
Только внешний dead man's switch (healthchecks.io/UptimeRobot, free tier)Узнаете, что сервер с продом и мониторингом целиком недоступенБесплатно или почти бесплатно
Отдельный небольшой VPS под Prometheus/Grafana/AlertmanagerПолноценное наблюдение, независимое от отказа прод-хостаСтоимость младшего тарифа VPS
Отдельный VPS + внешний dead man's switchДва независимых уровня защитыТа же цена VPS + бесплатный внешний чек

Даже без денег на второй сервер внешний dead man's switch закрывает худший сценарий — полную тишину при падении хоста целиком. Это не заменяет полноценно вынесенный мониторинг, но резко снижает шанс узнать об инциденте от клиента вместо алерта. Экономика такого решения та же, что и в других инфраструктурных компромиссах между «сделать самому» и «взять готовое» — сравнение разобрано в статье про то, что дешевле — свой мониторинг или сервис. И отдельно стоит держать в голове урок из истории про резервный сервер, который не включился, когда понадобился: любое резервирование, включая вынос мониторинга, стоит хотя бы раз проверить вживую, а не только на бумаге — остановить прод-хост в тестовое окно и убедиться, что алерт действительно приходит.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Обязательно ли выносить мониторинг на другого провайдера, или хватит второй VM у того же хостера?

Другая VM того же провайдера снижает риск падения вместе с конкретным хостом или гипервизором, но не защищает от сбоя на уровне дата-центра или сети провайдера целиком. Более надёжный, хоть и более сложный вариант — другой провайдер или хотя бы физически другой регион.

Если поставить лимиты ресурсов в docker-compose, можно оставить мониторинг на том же сервере?

Лимиты CPU и памяти снижают риск, что мониторинг «съест» ресурсы прода в моменты нагрузки, но не решают главную проблему — при падении самого хоста (сеть, питание, ядро) лимиты ресурсов ни на что не влияют, потому что падает всё сразу.

С чего начать, если сейчас нет бюджета на отдельный сервер под мониторинг?

С бесплатного внешнего dead man's switch вроде healthchecks.io — он не заменит полноценно вынесенный стек, но хотя бы сообщит, что сервер с продом и мониторингом стал недоступен целиком, а не будет молчать до жалобы клиента.

Нужно ли резервировать сам вынесенный сервер мониторинга?

Для небольших проектов обычно достаточно связки «вынесенный мониторинг плюс внешний дозор» — это уже два независимых уровня защиты. Полное резервирование самого мониторинга (второй Prometheus в active-passive) оправдано для крупной инфраструктуры, где простой наблюдения даже на несколько минут критичен сам по себе.

Как проверить, что новая схема действительно работает, а не просто выглядит правильно?

Остановите прод-сервер в согласованное тестовое окно, предупредив команду, и засеките, придёт ли алерт с вынесенного мониторинга. Отдельно стоит проверить и dead man's switch — временно остановить сам стек наблюдения и убедиться, что тишина тоже порождает алерт, а не проходит незамеченной.

Обсудить статью, задать вопрос или начать новую тему

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

Перейти в сообщество →