Привет, Хабр. Меня зовут Серафим Недошивин, уже как год я пишу мультитенантную 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 без сторонних сервисов. Благодарю за внимание.

Kroncl - разработчикам | GitHub проекта | Server | Client

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


  1. gerbert_MX
    09.05.2026 09:30

    метрики и логи всегда больдырказадница в каком бы то ни было даже среднем проекте

    как бы не хотелось "попроще" но прометей уже давно стандарт с которым проще смирится и поддерживать, чем страдать с самописными костылями

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

    интересное решение со статусом платформы, сразу подумал о том что бы просто написать одностраничный агрегатор что с прометея берет выборку простым HTTP и уже отдает в json для "статусной" страницы что бы прометей наружу не торчал

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


    1. mainbotan Автор
      09.05.2026 09:30

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

      В статье не ставил честно говоря задачу доказать, что можно проще, скорее, что в конкретных случаях целесообразнее воссоздать сбор метрик вручную - результат уже на оценку читателю.

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


  1. aborouhin
    09.05.2026 09:30

    А что мешало прикрутить к Графане тот же SSO, что и у Вашего приложения, и встроить в Вашу админку графановские дашборды? К тому же при наличии прометеусовских метрик пользователи наверняка оценили бы возможность мониторить приложение не только встроенными средствами, но и интегрировав в свой централизованный мониторинг всего остального.


    1. Xelld
      09.05.2026 09:30

      пользователи наверняка оценили бы возможность мониторить приложение не только встроенными средствами

      Пользователи - нет, а вот эксплуатация - да :)

      Согласен, наличие в продукте метрик для интеграции со своим мониторингом сильно упрощает жизнь.

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


    1. mainbotan Автор
      09.05.2026 09:30

      В общем и целом - ничего. Пару часов за настройкой дашбордов и загораживанием sso порта графаны. Проблема не столько в недостаточности графаны и прометеуса, сколько в разграничении доступов между админами и суммаризации всех собранных метрик под видом статуса всей платформы.

      Клиенты/пользователи - преимущественно не технические пользователи, нужны им не столько дашборды, сколько понимание текущего статуса и произошедших за последние 24 часа инцидентов. В комментарии сверху хабровчанин предлагает реализовать то же самое поверх http, добавив по сути агрегирующий слой отдельным сервисом и тягать метрики с прометеуса - как вариант.

      В конкретно моём случае проще показалось в целом отказаться от внешнего "склада" метрик и хранить и собирать на стороне основного приложения. Как результат - все те же метрики и диаграммы, что и с графаной и промой + страница статуса платформы для всех клиентов.

      Какой вариант выбирать - на усмотрение читателя.


      1. aborouhin
        09.05.2026 09:30

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

        Так Графана же умеет разграничивать доступ к дашбордам по пользователям. Если я правильно понял задачу - то сделать общий дашборд с агрегацией всего доступным для всех, а дальше на отдельные дашборды уже выдавать права доступа по необходимости.

        загораживанием sso порта графаны

        А почему порта? У неё поддержка SSO встроенная. Тогда как раз и получится, что те же пользователи, которые в системе, они же и в Графане, и там для них можно использовать встроенные механизмы разграничения доступа.


        1. mainbotan Автор
          09.05.2026 09:30

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

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

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

          В итоге единая модель сбора + отображения метрик для админов, так ещё и с определением инцидентов и статусом всей платформы для клиентов.


          1. aborouhin
            09.05.2026 09:30

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

            Официальная документация же. Ну и в любом дашборде тыкаем Edit -> шестерёнку Options -> Settings -> Permissions. Что-то там только в энтерпрайз-версии доступно, но базовый функционал разграничения доступа по дашбордам я и у себя в опенсорсной инсталляции наблюдаю. Там, кстати, другой вопрос: если ли у Вас свой SaaS, позволяет ли такие фокусы лицензия Графаны...


  1. Xelld
    09.05.2026 09:30

    Это здорово, что вы думаете про мониторинг и метрики сразу.

    Но точно ли вам нужно изобретать свое решение? В инфраструктуре больше одного сервера мониторинг в целом не помешает, а уж на масштабе - просто необходим, поэтому рядом с вашей платформой уже что-то будет, она же не единственная система в условной компании.

    Те же ошибки HTTP логичнее собирать с балансировщиков, например.

    Когда у разных команд есть один инструмент для всех частей и не нужно делать свою - обычно получается лучше.

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


    1. mainbotan Автор
      09.05.2026 09:30

      Прома + графана были оставлены больше как запасной вариант, если что-то пойдет уж совсем не так.

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

      На больших масштабах, да, чаще всего и правда кроме основной платформы рядом будет стоять ещё пару сервисов, контролирующих нагрузку на ядро. Вопрос только в том, что это будут за сервисы.

      В статье упоминаю, что в идеале воркеры и хранилище метрик должны быть изолированы от основной платформы. Такой подход с агрегацией метрик своими силами очевидно имеет большой минус в виде отсутствия стандартизации, но с другой стороны дает возможность крутить собранными данным как душе угодно. Хотите страницу статуса - получайте. Хотите репорты при превышении нагрузки - пожалуйста.....список можно продолжать бесконечно


      1. Xelld
        09.05.2026 09:30

        всегда было желание объединить мониторинг и другие административные функции в рамках одной системы

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

        Кстати о метриках - в grafana вполне себе есть гибкое управление доступом к дашбордам.

        На больших масштабах, да, чаще всего и правда кроме основной платформы рядом будет стоять ещё пару сервисов,

        Там будет намного больше, чем "пара" :)

        Если вы целитесь и в такой сегмент - лучше сразу предусмотреть типовые потребности эксплуатации и не забывать про это.

        Если ваша платформа может отдать свой статус, ещё и с детализацией, вам будут благодарны.


        1. mainbotan Автор
          09.05.2026 09:30

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



  1. Granulex
    09.05.2026 09:30

    "Психопат на крайней стадии выгорания" – лучший технический архетип года. Но у его подхода есть классика жанра: снимки каждую минуту будут идеальны ровно до первого "а где был алерт, система лежала 40 секунд?". ERP-инциденты уважают ваш интервал polling’а и заканчиваются за 30 секунд до следующего снимка.


    1. mainbotan Автор
      09.05.2026 09:30

      Да, сэр, вы правы, алертинг это в целом отдельная тема, хоть и тесно связанная с метриками.


    1. gerbert_MX
      09.05.2026 09:30

      настоящий психопат это каскадные логи когда сначала пишется каждый чих а потом идут "оптимизации" по минута-час-сутки-месяц с бекапами и тд

      и психопатом такие люди становятся когда к ним приходят с требованием разобраться что за проблема была месяц назад, как раз тогда когда подробные логи за тот период уже очистились, а дисцилированые ничего не говорят

      а алерты это совсем другое пальто. даже в рамках Графаны там можно ежа против шерсти родить в процессе настройки но все равно прошляпить фактическую деградацию. как по мне алерты начинаются уже в самом приложении, а не метриками едиными