Как мы учились смотреть на проблемы продукта «сверху»

Публикации в СМИ
22.10.2025
Опубликовано на
Хабре

Всем привет!

Меня зовут Александр Ковалев, и я занимаюсь разработкой инженерных продуктов Platform V Works. В этой статье поговорим про observability, а именно про конкретную область — мониторинг в продуктах. Обсудим, как вообще появляется сама идея мониторинга, какие есть варианты реализации. Расскажу, по какому пути пошли мы, какие ресурсы и материалы использовали.

Статья может быть полезна тем, кто впервые сталкивается с такими инструментами, как Prometheus и Grafana, а также всем, кто хочет изучить чужие примеры и прокачать компетенции в данной области.

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

Но по мере развития сервиса пользователей становится много, они объединяются в группы под разные сегменты, продукты или команды, а бизнес-функциональность усложняется. Можно по-прежнему анализировать происходящее через логи: развивать систему журналирования, переходить от терминала к Kibana/OpenSearch, настраивать там View и собирать фильтры. Но это неудобно, если помимо бизнес-событий нужны ещё и инфраструктурные показатели — нагрузка, задержки, ошибки на сетевом уровне. А эти показатели, в зависимости от зрелости организации, кто-то до сих пор смотрит через терминал или Zabbix.

В промышленном контуре возникает вопрос: «Достаточно ли нам логов для понимания того, что конкретно пошло не так при серьёзном сбое?» Изучая только логи, мы смотрим на единичные срабатывания, и контекст проблемы смещается для каждой выбранной строчки, а в голове при этом крутится мысль: «Эту проблему мы знаем, она не влияет ни на что».

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

В результате у нас появилась потребность в двух компонентах:

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

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

Избежать хаоса во внедрении метрик

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

Первый вариант, который является, скорее, базовым:

bd145f1a1b2f1066a388fefd7cbf553b.png

Есть существенные недостатки:

  • мы выставляем «наружу» каждый сервис, с которого хотим собирать метрики;
  • накладные расходы на коммуникации по уведомлению об изменениях;
  • отслеживание и применение изменений на Prometheus (на новый сервис появится новый Target);
  • накладные расходы на обеспечение безопасности.

Эта схема нам не подошла, но она подойдёт для pet-проекта, где основная задача — апробация.

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

a5cc4a82c986ee5ecc089b45273dc520.png

В архитектуре с OpenTelemetry Collector gateway приложения не ожидают, что внешний Prometheus скрапит их напрямую. Вместо этого они отправляют метрики на единый endpoint Collector. Там данные проходят через pipeline обработки: фильтрацию, агрегацию, возможно, sampling/recording. Затем Collector экспортирует подготовленные данные в Prometheus.

Преимущества этого решения:

  • Больше контроля над потоками телеметрии: Collector может обогащать метки, агрегировать, фильтровать.
  • Приложения отправляют данные в Collector, а не ждут скрапа снаружи.
  • Collector может отправлять напрямую телеметрию в выбранный вами backend или remote-write.

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

Третий вариант, на котором мы остановились и который, по моему опыту, встречается чаще в крупных организациях:

954ce41837667d2e4fd135aca2737f2a.png

Развёрнут собственный namespace‑collector Prometheus, скрапящий с сервисов метрики /actuator/prometheus. В свою очередь общий (коммунальный) Prometheus получает метрики с локального collector’а, не дёргая каждый сервис напрямую.Вариант, который тоже является рабочим, но со своими недостатками:

  • При скрапинге множества сервисов напрямую легко получить избыточное количество временных рядов.
  • Pull-модель Prometheus не даёт промежуточной обработки: нельзя фильтровать, агрегировать или обогащать метрики до импорта — всё происходит как есть.
  • Прямой скрап большого количества pod’ов или namespace’ов усложняет поддержку: при росте системы конфигурация усложняется.

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

Искушённый читатель, взглянув на схему, скажет: «Послушай, ты нарисовал всего один контейнер сервиса, а как же реальность? Есть системы missioncritical, где выставляются требования по горизонтальному масштабированию service-контейнеров. И вообще, мы можем жить сразу в нескольких ЦОДах одновременно, да ещё и с георезервированием».

cbe0f0fc2457849dcb4173b01a080866.png

