Ещё один круг ада: мониторинг ERP без Prometheus, Grafana и выделенного DevOps
- воскресенье, 10 мая 2026 г. в 00:00:13
Привет, Хабр. Меня зовут Серафим Недошивин, уже как год я пишу мультитенантную ERP-подобную систему (Go, pgx | next.js, ts) для малого бизнеса и, чтобы не сойти с ума, рассказываю о проблемах, с которыми сталкиваюсь на этом нелёгком пути. В первой статье речь шла о 10 в первую очередь архитектурных проблемах (или кругах ада), включая изоляцию данных организаций, систему доступов и миграции схем базы данных.
Причина, по которой написана уже эта статья, крайне проста: загнивая от усталости, дописывая последнюю строчку последнего (или не очень) модуля системы, теша себя мыслями о скорой зарплате, каждый уважающий себя философ задаётся вопросом - а как контролировать в бою всё то, что мы написали¿¿
Глупец скажет - никак, мы же покрыли тестами.
Хитрец скажет - Grafana+Prometheus отдельными сервисами.
Психопат на крайней стадии выгорания скажет - поднимем отдельную админку и будем собирать метрики и инциденты без прометеуса, снимая снимки системы каждую минуту асинхронными воркерами под каждый компонент платформы, включая сервер, базу данных, объектное хранилище и кэш. На лету будем высчитывать дельты серверных метрик, а в завершение отрисуем всё это дело без графаны, силами Recharts и Святого Духа, упакуем в отдельную панель для технических администраторов и наконец - сделаем клиентский status-page платформы.
Очевидно, что автор этой статьи относится к последнему типу. По этой причине сегодня речь пойдёт об опыте построения самодельной системы мониторинга и интеграции её в уже существующую административную панель платформы. Приятного чтения.
От автора: всё, описанное в статье, не является эталоном и не утверждает о целесообразности использования такого подхода в других проектах. Многое можно реализовать гораздо чище и интеллигентнее.
Kroncl - разработчикам | GitHub проекта