Когда сервисы масштабируются, например, HPA=3, то namespace‑collector Prometheus одновременно скрапит три реплики каждого сервиса с endpoint /actuator/prometheus. Он агрегирует данные через recording rules, а центральный (коммунальный) Prometheus обращается только к этому collector через /federate, получая лишь заранее подготовленные метрики. Так снижаются нагрузка, cardinality и упрощается безопасность инфраструктуры.Это ускоряет дальнейшие запросы и снижает нагрузку, так как Grafana и центральный Prometheus (через /federate) работают уже с агрегированными рядам. Официальная документация Prometheus описывает настройку recording rules, синтаксис record: и expr:.

Что с безопасностью

Тема широкая, поэтому поговорим только о выбранном нами варианте реализации.

Prometheus-collector внутри namespace находится в mesh и просто делает обычный HTTP-запрос к /metrics — он не знает про TLS. Однако Istio sidecar перехватывает этот запрос, упаковывает в TLS и отправляет в mTLS-канале к sidecar приложения. Этот sidecar расшифровывает запрос HTTP и передаёт его service-контейнеру. Ответ идёт назад по тому же пути, тоже через TLS. В итоге Prometheus работает на plaintext, но Istio обеспечивает шифрование всего трафика внутри mesh.

Когда же центральный Prometheus (вне namespace) скрейпит namespace-collector, он уже подключается через HTTPS и mTLS — тут мы используем настройки tls_config.

Получается такая цепочка безопасности:

  1. Внутри namespace: Prometheus делает HTTP → Istio упаковывает в TLS → target получает метрики, ответ возвращается в TLS → Prometheus получает plaintext.
  2. Между Prometheus: коммунальный Prometheus делает HTTPS/mTLS на namespace-collector.

Таким образом, скрейп защищён там, где нужно (mesh), а Prometheus остаётся простым HTTP-клиентом. И только федерация требует явной TLS-конфигурации.

Понять, кто и за что отвечает в процессе

Внедрение мониторинга — не только техническая задача, но и организационный процесс. Мы разбили workflow на три зоны:

  1. разработка: формирует, реализует и проверяет метрики;
  2. владелец продукта: валидирует и приоритизирует;
  3. сопровождение: поддерживает и расширяет.

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

f456dd5926a914f165d2c3038d3ff9c9.png

Команда разработки:

  • формирование первичного списка метрик;
  • реализация и тестирование метрик;
  • документирование всех метрик: названия, тип метрик, описание, принадлежность к сервису;
  • создание дашбордов Grafana;
  • сохранение стабильной версии дашбордов в дистрибутиве продукта.

Владелец продукта:

  • валидация метрик: оценка «ценности» и релевантности для бизнес-задач;
  • принятие решений и заявки на добавление дополнительных метрик.

Сопровождение:

  • запросы на новые метрики для аналитики и анализа проблем при эксплуатации;
  • настройка или обновление дашбордов Grafana, подключенных к центральному Prometheus.

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

Следующим шагом определяли слои системной архитектуры сервисов для определения узких мест и потребности в отслеживании информации на графиках в Grafana. Этот процесс для команд с разным уровнем зрелости может проходить по-разному.

Мы выделили несколько уровней:

f9c9dde8a5544f685cc0a50acfddc213.png

Наша команда выделила внутри каждого сервиса три ключевых уровня: основной интерфейс, основные функции (Core) и выходные интеграции. На каждом уровне мы определяем нужные метрики, например, latency и error rate на endpoint, загрузку очередей и зависимостей на внутреннем уровне, saturation внешних вызовов и очередей.

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

Все метрики по сервисам описали в документации, взяв за референс документацию OpenShift. В дальнейшем в Grafana настроили Data sources и приступили к созданию дашбордов Grafana c использованием PromQl.Пройдя полностью ранее описанные шаги, мы пришли к процессу итеративного обновления stable-версии наших дашбордов и их доставки до промышленного контура.

Разбираемся в агрегации метрик

Начнём с анализа четырёх типов метрик Prometheus:

ТипЧто отражаетПодходит для…Ограничения и особенности
CounterСуммарные значения (события, ошибки)rate(), alerting, throughputТолько растёт, требует rate(), перезапуск сервиса = сброс счетчика
GaugeТекущее состояние (загрузка, размер)CPU/memory/queue depth, snapshot-текущееНет rate(), требует агрегации вручную
HistogramРаспределение значений (latency)Вычисление квантилей, latency P95/P99Нуждается в bucket-настройках, увеличивает cardinality*
SummaryКлиентские квантильные наблюденияP99 latency без server-side агрегацииНе агрегируется между экземплярами, дорог в ресурсах