Для начала определимся с тем, что и с какой целью будем мерить. Начнём с того, что практически каждое приложение можно разделить на компоненты/сервисы, в числе которых наиболее вероятно будут: api-сервер, база данных, кэш, файловое хранилище. Каждый из этих сервисов, что очевидно, может в любой момент упасть и, что ещё более очевидно, имеет свои лимиты допустимых значений нагрузки.
Таким образом, ваш Go-сервер может выдержать нагрузку, скажем, в 1000 RPS, а инстанс PostgreSQL имеет максимально допустимый лимит схем в 1000, при превышении которого скорость работы сервиса значительно снизится.
В совокупности каждый компонент платформы имеет несколько критичных метрик, по которым можно и нужно определять работоспособность всей платформы. Для краткости статьи сфокусируемся на двух наиболее критичных сервисах Kroncl (название платформы) и не будем упоминать мониторинг бизнес-метрик (это тема для отдельной статьи).
Итак, наша цель — собирать метрики с двух компонентов:
Go-сервер: requests_total, requests_5xx_total, requests_4xx_total, avg_response_time_ms, p95_response_time_ms, goroutines_count, heap_alloc_mb, open_fds_count… полный набор метрик
Postgres: total_database_size_mb, total_schemas_count, total_tables_count, total_active_connections, xact_commit, xact_rollback, tup_fetched … полный набор метрик
Особое внимание в случае Kroncl стоит уделить базе данных, ведь мультитенантность здесь реализована с помощью изоляции по схемам, что, очевидно, влечёт за собой жёсткие требования по отслеживанию превышения лимитов схем на инстанс.
Индустриальный стандарт для обеспечения observability сервисов - Prometheus + Grafana. Работает эта прекрасная связка по схеме: Prometheus стучится на отдельный эндпоинт вашего сервера, собирая накопившиеся с момента запуска метрики, после чего Grafana отрисовывает собранные данные в красивые диаграммы. При использовании такого подхода вся работа заключается в обеспечении /metrics эндпоинта для сбора метрик и настройки дашбордов в Grafana.
Но не спешите радоваться. Используя такой подход, вы добавляете сразу 2! новых сервиса в инфраструктуру приложения. Это не кажется проблемой только на dev-стадии проекта. Для использования Grafana в проде придётся конфигурировать дашборды, чтобы всё не слетало после банального рестарта сервиса + разводить порт на отдельный админский поддомен, всеми любимый Prometheus тянет за собой отдельную базу, и, кроме того, вы лишаетесь возможности разграничения доступа между тех. админами платформы (кто и какие метрики видит).
Но конкретно в моём случае критично другое: как формализовывать метрики, хранимые в базе Prometheus, в виде страницы статуса платформы, открытой для всех клиентов? Как определять инциденты на основании превышения системных лимитов того или иного компонента?
Кроме того, по скромному мнению автора, мониторинг сервисов не должен превращаться в беготню по тех. поддоменам платформы и вспоминанию паролей от админки. В идеальном мире контроль над платформой обеспечивается одной единой панелью, включающей как мониторинг системных и бизнес-метрик, так и техническую поддержку клиентов. Поднимается такое творение один раз вместе с ядром платформы.
Итак, надеюсь, мне удалось объяснить, почему в проекте, где важна простота развёртывания, единая панель управления и интеграция метрик во внутреннюю админку, стандартная связка становится скорее обузой, чем панацеей. Поэтому для реализации наблюдаемости был выбран третий путь, о котором и пойдёт речь дальше.
P.S. В конфигурации Kroncl, на всякий пожарный, всё равно присутствуют и Prometheus, и Grafana — как fallback-решение от греха подальше.
Для реализации сборщика метрик не будем изобретать новые подходы. Снимки системы за каждый промежуток времени - лучший вариант, к тому же ещё и просто реализуемый на Go с помощью воркеров поверх robfig/cron/v3.
План такой: под каждый компонент (сервер, база данных) реализуется воркер, собирающий метрики с соответствующего компонента (в случае с сервером - чтение глобальных счётчиков из памяти + расчёт дельт; в случае с базой - подключение и сбор метрик через pgx), после чего собранные данные сохраняются в базу.
В идеале система мониторинга не должна влиять на ресурсы боевого сервера и базы, что приводит к необходимости написания отдельного минималистичного сервера под воркеры и развёртывания отдельной базы для хранения метрик.
Зачем? - В случае полного падения основной базы/сервера (господи, сохрани) система мониторинга останется нетронутой.
Ваш покорный автор немного лентяй и решил пренебречь кейсами полного падения сервера или базы, поместив реализацию воркеров и хранение метрик в основном приложении и базе данных. Но учитывайте, что на больших масштабах разделение - рекомендуемое требование.
Реализация двух воркеров сбора метрик находится в core/workers Kroncl, инициализация и shutdown упакованы в точке входа - пакет app сервера. Периоды сбора метрик каждого компонента можно изменять с помощью config/workers.go.
Вспомогательный пакет metrics занимается инициализацией глобальных счётчиков серверных метрик.
Акцентирую внимание читателя на необходимости расчёта дельт некоторых метрик сервера. Дельта — это прирост значения счётчика за интервал между снятием двух последовательных снапшотов. Представьте, что с момента запуска сервера пришло 1000 запросов. Через минуту – 1020. Абсолютные значения (1000, 1020) на графике покажут рост, но пиков не видно. Дельта за минуту — 20 запросов. Так мы видим реальную нагрузку на сервер в каждый момент времени. Таким образом, для каждого цикла воркера мы сохраняем текущее абсолютное значение счётчика, а в БД записываем разницу между новым и предыдущим значением.
В конечном итоге таблицы metrics_db/server_history накапливают метрики с момента старта сервера, помечая каждую метку временными метками. Получаем историю изменения метрик компонентов, в нашем случае — сервера и базы данных.
Аналогично можно реализовать воркеры сбора метрик кэша, например Redis, и файлового хранилища, например Minio. Можно считать количество бакетов, элементы в бакетах, ключи в кэше, максимальный срок жизни строки кэша.
Итак, у нас есть история изменения метрик всех компонентов. Уже на этом этапе можно отображать графики счётчиков за целевой промежуток времени для админов платформы. Но много ли это даёт технически неподкованным пользователям? Вряд ли рядовой администратор диагностирует болезнь сервера, глядя на зашкаливающую метрику распределения кучи (heap_alloc).
Отсюда вытекает потребность в определении некоего отклонения от нормы метрик платформы. Для начала выделим 4 статуса системы и её компонентов:
operational - полная стабильность, отсутствие инцидентов;
degraded - деградация;
partial_outage - частичное нарушение;
major_outage - крупный сбой;
Для каждой критичной метрики объявим допустимый предел в config/status.go. Таким образом, можно регулировать жёсткость настроек текущей системы, увеличивая или уменьшая лимиты.
В пакете core/status реализуем логику динамического расчёта статуса всей системы и её компонентов за целевой промежуток времени с шагом анализа в 1 день. Определим количество отклонений от нормы каждой метрики на основе сохранённой истории в базе данных и присвоим каждому отклонению идентификатор вида <тип компонента>.<метрика>.<timestamp>. Каждое такое отклонение от нормы и называется инцидентом. На основе количества инцидентов можно определять статус компонента/всей системы (деградация, частичное нарушение, полная стабильность, крупный сбой).
Примеры инцидентов:
server-p95-1778260244
server-gc-1778259555
db-cache-1778260235
А теперь откроем эндпоинт статуса платформы для всех пользователей и отобразим в стиле я красивый:

К динамическому расчёту инцидентов можно добавить кэширование или вовсе запускать отдельным воркером раз в день.
Для детализации метрик и просмотра подробностей состояния отдельных компонентов системы в Kroncl реализована административная панель, совмещающая управление тикетами техподдержки, клиентской базой и просмотр метрик.
Админка строится на системе определения доступа, основанной на числовых кодах от 1 до 5 (модель проще, чем в основной платформе — статья, посвящённая системе прав Kroncl) и X-Admin-Keyword (ключевая фраза, подтверждающая критичные для платформы действия).
Реализация административной панели упакована в admin директории.
Таким образом, администраторы платформы с разными уровнями доступа получают доступ к анализу метрик разных компонентов платформы.
Выглядит это так:


Автор не знает, что сказать в завершение. Наверное, ничего. Система мониторинга своими руками на чистом Go без сторонних сервисов. Благодарю за внимание.