Prometheus: глубокое погружение и подготовка к техническим собеседованиям

Курс охватывает все аспекты работы с Prometheus: от базовой архитектуры и PromQL до масштабирования, долговременного хранения и настройки Alertmanager. Программа сфокусирована на реальных задачах и подготовке к интервью уровня Middle/Senior, используя лучшие индустриальные практики [system-design.space](http://system-design.space/chapter/prometheus-architecture), [purpleschool.ru](https://purpleschool.ru/knowledge-base/kubernetes/monitoring/kubernetes-prometheus) и [glukhov.org](https://www.glukhov.org/ru/post/2025/11/monitoring-with-prometheus/).

1. Архитектура Prometheus и модель pull

Архитектура систем мониторинга определяет, насколько надежно и масштабируемо вы сможете наблюдать за состоянием вашей инфраструктуры. В мире cloud-native приложений стандартом де-факто стал Prometheus — система мониторинга и база данных временных рядов (Time-Series Database, TSDB). Понимание его внутреннего устройства и философии сбора данных критически важно для проектирования отказоустойчивых систем и успешного прохождения технических собеседований на позиции уровня middle и senior.

Глобальная архитектура Prometheus

Экосистема Prometheus состоит из нескольких взаимосвязанных компонентов. В отличие от монолитных систем прошлого поколения, Prometheus исповедует философию Unix: каждый инструмент делает одну вещь, но делает ее хорошо.

!Схема архитектуры Prometheus — показано взаимодействие центрального сервера с целями мониторинга через Service Discovery, а также связь с Alertmanager и Grafana

Центральным элементом является Prometheus Server, который выполняет три основные функции:

  • Retrieval (Сборщик) — компонент, отвечающий за периодический опрос (скрейпинг) целевых систем и получение от них метрик.
  • TSDB (База данных временных рядов) — локальное хранилище, оптимизированное для быстрой записи и сжатия огромного массива числовых данных с временными метками.
  • HTTP API — интерфейс для выполнения запросов на языке PromQL (Prometheus Query Language), который используется внешними системами визуализации, такими как Grafana.
  • Помимо самого сервера, в архитектуру входят:

    * Targets (Цели) — ваши микросервисы, базы данных или серверы, которые отдают метрики. Service Discovery (Обнаружение сервисов) — механизм динамического поиска целей. В облачных средах IP-адреса меняются постоянно, поэтому Prometheus интегрируется с Kubernetes, Consul, AWS EC2* и другими провайдерами, чтобы автоматически узнавать, кого нужно мониторить. * Alertmanager — отдельный сервис для маршрутизации, группировки и дедупликации оповещений (алертов), отправляемых сервером Prometheus. * Pushgateway — промежуточный кэш для метрик от кратковременных задач (batch jobs).

    Фундаментальный выбор: Модель Pull

    Главная архитектурная особенность Prometheus, вызывающая больше всего дискуссий на собеседованиях — это использование Pull-модели (модели вытягивания) для сбора метрик.

    В традиционных системах мониторинга (например, StatsD или InfluxDB с агентом Telegraf) чаще используется Push-модель (модель проталкивания). При Push-модели само приложение знает адрес сервера мониторинга и активно отправляет ему свои метрики по сети.

    Prometheus работает иначе. Ваше приложение не знает о существовании Prometheus. Оно лишь открывает HTTP-эндпоинт (по умолчанию /metrics), при обращении к которому отдает текущее состояние своих метрик в виде простого текста. Prometheus сам инициирует HTTP GET-запросы к этому эндпоинту с заданной периодичностью. Этот процесс называется скрейпингом (scraping).

    !Подвигайте ползунки интервала опроса и количества сервисов — и вы увидите, как Pull-модель сглаживает сетевую нагрузку по сравнению с Push-моделью

    Почему Prometheus выбрал Pull-модель?

    На технических интервью часто просят аргументировать выбор между Pull и Push. Для архитектора важно понимать неочевидные преимущества подхода Prometheus:

  • Централизованное управление нагрузкой. В Push-модели, если тысяча микросервисов одновременно запустится и начнет отправлять метрики, сервер мониторинга может упасть от DDoS-атаки собственными клиентами (проблема thundering herd). В Pull-модели Prometheus сам контролирует расписание опросов. Он распределяет запросы во времени, сглаживая нагрузку на сеть и собственную дисковую подсистему.
  • Упрощение клиентских библиотек. Приложению не нужно реализовывать сложную логику: буферизацию данных при недоступности сети, повторные попытки отправки (retries), управление таймаутами или резолвинг DNS-имен сервера мониторинга. Приложение просто держит текущие значения счетчиков в оперативной памяти и отдает их по HTTP-запросу. Это делает клиентские библиотеки (инструментарий) невероятно легковесными и надежными.
  • Бесплатный мониторинг доступности (Liveness). Это, пожалуй, самый сильный аргумент. Если Prometheus не может достучаться до эндпоинта /metrics (таймаут, отказ в соединении, HTTP 500), он автоматически генерирует синтетическую метрику up == 0 для этой цели. В Push-модели отсутствие данных неоднозначно: сервис упал, сеть порвалась или сервису просто нечего отправлять (например, нет входящих запросов)? Pull-модель дает четкий ответ на вопрос, жив ли процесс.
  • Удобство локальной отладки. Разработчик может просто открыть браузер или использовать утилиту curl http://localhost:8080/metrics, чтобы посмотреть, какие метрики генерирует его приложение прямо сейчас. Для отладки Push-модели пришлось бы поднимать локальный сервер-приемник.
  • > Pull-модель предоставляет прямую обратную связь, когда цель не отвечает. Вместо того чтобы полагаться на отсутствие отправленных метрик, что может занять время для осознания проблемы, вы получаете немедленное уведомление о том, что цель не смогла предоставить свои данные. > > O11y Blog

    Сравнение моделей сбора данных

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

    | Характеристика | Pull-модель (Prometheus) | Push-модель (InfluxDB, StatsD) | | :--- | :--- | :--- | | Инициатор соединения | Сервер мониторинга | Приложение (клиент) | | Обнаружение падений | Мгновенно (метрика up = 0) | Требуется анализ отсутствия данных (gap analysis) | | Конфигурация | Централизованная (на сервере) | Децентрализованная (в каждом приложении) | | Сложность клиента | Низкая (только хранение состояния) | Высокая (буферы, ретраи, сетевая логика) | | Обход NAT/Firewall | Сложен (серверу нужен доступ к клиенту) | Прост (клиенту нужен доступ к серверу) | | Краткосрочные задачи | Требует костылей (Pushgateway) | Идеально подходит |

    Математика скрейпинга: расчет нагрузки

    Как senior-специалист, вы должны уметь оценивать требования к ресурсам (Capacity Planning). Нагрузка на Prometheus напрямую зависит от количества целей, количества метрик на каждой цели и частоты опроса.

    Для расчета скорости поступления данных (Ingestion Rate) используется следующая формула:

    Где: * — скорость поступления данных (samples per second, сэмплов в секунду). * — количество целей (targets), которые опрашивает Prometheus. * — среднее количество метрик, отдаваемых одной целью за один скрейп. * — интервал опроса (scrape interval) в секундах.

    Пример из практики: У вас есть кластер Kubernetes с 500 подами (). Каждый под отдает в среднем 2000 метрик (). Вы настроили стандартный интервал опроса в 15 секунд ().

    сэмплов в секунду.

    Зная эту цифру, вы можете планировать размер диска и требования к CPU для TSDB. Один сэмпл в Prometheus в среднем занимает 1-2 байта на диске благодаря эффективным алгоритмам сжатия (Gorilla compression). Таким образом, 66 666 сэмплов/сек будут генерировать примерно 115 МБ данных в час.

    Жизненный цикл скрейпинга (Scrape Lifecycle)

    На глубоких технических интервью вас могут попросить описать путь метрики от приложения до базы данных Prometheus. Этот процесс состоит из нескольких строгих этапов:

  • Service Discovery. Prometheus обращается к API Kubernetes (или другого провайдера) и получает список всех существующих подов и их метаданные (IP-адреса, неймспейсы, аннотации).
  • Target Relabeling (Перемаркировка целей). До того как сделать HTTP-запрос, Prometheus применяет правила relabel_configs. На этом этапе можно отфильтровать цели (например, опрашивать только поды с аннотацией prometheus.io/scrape: "true"), изменить порт или подменить IP-адрес.
  • HTTP Scrape. Prometheus выполняет GET-запрос по адресу цели. Если запрос успешен, он парсит текстовый ответ.
  • Metric Relabeling (Перемаркировка метрик). После получения данных, но до их сохранения в TSDB, применяются правила metric_relabel_configs. Это критически важный этап для оптимизации: здесь можно удалить тяжелые метрики (drop), которые вам не нужны, чтобы сэкономить память и диск.
  • TSDB Storage. Данные записываются в Write-Ahead Log (WAL) для защиты от потери при сбоях, а затем помещаются в блоки в оперативной памяти (Head block), которые позже сбрасываются на диск.
  • Исключения из правил: зачем нужен Pushgateway?

    Несмотря на то, что Prometheus построен вокруг Pull-модели, существуют сценарии, где она физически не может работать. Классический пример — эпохальные (кратковременные) задачи (ephemeral batch jobs).

    Представьте скрипт резервного копирования базы данных, который запускается по cron раз в сутки, отрабатывает за 3 секунды и завершается. Если интервал опроса Prometheus составляет 15 секунд, он с вероятностью 80% просто не успеет опросить этот скрипт, пока тот жив.

    Для решения этой проблемы был создан Pushgateway. Это отдельный сервис, который работает как кэш метрик.

  • Кратковременный скрипт перед завершением своей работы пушит (отправляет) свои метрики в Pushgateway.
  • Pushgateway сохраняет эти метрики в памяти и держит их доступными.
  • Prometheus в своем обычном режиме пуллит (опрашивает) Pushgateway и забирает метрики скрипта.
  • Антипаттерны использования Pushgateway

    Типичная ошибка новичков (и частый вопрос на собеседованиях) — попытка использовать Pushgateway для обхода сетевых ограничений (NAT, Firewall) для обычных, долгоживущих сервисов.

    Почему это плохая идея: * Потеря метрики up. Prometheus будет опрашивать Pushgateway. Метрика up будет показывать статус самого Pushgateway, а не ваших микросервисов. Если микросервис умрет, Prometheus об этом не узнает — он продолжит получать старые, закэшированные метрики из Pushgateway. * Единая точка отказа (SPOF). Pushgateway становится узким местом. Если он упадет, вы потеряете метрики от всех сервисов, которые в него пишут. * Проблемы с очисткой (Staleness). Pushgateway хранит метрики вечно, пока их явно не удалят через API. Если микросервис изменил свой IP или был удален, его старые метрики останутся висеть в Pushgateway, засоряя TSDB.

    Для обхода сетевых ограничений в долгоживущих сервисах правильнее использовать механизм Prometheus Federation или Remote Write (о которых мы поговорим в следующих статьях), но не Pushgateway.

    Высокая доступность (High Availability) в Prometheus

    Как обеспечить отказоустойчивость самого мониторинга? Архитектура HA в Prometheus элегантна в своей простоте. В ней нет кластеризации в традиционном понимании.

    Чтобы получить HA, вы просто запускаете два (или более) абсолютно независимых сервера Prometheus с идентичной конфигурацией. Оба сервера независимо друг от друга опрашивают одни и те же цели (Targets) и хранят данные на своих локальных дисках.

    Плюсы такого подхода: * Shared-nothing архитектура. Падение одного сервера никак не влияет на другой. Нет сложных алгоритмов консенсуса (как Raft) или проблем с разделением сети (split-brain) между серверами мониторинга. * Простота эксплуатации. Не нужно настраивать сложные кластерные связи.

    Минусы: * Двойная нагрузка на цели. Ваши микросервисы будут опрашиваться в два раза чаще. * Дублирование алертов. Оба сервера вычислят, что сервис упал, и оба отправят алерт. Эта проблема решается на уровне Alertmanager, который умеет принимать дублирующиеся алерты от разных серверов Prometheus, дедуплицировать их и отправлять пользователю только одно уведомление.

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

    10. Создание и управление правилами оповещения

    Создание и управление правилами оповещения

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

    За этот этап отвечают Правила оповещения (Alerting Rules). На собеседованиях уровня Middle и Senior интервьюеры редко просят просто написать синтаксис правила. Их интересует другое: как вы боретесь с ложными срабатываниями, как проектируете архитектуру алертов для микросервисов, умеете ли тестировать правила до выкатки в продакшен и понимаете ли внутренний конечный автомат (State Machine) сервера мониторинга.

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

    Анатомия правила оповещения

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

    Рассмотрим классический пример:

    Разберем ключевые компоненты:

  • alert: Имя алерта. Должно быть коротким, понятным и использовать CamelCase (стандарт индустрии).
  • expr: PromQL-выражение. Если запрос возвращает хотя бы один временной ряд (вектор не пуст), условие считается выполненным.
  • for: Время ожидания. Защита от кратковременных всплесков.
  • labels: Статические пары ключ-значение, которые добавляются к алерту. Именно по ним диспетчер уведомлений будет выполнять маршрутизацию (например, team: backend).
  • annotations: Информационные поля для людей. Они не участвуют в маршрутизации, но содержат текст, который прочитает дежурный инженер.
  • Шаблонизация: делаем алерты полезными

    Алерт, который говорит «Упала база данных», бесполезен в кластере из 100 серверов. Инженер должен сразу видеть контекст. Для этого в блоке annotations используется Go-шаблонизация.

    Внутри шаблонов {{ ... }} вам доступны две главные переменные:

  • labels.instance }}.
  • value читаемым, если оно равно 0.053421?». Ответ: использовать встроенные функции форматирования, такие как humanize или humanizePercentage. Выражение {{ $value | humanizePercentage }} превратит 0.0534 в понятные 5.34%.
  • Конечный автомат: Inactive, Pending и Firing

    Один из самых частых вопросов на глубокое понимание системы: «Расскажите пошагово, что происходит внутри сервера с момента, когда метрика превысила порог, до момента отправки уведомления?».

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

  • Inactive (Неактивен): Выражение expr не возвращает данных (условие не выполняется). Всё хорошо.
  • Pending (В ожидании): При очередной оценке правил (по умолчанию каждые 15 секунд) выражение expr вернуло результат. Сервер фиксирует время начала проблемы, но не отправляет уведомление. Он ждет, пока пройдет время, указанное в параметре for.
  • Firing (Срабатывание): Если при каждой последующей оценке выражение остается истинным на протяжении всего времени for, алерт переходит в состояние Firing и отправляется во внешнюю систему маршрутизации.
  • !Подвигайте ползунок времени и посмотрите, как метрика превышает порог. Обратите внимание: алерт не отправляется сразу! Он переходит в статус Pending и ждет истечения таймера 'for', прежде чем стать Firing.

    Зачем нужен параметр for?

    Параметр for — это ваша главная защита от Flapping-алертов (мерцающих алертов).

    Представьте, что вы мониторите потребление памяти. Пришел Garbage Collector (сборщик мусора) приложения, и на 10 секунд потребление памяти прыгнуло до 95%, а затем упало до 40%. Если бы параметра for не было, вы бы получили уведомление о критической нехватке памяти, а открыв дашборд, увидели бы, что всё в порядке. Указав for: 5m, вы говорите системе: «Сообщи мне, только если память держится выше 95% непрерывно в течение 5 минут».

    Архитектурный подход: Симптомный алертинг

    Когда новички начинают писать правила, они покрывают алертами всё: загрузку CPU, использование оперативной памяти, количество потоков, IOPS диска. Это приводит к выгоранию дежурных инженеров (Alert Fatigue).

    Senior-инженеры используют Симптомный алертинг (Symptom-based Alerting).

    Суть подхода: вы должны алертить на симптомы, которые видит клиент, а не на причины (внутренние ресурсы).

    Сравним два подхода:

  • Причина (Плохо): node_cpu_seconds_total > 90%. Ну и что? Если CPU загружен на 100%, но приложение отвечает за 50 миллисекунд и пользователи счастливы — это не инцидент, это эффективная утилизация ресурсов.
  • Симптом (Хорошо): http_request_duration_seconds > 2s. Пользователи ждут загрузки страницы более двух секунд. Это реальная проблема. Дежурный получает алерт, открывает дашборд и уже там видит причину — ага, CPU перегружен.
  • !Симптомный алертинг похож на айсберг. Над водой (симптомы) — то, что видят пользователи: ошибки 5xx, высокая задержка, недоступность сервиса. Именно на это нужно вешать алерты. Под водой (причины) — CPU, память, сеть, диски. Это метрики для дашбордов и расследования, а не для ночных звонков.

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

    Продвинутые сценарии и краевые случаи

    Написание базовых правил не вызывает трудностей. Сложности начинаются при работе с распределенными системами и пропадающими метриками.

    Проблема исчезающих метрик: функция absent()

    Классическая задача с собеседования: «У вас есть cron-джоба, которая раз в час делает бэкап базы данных и пушит метрику backup_last_success_timestamp_seconds. Как написать алерт, который сработает, если бэкап не выполнялся более 2 часов, или если джоба вообще удалена и метрика перестала поступать?»

    Если вы напишете time() - backup_last_success_timestamp_seconds > 7200, это сработает, если метрика существует в базе. Но если сервер сгорел, метрика просто перестанет поступать (возникнет Staleness — устаревание данных). В PromQL математические операции с отсутствующим временным рядом возвращают пустоту. Алерт не сработает!

    Для решения этой проблемы используется функция absent(). Она проверяет существование вектора. Если вектор пуст (метрики нет), функция возвращает значение 1. Если метрика есть — возвращает пустоту.

    Правильный алерт на отсутствие бэкапа:

    Опасная ловушка absent(): Функция absent() удаляет все лейблы из результата, кроме тех, что явно указаны в селекторе. Если вы напишете absent(up{job="api"}), и у вас упадет один из десяти инстансов API, алерт не сработает! Он сработает только тогда, когда упадут все 10 инстансов и метрика up{job="api"} полностью исчезнет.

    Масштабирование: Alerting on Recording Rules

    В высоконагруженных системах вычисление сложных PromQL-выражений (например, расчет 99-го процентиля по тысячам контейнеров) может занимать секунды. Если у вас 500 таких алертов, сервер мониторинга потратит все ресурсы на их оценку.

    Лучшая практика (Best Practice) — разделять вычисления и алертинг:

  • Создайте правило записи (Recording Rule), которое раз в минуту агрегирует данные и сохраняет их в новую метрику (например, cluster:api_latency:p99).
  • Настройте правило оповещения, которое просто проверяет эту готовую метрику: expr: cluster:api_latency:p99 > 0.5.
  • Это снижает нагрузку на CPU сервера в десятки раз и делает логику алертов кристально чистой.

    Unit-тестирование алертов (Уровень Senior)

    В современной DevOps-культуре конфигурация мониторинга хранится в Git (Infrastructure as Code). И так же, как мы не деплоим код без тестов, мы не должны деплоить алерты без проверки.

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

    promtool позволяет писать Unit-тесты для правил оповещения. Вы создаете YAML-файл, в котором описываете:

  • Входные данные (Mock data) — какие метрики и с какими значениями «поступают» в систему в определенные моменты времени.
  • Ожидаемые результаты — какие алерты должны перейти в состояние Firing на конкретной минуте.
  • Пример структуры теста:

    Запуск команды promtool test rules test.yml в CI/CD пайплайне гарантирует, что ваша логика алертинга работает математически верно до того, как она попадет на бой.

    Резюме

    Создание правил оповещения — это баланс между чувствительностью системы и спокойствием инженеров.

    Запомните главные правила:

  • Используйте for для защиты от кратковременных всплесков.
  • Шаблонизируйте аннотации, чтобы дежурный сразу понимал контекст проблемы.
  • Алертите на симптомы (то, что болит у клиента), а не на причины (то, что происходит внутри сервера).
  • Помните про краевые случаи: используйте absent() для отслеживания пропавших метрик.
  • Покрывайте критичные алерты Unit-тестами через promtool.
  • Внедрение этих практик позволит вам построить систему мониторинга, которой доверяет команда: если пришел алерт, значит, проблема действительно существует и требует немедленного вмешательства.

    ```

    11. Интеграция Prometheus с системами визуализации

    Интеграция Prometheus с системами визуализации

    Сбор метрик, написание сложных PromQL-запросов и настройка маршрутизации алертов — это фундамент надежной системы мониторинга. Однако в момент критического инцидента дежурный инженер не будет вручную вводить запросы в консоль Prometheus. Ему нужен визуальный контекст: графики, тепловые карты и списки активных проблем, собранные в одном месте.

    Индустриальным стандартом для визуализации данных из баз временных рядов является Grafana. На технических собеседованиях уровня Middle и Senior вопросы об интеграции этих двух систем редко касаются базовой настройки интерфейса. Интервьюеров интересует, как вы управляете дашбордами как кодом, понимаете ли механику динамических интервалов при масштабировании графиков и умеете ли оптимизировать тяжелые запросы, которые могут «положить» сервер мониторинга.

    Архитектура взаимодействия: под капотом Data Source

    Интеграция начинается с добавления Prometheus в качестве источника данных (Data Source) в Grafana. Архитектурно Grafana выступает как обычный HTTP-клиент, который отправляет запросы к HTTP API Prometheus (эндпоинты /api/v1/query для мгновенных векторов и /api/v1/query_range для векторов диапазона).

    Важный нюанс, о котором часто спрашивают на собеседованиях: выбор HTTP-метода для отправки запросов. По умолчанию Grafana использует метод GET, передавая PromQL-выражение в параметрах URL.

    Проблема возникает при использовании сложных дашбордов с множеством переменных. Если ваш запрос содержит фильтр по сотне микросервисов, итоговый URL может превысить лимит веб-сервера или балансировщика нагрузки (обычно от 2 до 8 килобайт). В результате график просто не загрузится, выдав ошибку 414 URI Too Long. Решение для Senior-инженера — переключить параметр HTTP Method в настройках Data Source на POST. В этом случае тяжелый PromQL-запрос передается в теле запроса (body), обходя ограничения длины URL.

    Динамические дашборды: переменные и шаблонизация

    Жестко зашивать имена серверов или названия сервисов в графики — антипаттерн. Профессиональные дашборды строятся на основе Query Variables (переменных запроса), которые динамически подтягивают доступные значения из базы данных.

    Например, чтобы создать выпадающий список всех доступных кластеров, в настройках переменной Grafana используется специальная функция: label_values(up, cluster)

    Эта функция обращается к метрике up (которую мы разбирали в первой статье) и извлекает все уникальные значения лейбла cluster.

    Ловушка интервалов: __rate_interval

    Это один из самых сложных и частых вопросов на глубокое понимание связки Prometheus и Grafana: «Почему график функции rate() иногда показывает нули или разрывы при сильном отдалении масштаба времени, и как это исправить?».

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

    Где — шаг запроса, — выбранный диапазон времени, а — ширина графика в пикселях. Этот вычисленный шаг передается во встроенную переменную __interval]).

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

    > Правильный стандарт написания запросов в Grafana: > Всегда используйте __interval используйте только для функций агрегации поверх векторов диапазона (например, avg_over_time).

    !Подвигайте ползунок масштаба времени. Обратите внимание, как при сильном отдалении обычный __rate_interval автоматически расширяет окно, спасая визуализацию.

    Dashboard as Code (DaC) и Provisioning

    Создание графиков мышкой в веб-интерфейсе (ClickOps) подходит для локальной разработки. В Enterprise-средах дашборды должны версионироваться в Git, проходить код-ревью и разворачиваться автоматически. Этот подход называется Dashboard as Code (DaC).

    В Grafana за это отвечает механизм Provisioning (Инициализация). Он позволяет декларативно описать источники данных и дашборды в YAML-файлах.

    Типичный рабочий процесс выглядит так:

  • Инженер создает дашборд в интерфейсе тестовой Grafana.
  • Экспортирует его в формате JSON-модели.
  • Сохраняет JSON-файл в репозиторий (например, GitLab).
  • CI/CD пайплайн доставляет файл на сервер.
  • Grafana, благодаря настроенному Provisioning, автоматически подхватывает новый JSON с диска и обновляет интерфейс без перезагрузки сервиса.
  • Такой подход гарантирует, что при падении сервера визуализации вы сможете восстановить все графики за несколько секунд из репозитория.

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

    Представьте дашборд, который выводит 99-й процентиль времени ответа для 50 микросервисов за последние 30 дней. Когда 10 инженеров одновременно открывают эту страницу, Grafana отправляет десятки тяжелых запросов в Prometheus. Сервер мониторинга начинает потреблять гигабайты оперативной памяти, уходит в OOM (Out of Memory) и перезагружается.

    Как Senior-инженер, вы должны уметь защищать инфраструктуру от таких ситуаций. Существует три уровня оптимизации:

  • Ограничение Min Step в Grafana: В настройках панели (Query options) можно задать минимальный шаг (например, 1m). Даже если пользователь выберет диапазон за 1 час, Grafana не будет запрашивать данные с секундным разрешением, что снизит нагрузку на TSDB.
  • Использование Recording Rules: Как мы обсуждали в предыдущих статьях, тяжелые вычисления (особенно histogram_quantile`) должны выполняться в фоне на стороне Prometheus. Grafana должна запрашивать уже готовую, предварительно вычисленную метрику.
  • Grafana Query Caching: В высоконагруженных системах настраивается кэширование запросов. Если несколько пользователей открывают один и тот же дашборд с одинаковым временным диапазоном, Grafana отправляет запрос в Prometheus только один раз, отдавая остальным результат из кэша.
  • Архитектура идеального дашборда: подход сверху вниз

    Интервьюеры часто просят спроектировать дашборд для абстрактного веб-сервиса. Ошибка новичка — вывалить на экран графики загрузки CPU, памяти и дисков.

    Правильный архитектурный паттерн — Top-Down Troubleshooting (Поиск неисправностей сверху вниз). Дашборд должен читаться как книга, от бизнес-показателей к железу.

    !Схема идеального дашборда. Верхний ряд — бизнес-метрики и RED-симптомы (пользовательский опыт). Средний ряд — внутренности приложения (пулы соединений, сборщик мусора). Нижний ряд — инфраструктура (CPU, сеть, диски). Такой дизайн позволяет локализовать проблему за секунды.

  • Верхний уровень (Симптомы): Здесь располагаются метрики, отражающие пользовательский опыт. Используется RED-метод: количество запросов, процент 5xx ошибок, 99-й процентиль задержки. Если здесь всё зеленое — систему можно не скроллить дальше.
  • Средний уровень (Приложение): Внутренние метрики рантайма. Размер пула соединений с базой данных, время работы Garbage Collector, количество активных горутин или потоков.
  • Нижний уровень (Инфраструктура): Метрики узлов (Node Exporter). Утилизация CPU, использование RAM, IOPS дисков, сетевые дропы.
  • Такая структура позволяет дежурному инженеру при получении алерта мгновенно понять: пользователи страдают из-за ошибок (верхний уровень), потому что приложение исчерпало пул соединений (средний уровень), так как база данных тормозит из-за перегрузки диска (нижний уровень).

    Интеграция с трассировками: Exemplars

    Современная наблюдаемость (Observability) требует бесшовного перехода между метриками, логами и трассировками. Prometheus и Grafana поддерживают механизм Exemplars (Экземпляры).

    Экземпляр — это конкретный пример (Trace ID), привязанный к агрегированной метрике. Например, на графике гистограммы времени ответа вы видите всплеск до 5 секунд. Благодаря Exemplars, Grafana отобразит на этом графике маленькую точку. Кликнув на нее, вы мгновенно перейдете в систему распределенной трассировки (например, Jaeger или Tempo) и увидите конкретный запрос пользователя, который выполнялся эти 5 секунд, с пошаговой детализацией каждого вызова функции.

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

    12. Архитектура мониторинга микросервисов

    Архитектура мониторинга микросервисов

    В предыдущих статьях мы заложили прочный фундамент: разобрали механику сбора данных, написание запросов, настройку алертов и визуализацию. Однако на собеседованиях уровня Middle+ и Senior интервьюеры редко спрашивают, как написать базовый PromQL-запрос. Их интересует системный дизайн: как вы спроектируете мониторинг для 500 микросервисов, размазанных по трем дата-центрам, как обеспечите глобальную видимость данных и что будете делать, когда локальный диск сервера переполнится.

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

    За пределами RED: USE-метод и Четыре золотых сигнала

    Ранее мы обсуждали RED-метод, который идеально подходит для оценки пользовательского опыта (запросы, ошибки, длительность). Но микросервисы не висят в вакууме — они потребляют физические ресурсы (CPU, память, сеть, диски). Для мониторинга инфраструктурного слоя применяется USE-метод (Utilization, Saturation, Errors), разработанный Бренданом Греггом.

  • Utilization (Утилизация) — процент времени, в течение которого ресурс был занят полезной работой. Например, диск выполнял операции ввода-вывода 80% времени за последнюю минуту.
  • Saturation (Насыщение) — объем дополнительной работы, которая не может быть выполнена прямо сейчас и вынуждена ждать в очереди. Для CPU это длина очереди выполнения (run queue), для памяти — использование swap-файла.
  • Errors (Ошибки) — количество аппаратных или системных ошибок (например, битые сектора диска или отброшенные сетевые пакеты).
  • > RED-метод показывает, что ваши пользователи страдают. USE-метод показывает, почему страдает ваше оборудование.

    Google в своей практике SRE (Site Reliability Engineering) объединила подходы к приложению и инфраструктуре, создав концепцию Четыре золотых сигнала (The Four Golden Signals):

  • Latency (Задержка) — время, затрачиваемое на обслуживание запроса. Важно разделять задержку успешных и ошибочных запросов (ошибка часто возвращается мгновенно, искажая среднее значение).
  • Traffic (Трафик) — мера востребованности системы (HTTP-запросы в секунду для веб-API, количество сессий для базы данных).
  • Errors (Ошибки) — частота неудачных запросов (HTTP 5xx, логические ошибки бизнес-логики).
  • Saturation (Насыщение) — насколько полно используется система, акцент на ресурсах, которые наиболее ограничены (например, исчерпание пула соединений с БД).
  • Математика насыщения: Закон Литтла

    Насыщение — самый сложный для понимания сигнал. Инженеры часто путают его с утилизацией. Утилизация в 99% — это не всегда проблема, если очередь пуста. Проблема начинается, когда запросы начинают скапливаться.

    В теории массового обслуживания существует фундаментальная теорема — Закон Литтла (Little's Law). Она связывает количество запросов в системе, скорость их поступления и время обработки:

    Где:

  • — среднее количество запросов, находящихся в системе (включая те, что обрабатываются, и те, что ждут в очереди).
  • (лямбда) — средняя интенсивность поступления новых запросов (Traffic).
  • — среднее время нахождения запроса в системе (Latency).
  • Представим микросервис корзины покупок. Он получает 100 запросов в секунду (). Обработка одного запроса занимает 0.05 секунды ().

    .

    В любой момент времени микросервис обрабатывает 5 запросов одновременно. Если пул потоков веб-сервера равен 10, система работает стабильно (утилизация 50%, насыщение 0).

    Внезапно база данных начинает тормозить, и время обработки () вырастает до 0.5 секунды. Трафик () остается прежним — 100 запросов в секунду.

    .

    Теперь в системе одновременно находится 50 запросов. Но пул потоков по-прежнему равен 10! Это означает, что 10 запросов обрабатываются, а 40 — висят в очереди. Возникает жесткое насыщение (Saturation). Очередь быстро переполнит оперативную память, и микросервис упадет с ошибкой OOM (Out of Memory), вызвав каскадный сбой всей системы.

    !Интерактивный симулятор Закона Литтла. Подвигайте ползунки интенсивности трафика и времени обработки. Обратите внимание, как при превышении лимита потоков очередь начинает расти экспоненциально, демонстрируя состояние насыщения системы.

    Масштабирование сбора: Иерархическая федерация

    Когда количество микросервисов и узлов растет, один сервер Prometheus перестает справляться со скрейпингом. Возникает потребность в горизонтальном масштабировании.

    Первый архитектурный паттерн для решения этой задачи — Иерархическая федерация (Hierarchical Federation).

    Идея заключается в построении дерева серверов. На нижнем уровне (Leaf nodes) находятся локальные серверы Prometheus, установленные в каждом дата-центре или Kubernetes-кластере. Они собирают сырые метрики с высоким разрешением (например, каждые 10 секунд).

    На верхнем уровне находится глобальный сервер (Global node). Он не ходит к микросервисам напрямую. Вместо этого он выполняет скрейпинг специального эндпоинта /federate у локальных серверов.

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

    Локальные серверы используют правила записи (Recording Rules) для предварительного вычисления тяжелых метрик (например, суммарный трафик кластера). Глобальный сервер через /federate запрашивает только эти готовые агрегаты, используя селекторы лейблов (например, match[]={job="cluster_aggregates"}). Это позволяет получить единый дашборд состояния всей компании без перегрузки сети и дисков.

    Долговременное хранение: Remote Write

    Федерация решает проблему масштабирования сбора, но не решает проблему долговременного хранения (Long-Term Storage, LTS). Локальный диск сервера не бесконечен. По умолчанию данные хранятся 15 дней. Если бизнес требует хранить метрики за год для анализа трендов, локальный TSDB не подойдет.

    Для интеграции с внешними хранилищами используется механизм Remote Write.

    Вместо того чтобы внешняя система запрашивала данные (Pull), Prometheus сам начинает активно отправлять (Push) собранные сэмплы во внешнюю базу данных. Это происходит параллельно с записью на локальный диск.

    Процесс работает так:

  • Prometheus читает данные из своего Write-Ahead Log (WAL).
  • Формирует пакеты (батчи) сэмплов.
  • Сжимает их алгоритмом Snappy.
  • Сериализует в формат Protobuf.
  • Отправляет HTTP POST-запросом на указанный эндпоинт.
  • Remote Write позволяет отвязать сбор данных от их хранения. В качестве приемников (Receivers) часто выступают специализированные кластерные TSDB, такие как VictoriaMetrics, Cortex или Mimir.

    Глобальная видимость и LTS: Архитектура Thanos

    Индустриальным стандартом для построения отказоустойчивого мониторинга с долговременным хранением стал проект Thanos. Он решает сразу три задачи: неограниченное хранение, глобальный поиск и дедупликация данных от HA-пар Prometheus.

    Thanos не заменяет Prometheus, он оборачивает его, добавляя новые компоненты (паттерн Sidecar).

    !Архитектура Thanos. Видно, как Sidecar прикреплен к каждому Prometheus и отправляет исторические блоки в S3. Компонент Querier выступает единой точкой входа: он запрашивает свежие данные напрямую у Sidecar, а старые — у Store Gateway, объединяя их в один ответ для Grafana.

    Ключевые компоненты Thanos

  • Thanos Sidecar — легковесный процесс, который запускается рядом с каждым сервером Prometheus. У него две задачи. Во-первых, он перехватывает PromQL-запросы извне и транслирует их в локальный Prometheus. Во-вторых, каждые 2 часа (когда Prometheus формирует неизменяемый блок данных на диске), Sidecar берет этот блок и загружает его в дешевое объектное хранилище (AWS S3, GCS, MinIO).
  • Thanos Store Gateway — компонент, который умеет быстро читать исторические блоки данных прямо из объектного хранилища S3, не скачивая их целиком на локальный диск (используя индексы).
  • Thanos Querier (Query) — единая точка входа для Grafana. Когда пользователь делает запрос за последний месяц, Querier понимает, что свежие данные (за сегодня) лежат на локальных дисках, а старые — в S3. Он параллельно отправляет запросы к Sidecar (за свежим) и к Store Gateway (за историей), склеивает результаты и отдает пользователю.
  • Thanos Compactor — фоновый процесс, который сканирует S3, объединяет мелкие блоки в крупные для ускорения запросов и выполняет даунсэмплинг (Downsampling) — искусственное снижение разрешения старых данных (например, усреднение 15-секундных точек до 1-часовых) для экономии места.
  • Дедупликация на лету

    На собеседовании на позицию Senior вас обязательно спросят: «У нас есть два сервера Prometheus в HA-режиме, они собирают одни и те же метрики. Как Thanos избегает задвоения данных на графиках?»

    Магия происходит в компоненте Thanos Querier. При настройке HA-пары каждому серверу задается уникальный внешний лейбл (например, replica="A" и replica="B"). Оба сервера собирают метрику http_requests_total.

    Когда Querier запрашивает данные, он получает два потока: один от реплики А, другой от реплики Б. Querier использует алгоритм дедупликации на основе временных меток и заданного лейбла replica. Если он видит две точки с одинаковыми лейблами (за исключением replica) и близкими таймстемпами, он просто отбрасывает одну из них на лету. Если реплика А падает, Querier бесшовно продолжает отдавать данные от реплики Б. График в Grafana остается ровным, без провалов и удвоений.

    Мониторинг Serverless и FaaS

    Архитектура микросервисов всё чаще включает бессерверные вычисления (Serverless) — AWS Lambda, Google Cloud Functions. Функция просыпается по триггеру, обрабатывает запрос за 200 миллисекунд и уничтожается.

    Классическая Pull-модель здесь бессильна. Prometheus физически не успеет обнаружить функцию через Service Discovery и сделать HTTP-запрос до того, как она исчезнет.

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

    Современное архитектурное решение — использование Агрегаторов метрик (например, StatsD или Telegraf) в сочетании с паттерном Remote Write.

  • Serverless-функция при завершении работы отправляет UDP-пакет с метриками (Push) в промежуточный сервис-агрегатор, который работает постоянно.
  • Агрегатор суммирует полученные данные в памяти.
  • Prometheus собирает данные с агрегатора по классической Pull-модели.
  • Альтернативный, более современный путь — использование стандарта OpenTelemetry. Функция отправляет метрики в OpenTelemetry Collector, который напрямую транслирует их в хранилище (например, Mimir или Thanos Receiver) через протокол Remote Write, полностью минуя классический сервер Prometheus.

    13. Долговременное хранение данных и удаленная запись

    Долговременное хранение данных и удаленная запись

    На технических собеседованиях уровня Middle+ и Senior вопросы о написании запросов быстро сменяются вопросами об эксплуатации. Интервьюер может спросить: «Наш сервер Prometheus внезапно перезагрузился по питанию. Потеряем ли мы данные за последние два часа?» или «У нас 10 миллионов активных временных рядов, и диск заполняется слишком быстро. Как вы будете это чинить?».

    В предыдущей статье мы вскользь коснулись механизма удаленной записи и архитектуры Thanos для обеспечения глобальной видимости. Теперь пришло время заглянуть под капот. В этой статье мы разберем анатомию локального хранилища Prometheus, математику сжатия временных рядов, тонкую настройку очередей отправки данных и сравним ведущие индустриальные решения для долговременного хранения (Long-Term Storage, LTS).

    Анатомия локального хранилища (TSDB)

    Prometheus не использует реляционные базы данных вроде PostgreSQL или документоориентированные вроде MongoDB. Он использует собственную, узкоспециализированную базу данных временных рядов — TSDB (Time-Series Database), написанную с нуля для максимальной скорости записи.

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

    Оперативная память и Write-Ahead Log (WAL)

    Когда Prometheus выполняет скрейпинг и получает новые метрики, он не пишет их сразу в файлы на жестком диске. Это было бы слишком медленно из-за высоких накладных расходов на операции ввода-вывода (I/O). Вместо этого свежие данные сохраняются в оперативной памяти (RAM) в структуре, называемой Head Block.

    Но хранение данных только в памяти небезопасно. Если процесс prometheus упадет (например, из-за ошибки OOM — Out of Memory) или сервер перезагрузится, все данные в памяти исчезнут. Для решения этой проблемы используется Write-Ahead Log (WAL) — журнал упреждающей записи.

    Параллельно с добавлением данных в оперативную память, Prometheus дописывает сырые, несжатые события в конец файлов WAL на диске. Запись в конец файла (append-only) — это самая быстрая дисковая операция.

    Если сервер падает, при следующем запуске Prometheus читает файлы WAL и восстанавливает состояние оперативной памяти до момента сбоя. Ответ на вопрос интервьюера из начала статьи: нет, данные не потеряются, они будут восстановлены из WAL, хотя запуск сервера займет некоторое время.

    Блоки и Компакция

    Каждые 2 часа Prometheus берет данные из оперативной памяти (Head Block), сжимает их, строит индексы и сохраняет на диск в виде неизменяемой директории — Блока (Block). После успешного создания блока старые файлы WAL удаляются.

    Каждый блок содержит:

  • chunks/ — директорию с самими значениями метрик и временными метками.
  • index — файл, связывающий имена метрик и лейблы с конкретными чанками (работает как оглавление в книге).
  • meta.json — метаданные блока (время начала, время конца, количество рядов).
  • Со временем на диске скапливается множество мелких 2-часовых блоков. Запрашивать данные за месяц, читая сотни мелких файлов, неэффективно. Поэтому в фоне работает процесс Компакции (Compaction). Он берет несколько старых блоков (например, три 2-часовых) и сливает их в один более крупный блок, параллельно удаляя дубликаты и оптимизируя индексы.

    !Архитектура локального хранилища TSDB — путь данных от оперативной памяти до сжатых блоков на диске.

    Удаление данных: Tombstones

    Что происходит, когда вы отправляете HTTP-запрос в API Prometheus с требованием удалить определенную метрику (например, содержащую персональные данные)?

    Prometheus не удаляет данные из файлов чанков немедленно. Блоки на диске неизменяемы (immutable). Перезапись гигабайтного файла ради удаления одного ряда парализовала бы систему.

    Вместо этого Prometheus создает специальный файл — Tombstone (Надгробие). В этот файл записывается информация: «Метрика X в диапазоне времени Y считается удаленной». При выполнении PromQL-запросов система читает данные из чанков, затем сверяется с Tombstone и отфильтровывает «мертвые» данные на лету. Физическое удаление данных с диска произойдет только при следующей фоновой компакции этого блока.

    Математика хранения: алгоритм Gorilla

    На собеседованиях часто просят оценить требования к оборудованию. Допустим, ваша инфраструктура генерирует 100 000 сэмплов в секунду. Сколько места на диске потребуется для хранения этих данных в течение 30 дней?

    Каждый сэмпл в Prometheus состоит из временной метки (timestamp, 8 байт) и значения (float64, 8 байт). Итого 16 байт на одну точку.

    Если бы Prometheus хранил данные в сыром виде, расчет был бы таким: ГБ (около 4 Терабайт).

    Однако в реальности Prometheus займет всего около 300-400 ГБ. Как это возможно?

    Prometheus использует алгоритм сжатия Gorilla compression, разработанный в Facebook. Он основан на дельта-кодировании (XOR-сжатии):

  • Временные метки: Поскольку скрейпинг происходит через равные интервалы (например, ровно каждые 15 секунд), разница между метками постоянна. Prometheus сохраняет не саму метку, а разницу разниц (delta-of-deltas), которая в большинстве случаев равна 0 и занимает всего 1 бит.
  • Значения: Многие метрики (например, доступная память или температура) меняются очень плавно. Алгоритм применяет побитовое исключающее ИЛИ (XOR) между предыдущим и текущим значением. Если значения близки, результат содержит много нулей, которые легко сжимаются.
  • Благодаря алгоритму Gorilla, средний размер одного сэмпла в Prometheus составляет всего 1.5 – 2 байта вместо 16.

    Формула для расчета требуемого дискового пространства:

    Где:

  • — требуемое место в Гигабайтах.
  • — количество сэмплов в секунду.
  • — время хранения в секундах (например, 30 дней = 2 592 000 секунд).
  • — в среднем 1.5 байта.
  • !Подвигайте ползунки — и узнайте, сколько дискового пространства потребует ваш мониторинг через год.

    Стратегии удержания данных (Retention)

    По умолчанию Prometheus хранит данные 15 дней. Вы можете изменить это поведение с помощью флагов запуска. Существует два основных подхода:

  • Ограничение по времени (--storage.tsdb.retention.time=30d). Prometheus будет хранить данные ровно 30 дней. Проблема этого подхода в том, что если разработчики выкатят баг, вызывающий взрыв кардинальности (как мы обсуждали в предыдущих статьях), диск может переполниться за пару дней, и сервер упадет.
  • Ограничение по размеру (--storage.tsdb.retention.size=500GB). Prometheus будет хранить столько дней, сколько поместится в 500 ГБ. Если трафик метрик вырастет, глубина истории просто сократится, но сервер продолжит работать стабильно.
  • > Лучшая практика (Best Practice) — использовать оба флага одновременно. Например: хранить данные 30 дней, НО не более 500 ГБ. Сработает тот лимит, который будет достигнут первым. Это гарантирует предсказуемость бизнес-требований и защищает инфраструктуру от падений.

    Глубокое погружение в Remote Write

    Как мы выяснили ранее, для хранения данных годами используется механизм Remote Write — активная отправка метрик во внешние системы. Но как именно Prometheus отправляет миллионы точек в секунду, не блокируя свою основную работу?

    Процесс Remote Write читает данные не из блоков, а напрямую из WAL. Это гарантирует, что данные отправляются во внешнюю систему практически в реальном времени (задержка обычно составляет миллисекунды).

    Конвейер отправки и Обратное давление

    Данные проходят через сложный внутренний конвейер:

  • Чтение из WAL: Prometheus читает новые записи из журнала.
  • Шардирование: Данные распределяются по нескольким параллельным очередям (Shards). Это позволяет утилизировать многоядерные процессоры.
  • Батчинг: Сэмплы накапливаются в пакеты (Batches), чтобы не отправлять по одному HTTP-запросу на каждую точку.
  • Сжатие и Сериализация: Пакет сериализуется в бинарный формат Protobuf и сжимается алгоритмом Snappy.
  • Отправка: Выполняется HTTP POST запрос к удаленному хранилищу.
  • Ключевая концепция здесь — Обратное давление (Backpressure). Если удаленное хранилище (например, Thanos или Mimir) начинает тормозить или сеть деградирует, очереди в Prometheus начинают заполняться.

    Если Prometheus продолжит читать WAL с прежней скоростью, он исчерпает всю оперативную память и упадет. Чтобы этого избежать, при заполнении очередей Remote Write приостанавливает чтение WAL. Данные начинают безопасно копиться на локальном диске в файлах WAL. Как только удаленная система «оживет», Prometheus автоматически увеличит количество шардов (вплоть до max_shards) и быстро отправит накопившийся бэклог.

    Тонкая настройка очередей

    На Senior-интервью вас могут попросить оптимизировать конфигурацию remote_write. Вот ключевые параметры блока queue_config:

  • capacity — сколько сэмплов может храниться в памяти одного шарда. Увеличение требует больше RAM, но лучше сглаживает сетевые пики.
  • max_shards — максимальное количество параллельных потоков отправки. Если Prometheus отстает от WAL (бэклог растет), увеличьте это значение.
  • max_samples_per_send — размер одного батча. Для локальных сетей можно ставить больше (10000), для нестабильных интернет-каналов — меньше, чтобы уменьшить цену повторной отправки при ошибке.
  • Экосистема Long-Term Storage: Что выбрать?

    В предыдущей статье мы рассмотрели архитектуру Thanos с использованием паттерна Sidecar. Однако это не единственное решение на рынке. Выбор LTS-системы — классическая задача системного дизайна.

    1. Thanos (Sidecar vs Receiver)

    Мы уже знаем, что Thanos Sidecar загружает 2-часовые блоки в S3. Это дешево и надежно. Но у этого подхода есть минус: задержка. Если локальный Prometheus сгорит вместе с диском до того, как блок будет загружен в S3 (в течение этих 2 часов), данные будут потеряны.

    Для критичных систем Thanos предлагает компонент Thanos Receiver. Он работает по модели Remote Write. Prometheus непрерывно пушит данные в Receiver, который сохраняет их в свой локальный WAL, а затем сам формирует блоки и отправляет в S3. Это снижает риск потери данных до секунд, но требует мощных серверов для самих Receiver-ов.

    2. Mimir (эволюция Cortex)

    Проект Mimir (от компании Grafana Labs) построен исключительно вокруг модели Remote Write. В отличие от Thanos Sidecar, Mimir не пытается читать данные с дисков Prometheus.

    Главная архитектурная особенность Mimir — встроенная Мультитенантность (Multi-tenancy). Это способность системы безопасно обслуживать множество независимых клиентов (команд, проектов или компаний) в рамках одного кластера, полностью изолируя их данные друг от друга.

    В HTTP-запросе Remote Write передается заголовок X-Scope-OrgID. Mimir читает его и физически разделяет данные. Команда А никогда не увидит метрики команды Б, даже если они хранятся в одном S3-бакете. Это делает Mimir идеальным выбором для крупных Enterprise-компаний и SaaS-провайдеров.

    3. VictoriaMetrics

    VictoriaMetrics — это феномен в мире мониторинга. В то время как Thanos и Mimir представляют собой сложные распределенные системы из десятков микросервисов, VictoriaMetrics может работать как один единственный бинарный файл.

    Она реализует собственный формат хранения на диске, который сжимает данные еще эффективнее, чем Gorilla в Prometheus, и потребляет в разы меньше оперативной памяти. VictoriaMetrics полностью поддерживает PromQL (и расширяет его собственным языком MetricsQL) и принимает данные через Remote Write.

    Многие компании выбирают VictoriaMetrics за радикальную простоту эксплуатации: вы просто запускаете один процесс, направляете на него Remote Write со всех своих Prometheus-серверов, и получаете быстрое, надежное хранилище на годы вперед без необходимости нанимать отдельную команду для поддержки кластера.

    Резюме архитектурного выбора

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

  • Нужен единый дашборд для нескольких кластеров с минимальными затратами на инфраструктуру? Используйте Thanos Sidecar.
  • Строите SaaS-платформу, где клиенты хотят видеть свои метрики, и нужна строгая изоляция? Ваш выбор — Mimir.
  • Устали от нехватки памяти, хотите максимальной производительности и простоты? Внедряйте VictoriaMetrics.
  • Понимание того, как данные рождаются в оперативной памяти, защищаются через WAL, сжимаются алгоритмом Gorilla и безопасно перетекают в распределенные хранилища через Remote Write, отличает уверенного инженера от новичка. Эти знания позволят вам не только успешно пройти собеседование, но и спасти инфраструктуру компании в момент критического сбоя.

    14. Оптимизация производительности Prometheus

    Оптимизация производительности Prometheus

    На техническом собеседовании уровня Senior DevOps или SRE рано или поздно звучит вопрос: «Наш сервер Prometheus внезапно упал с ошибкой OOM (Out of Memory). Ваши действия?».

    В предыдущих статьях мы разобрали, как Prometheus собирает данные, сжимает их и отправляет в долговременное хранилище. Однако в реальной эксплуатации, особенно в крупных Kubernetes-кластерах, система мониторинга сама становится высоконагруженным приложением. Неправильно настроенный скрейпинг или один некорректный PromQL-запрос могут исчерпать десятки гигабайт оперативной памяти за считанные минуты.

    Разберем анатомию потребления ресурсов, научимся профилировать сервер и применять техники защиты от перегрузок.

    Анатомия потребления оперативной памяти

    Как мы выяснили ранее, свежие данные попадают в оперативную память. Эта область памяти называется Head block (Головной блок). Именно здесь кроется главная опасность для стабильности сервера.

    На собеседованиях часто путают размер сэмпла и размер временного ряда. Благодаря алгоритму Gorilla, один сэмпл (точка на графике) занимает всего 1.5–2 байта. Но чтобы Prometheus мог быстро дописывать новые сэмплы, он должен держать в памяти структуру данных для каждого активного временного ряда (уникальной комбинации имени метрики и лейблов).

    Каждый активный временной ряд в Head block потребляет примерно 3–4 Килобайта (KiB) оперативной памяти на внутренние индексы, указатели и буферы.

    Формула оценки базового потребления памяти:

    Где:

  • — объем оперативной памяти.
  • — количество активных временных рядов (Active Time Series).
  • Если ваша инфраструктура генерирует 10 миллионов активных рядов, только на поддержание их структур в памяти потребуется около 30 ГБ RAM, не считая кэшей запросов и операционной системы.

    Два всадника Апокалипсиса: Кардинальность и Churn

    С кардинальностью (взрывом уникальных лейблов) мы уже знакомы. Но есть вторая, более коварная проблема, специфичная для динамических сред вроде Kubernetes — Churn rate (Текучесть временных рядов).

    Представьте, что у вас есть микросервис, который отдает 1000 метрик. Он запущен в 10 подах (контейнерах). Итого: 10 000 активных рядов.

    Внезапно начинается CI/CD деплой, или поды начинают падать из-за ошибки (CrashLoopBackOff). При каждом перезапуске пода Kubernetes выдает ему новое имя (например, pod_name="api-v2-7x89q").

    Для Prometheus появление нового значения лейбла pod_name означает создание новых 10 000 временных рядов. При этом старые ряды (от убитых подов) перестают получать данные, но они не удаляются из оперативной памяти мгновенно. Они остаются в Head block до следующей фоновой компакции (которая происходит раз в 2 часа).

    > Кардинальность — это когда в ресторане заняты все столики. Текучесть (Churn) — это когда посетители за столиками меняются каждые пять минут, но ресторан обязан хранить грязную посуду каждого гостя до конца смены.

    Высокий Churn rate приводит к тому, что количество рядов в памяти растет лавинообразно, даже если фактическое количество работающих подов в любую секунду времени остается неизменным. Это самая частая причина OOM-падений Prometheus в Kubernetes.

    !Подвигайте ползунки — и увидите, как частые перезапуски приложений незаметно съедают всю оперативную память сервера мониторинга.

    Профилирование: как найти виновника

    Когда потребление памяти растет, действовать вслепую нельзя. Prometheus предоставляет встроенные инструменты для расследования инцидентов.

    TSDB Status API

    В веб-интерфейсе Prometheus (раздел Status -> TSDB Status) или через API доступна статистика по базе данных. Это первый инструмент, который нужно использовать при траблшутинге.

    Система автоматически вычисляет «Топ-10» по нескольким категориям:

  • Метрики с наибольшим количеством рядов (показывает жертв взрыва кардинальности).
  • Лейблы с наибольшим количеством уникальных значений (помогает найти проблемный лейбл, например, user_id или session_token).
  • Метрики, занимающие больше всего памяти.
  • Профилирование на уровне Go (pprof)

    Если TSDB Status показывает, что с метриками все в порядке, но сервер все равно тормозит, проблема может быть в процессорном времени (CPU) или утечках памяти в самом коде.

    Prometheus написан на Go и по умолчанию экспортирует эндпоинты pprof — стандартного профайлера языка Go. Обратившись к http://<prometheus-ip>:9090/debug/pprof/heap, вы получите дамп памяти, который можно проанализировать утилитой go tool pprof. Это позволяет увидеть, какие именно функции внутри бинарного файла Prometheus потребляют ресурсы (например, парсинг регулярных выражений или оценка конкретного правила алертинга).

    Стратегии снижения нагрузки

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

    1. Хирургическое удаление данных (Drop)

    Самый эффективный способ снизить потребление памяти — не пускать мусорные данные в TSDB. Для этого используется этап metric_relabel_configs (напомним, он работает с метриками после скрейпинга, но до сохранения в базу).

    Частый антипаттерн в Kubernetes — сбор метрик от kube-state-metrics или cAdvisor без фильтрации. Они генерируют огромные массивы данных, из которых на дашбордах используется от силы 5%.

    Действие labeldrop не удаляет сам временной ряд, оно лишь отрезает указанные лейблы. Если после отрезания лейблов несколько рядов становятся идентичными, Prometheus автоматически схлопнет их в один, радикально снизив кардинальность.

    2. Оптимизация интервалов и таймаутов

    Новички часто устанавливают scrape_interval: 5s (собирать метрики каждые 5 секунд) для всей инфраструктуры, желая получить максимальное разрешение графиков. Это увеличивает нагрузку на CPU и сеть в 3 раза по сравнению со стандартными 15 секундами, практически не давая пользы для бизнеса.

    > Лучшая практика: Разделяйте интервалы. Для критичных бизнес-метрик (RED-метод) используйте 10-15 секунд. Для инфраструктуры (диски, аптайм узлов) достаточно 60 секунд.

    Также важно понимать концепцию Scrape jitter (Разброс скрейпинга). Если у вас 10 000 таргетов, и все они настроены на интервал 15 секунд, есть риск возникновения Проблемы громового стада (Thundering herd problem) — ситуации, когда тысячи серверов одновременно отправляют ответы, вызывая пик нагрузки на сеть и CPU Prometheus.

    Prometheus автоматически добавляет случайный сдвиг (jitter) к интервалам скрейпинга для разных таргетов, размазывая нагрузку по времени. Однако, если вы установите scrape_timeout равным scrape_interval, вы сломаете этот механизм, так как таймауты начнут обрывать соединения до того, как сработает сдвиг.

    3. Защита от тяжелых запросов

    Даже идеально настроенный сбор метрик не спасет, если разработчик выполнит в Grafana запрос вида rate({__name__=~".+"}[30d]) (попытка вычислить скорость роста абсолютно всех метрик за месяц).

    Prometheus загрузит все эти данные с диска в оперативную память для вычислений, что неминуемо приведет к OOM. Для защиты сервера необходимо использовать лимиты на уровне конфигурации запуска бинарного файла:

  • --query.timeout=2m — принудительно прерывает выполнение любого PromQL-запроса, если он длится дольше 2 минут.
  • --query.max-samples=50000000 — ограничивает максимальное количество сэмплов, которые один запрос может загрузить в память. Если запрос превышает лимит, он прерывается с ошибкой 503, спасая сервер от падения.
  • !Схема защиты Prometheus от тяжелых запросов — лимиты прерывают выполнение до того, как исчерпается оперативная память.

    Масштабирование скрейпинга

    Если оптимизация конфигурации больше не помогает, а вертикальное масштабирование (добавление RAM в сервер) уперлось в физические лимиты или бюджет, применяется горизонтальное масштабирование.

    Как мы обсуждали ранее, для этого используется шардирование таргетов через математическую операцию hashmod. Пул серверов (например, 10 000 подов) делится на несколько независимых серверов Prometheus. Каждый сервер собирает только свою часть (например, по 2 500 подов).

    Важный нюанс для собеседований: при шардировании вы теряете глобальную видимость. Вы больше не можете выполнить PromQL-запрос, который агрегирует данные со всех 10 000 подов, так как данные лежат на разных серверах. Для решения этой проблемы поверх шардированных инстансов обязательно разворачивается система глобального чтения (например, Thanos Querier или иерархическая федерация), которая будет выполнять MapReduce-запросы, собирая ответы от всех шардов на лету.

    Умение балансировать между детализацией данных, потреблением ресурсов и стабильностью системы — ключевой навык Senior-инженера. Понимание того, как Churn rate забивает Head block, и умение вовремя применить labeldrop или ограничить query.max-samples, позволит вам уверенно управлять мониторингом любого масштаба.

    15. Масштабирование и федерация серверов

    Масштабирование и федерация серверов

    На техническом собеседовании уровня Senior SRE часто моделируют следующую ситуацию: «Ваш сервер Prometheus потребляет 128 ГБ оперативной памяти, CPU загружен на 100%, а OOM-киллер регулярно завершает процесс. Оптимизация запросов и удаление мусорных метрик больше не помогают, так как инфраструктура выросла в пять раз. Как вы будете масштабировать систему?»

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

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

    Пределы вертикального масштабирования

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

    Вертикальный потолок — это физическое ограничение ресурсов одного сервера (обычно около 10-15 миллионов активных временных рядов), при достижении которого монолитная архитектура начинает деградировать. Даже если вы установите сервер с 1 ТБ оперативной памяти, вы столкнетесь с ограничениями сборщика мусора (Garbage Collector) языка Go и блокировками при записи на диск (Write-Ahead Log).

    Когда вертикальный потолок достигнут, инфраструктуру необходимо дробить. Самый простой способ — разнести мониторинг по логическим контурам (например, отдельный Prometheus для базы данных, отдельный для frontend-сервисов). Но это порождает новую проблему: как построить единый дашборд, если данные размазаны по десяткам независимых серверов?

    Иерархическая федерация (Hierarchical Federation)

    Нативный способ объединить данные из нескольких серверов Prometheus — использовать механизм федерации.

    Иерархическая федерация позволяет одному (глобальному) серверу Prometheus выполнять скрейпинг метрик с других (локальных) серверов Prometheus через специальный HTTP-эндпоинт /federate.

    Как это работает на практике

    Представьте, что у вас есть 50 кластеров Kubernetes в разных регионах. В каждом кластере работает свой локальный Prometheus, который собирает метрики с подов каждые 15 секунд.

    Вы разворачиваете один Глобальный Prometheus. Его задача — не ходить к каждому поду (это невозможно из-за сетевых доступов и объема данных), а раз в минуту опрашивать локальные серверы.

    Для настройки федерации в конфигурации глобального сервера используется стандартный блок scrape_configs, но с обязательным параметром match[]:

    Параметр honor_labels: true критически важен. Он указывает глобальному серверу не перезаписывать лейблы job и instance, пришедшие от локального сервера, своими собственными. Без этого параметра все метрики выглядели бы так, будто их сгенерировал сам локальный Prometheus, а не исходные приложения.

    Антипаттерн полной федерации

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

    > Антипаттерн полной федерации — архитектурная ошибка, при которой глобальный сервер запрашивает через /federate абсолютно все временные ряды без фильтрации. Это приводит к тому, что глобальный сервер падает от нехватки памяти (OOM) быстрее, чем локальные, так как он пытается аккумулировать нагрузку всей инфраструктуры.

    Правильный подход заключается в федерации только агрегированных данных. Локальные серверы должны использовать Правила записи (Recording Rules), чтобы предварительно вычислить тяжелые метрики (например, среднюю загрузку CPU по кластеру). Глобальный сервер забирает только эти готовые агрегаты (в нашем примере выше это метрики, начинающиеся с cluster:).

    Математика экономии проста. Если локальный кластер генерирует 1 000 000 сырых рядов, а правила записи агрегируют их в 1 000 рядов уровня кластера, то объем передаваемых данных снижается в 1000 раз.

    Расчет пропускной способности для федерации:

    Где:

  • — требуемая пропускная способность (байт/сек).
  • — количество передаваемых временных рядов.
  • — средний размер одного сэмпла в текстовом формате OpenMetrics (около 100 байт).
  • — интервал скрейпинга (в секундах).
  • !Подвигайте ползунки — и увидите, как предварительная агрегация на локальных серверах спасает глобальный Prometheus от перегрузки сети и памяти.

    Отказоустойчивость (HA) и проблема рассинхронизации

    Федерация решает проблему масштабирования, но не решает проблему отказоустойчивости. Если локальный Prometheus упадет, мы потеряем данные.

    Стандартный паттерн High Availability (HA) для Prometheus — запуск двух абсолютно идентичных серверов (Реплика А и Реплика Б), которые независимо друг от друга собирают метрики с одних и тех же таргетов.

    Однако возникает проблема при чтении данных. Если Grafana отправит запрос к Реплике А, а та была недоступна последние 5 минут, на графике будет дыра. Если Grafana отправит запрос к обеим репликам одновременно, она получит дублирующиеся точки, которые могут слегка отличаться по времени (из-за разброса скрейпинга — Scrape jitter).

    Для решения этой проблемы требуется слой дедупликации на этапе выполнения запроса.

    PromQL-прокси: легковесное масштабирование

    Если внедрение тяжеловесных LTS-решений (вроде Thanos) избыточно для вашей компании, индустрия предлагает элегантное решение — Promxy.

    Promxy — это легковесный прокси-сервер, который понимает язык PromQL. Он устанавливается между Grafana и вашими серверами Prometheus.

    Механизм Fan-out запросов

    Когда пользователь открывает дашборд, Promxy принимает запрос и выполняет Fan-out (веерную рассылку).

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

    Как Promxy обрабатывает HA-пары:

  • Получает запрос от Grafana.
  • Отправляет его одновременно в Реплику А и Реплику Б.
  • Получает два массива временных рядов.
  • Выполняет дедупликацию: если в Реплике А есть пропуск (сервер перезагружался), Promxy бесшовно заполняет эту дыру данными из Реплики Б.
  • Отдает Grafana единый, непрерывный график.
  • Кроме того, Promxy умеет работать с шардированными серверами. Если вы разделили 10 000 таргетов на два сервера (по 5 000 на каждый) с помощью hashmod, Promxy отправит запрос на оба сервера, склеит результаты и выполнит математические агрегации (например, sum) уже над общим набором данных. Это возвращает вам глобальную видимость инфраструктуры.

    Распределенные кластеры: VictoriaMetrics и Mimir

    Когда счет активных метрик идет на десятки миллионов, а хранить их нужно годами, на сцену выходят специализированные кластерные решения. В отличие от Thanos, который работает как надстройка над Prometheus (через паттерн Sidecar), системы вроде VictoriaMetrics и Grafana Mimir полностью заменяют собой TSDB Prometheus.

    Они используют механизм Remote Write: серверы Prometheus превращаются в «глупые» агенты (или заменяются на легковесный vmagent), единственная задача которых — собрать метрики и немедленно отправить их (Push) по сети в кластер хранения.

    Архитектура VictoriaMetrics Cluster

    VictoriaMetrics славится своей феноменальной производительностью и низким потреблением ресурсов. Кластерная версия состоит из трех независимых микросервисов, каждый из которых можно масштабировать отдельно:

  • vminsert — слой приема данных. Принимает входящий поток Remote Write от агентов. Его главная задача — определить, на какой именно узел хранения нужно записать конкретный временной ряд. Для этого он вычисляет консистентный хеш от имени метрики и ее лейблов.
  • vmstorage — слой хранения (база данных). Сохраняет метрики на диск. Это stateful-компонент (требует постоянных дисков). Благодаря алгоритмам сжатия, vmstorage часто занимает на диске в 2-3 раза меньше места, чем оригинальный Prometheus.
  • vmselect — слой обработки запросов. Принимает PromQL-запросы от Grafana, выполняет Fan-out запросы ко всем узлам vmstorage, агрегирует полученные данные и возвращает результат.
  • !Архитектура кластера VictoriaMetrics: данные от агентов поступают в балансировщик vminsert, распределяются по узлам vmstorage, а запросы от Grafana обрабатываются через vmselect, который собирает данные со всех хранилищ.

    Такое разделение (Read/Write separation) позволяет гибко управлять ресурсами. Если у вас резко вырос объем входящих метрик (например, начался DDoS-атака и взлетела кардинальность логов), вы масштабируете только vminsert и vmstorage. Если аналитики начали строить тяжелые отчеты за год — вы добавляете поды vmselect.

    Мультитенантность в Grafana Mimir и Cortex

    Если VictoriaMetrics фокусируется на производительности, то проекты Cortex (и его форк Grafana Mimir) создавались с прицелом на Мультитенантность (Multi-tenancy).

    В крупных корпорациях (или облачных провайдерах) мониторингом пользуются десятки независимых команд. Сливать метрики биллинга и метрики тестового стенда стажеров в одну базу — небезопасно.

    Мультитенантность позволяет логически разделить один физический кластер на множество изолированных «арендаторов» (tenants). Данные одной команды криптографически изолированы от данных другой, хотя физически они могут лежать на одном жестком диске.

    Реализуется это через Мультитенантный заголовок (Multi-tenancy header). При отправке данных через Remote Write, агент обязан добавить HTTP-заголовок (обычно X-Scope-OrgID: team-a). Кластер Mimir читает этот заголовок и прикрепляет его ко всем входящим метрикам.

    Когда пользователь из team-a делает запрос через Grafana, прокси-сервер автоматически подставляет этот же заголовок в запрос чтения. Mimir на уровне ядра базы данных отфильтрует все метрики, гарантируя, что пользователь увидит только свои данные. Это избавляет от необходимости поднимать отдельный кластер мониторинга для каждой новой команды.

    Сравнительная таблица архитектур

    | Характеристика | Prometheus + Promxy | VictoriaMetrics Cluster | Grafana Mimir | | :--- | :--- | :--- | :--- | | Сложность внедрения | Низкая | Средняя | Высокая | | Модель хранения | Локальные диски серверов | Локальные диски (vmstorage) | Объектное хранилище (S3) | | Мультитенантность | Нет | Поддерживается (через URL) | Нативная (через HTTP-заголовки) | | Идеальный сценарий | До 10 млн метрик, нужен HA без S3 | Высокая скорость записи, экономия RAM/CPU | Облачные провайдеры, сотни независимых команд |

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

    16. Траблшутинг и мониторинг самого Prometheus

    Траблшутинг и мониторинг самого Prometheus

    На технических собеседованиях уровня Senior SRE часто звучит классический вопрос, восходящий к римскому поэту Ювеналу: «Quis custodiet ipsos custodes?»«Кто будет сторожить самих сторожей?».

    Вы построили идеальную архитектуру: настроили экспортеры, написали сотни правил оповещения, развернули кластеры хранения. Но что произойдет, если сервер Prometheus, который должен уведомить вас о падении базы данных, упадет сам? Или если его внутренние очереди переполнятся, и он начнет молча отбрасывать метрики?

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

    Архитектура Мета-мониторинга

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

    Существует два основных подхода к мониторингу Prometheus:

  • Self-scraping (Самоскрейпинг). Prometheus по умолчанию отдает свои собственные метрики по адресу http://localhost:9090/metrics. Вы можете настроить его собирать данные с самого себя. Это базовый подход, который подходит для небольших инсталляций. Его главный минус очевиден: если процесс зависнет или упадет с ошибкой нехватки памяти, вы не получите алерт о том, что мониторинг сломался.
  • Внешний кластер мета-мониторинга. Индустриальный стандарт для крупных компаний. Разворачивается отдельный, минималистичный сервер Prometheus (часто в другом дата-центре или облачном регионе), который собирает метрики с основных серверов (production, staging, dev). Этот сервер не хранит бизнес-метрики, его задача — следить за тем, чтобы основные серверы были доступны, их базы данных не деградировали, а правила выполнялись вовремя.
  • При проектировании такой системы важно учитывать Эффект наблюдателя — ситуацию, при которой сама система мониторинга создает избыточную нагрузку на целевой сервис, искажая его работу. Если ваш мета-мониторинг будет запрашивать слишком много тяжелых метрик у основного Prometheus каждую секунду, он сам станет причиной его падения.

    Золотые сигналы здоровья Prometheus

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

    1. Здоровье скрейпинга (Сбор данных)

    Процесс сбора метрик управляется внутренним механизмом. Цикл скрейпинга — это фоновый рабочий процесс (worker) внутри Prometheus, который отвечает за инициацию HTTP-запроса к конкретному таргету, ожидание ответа, парсинг текстового формата и передачу данных в базу.

    Ключевые метрики для траблшутинга скрейпинга:

    * up — базовая метрика доступности таргета. Но в контексте мета-мониторинга нас интересует up{job="prometheus"}. * prometheus_target_interval_length_seconds — измеряет фактическое время между скрейпингами. Если вы задали интервал в 15 секунд, а эта метрика показывает 30 секунд, значит, сервер перегружен и не успевает обходить все таргеты. * prometheus_target_scrapes_exceeded_sample_limit_total — счетчик таргетов, которые превысили лимит sample_limit в конфигурации. Это ваш первый индикатор того, что какое-то приложение начало отдавать слишком много данных.

    Частая проблема на собеседованиях: «Таргет доступен через curl, но Prometheus показывает его как DOWN. Почему?»

    Ответ кроется в параметре Тайм-аут скрейпинга. Это максимально допустимое время, в течение которого целевая система должна ответить на HTTP-запрос сервера мониторинга. По умолчанию он равен 10 секундам. Если приложение выполняет тяжелый запрос к базе данных, чтобы собрать метрики, и отвечает за 11 секунд — Prometheus прервет соединение и пометит таргет как недоступный, даже если сеть и само приложение работают нормально.

    2. Здоровье TSDB (Хранение данных)

    База временных рядов — самый ресурсоемкий компонент. Проблемы здесь обычно связаны с дисковой подсистемой (I/O) или оперативной памятью.

    * prometheus_tsdb_head_samples_appended_total — счетчик успешно записанных сэмплов в память. Используя функцию rate() на этой метрике, вы получите реальный Ingestion Rate сервера. * prometheus_tsdb_wal_fsync_duration_seconds — гистограмма времени синхронизации журнала упреждающей записи с диском. Если 99-й процентиль этой метрики превышает 1 секунду, ваш диск (обычно сетевой том в облаке) не справляется с потоком записи. Это приведет к росту потребления RAM, так как данные не могут быть сброшены на диск. * prometheus_tsdb_compactions_failed_total — счетчик неудачных попыток объединения блоков данных. Если он растет, возможно, на диске закончилось свободное место, или файлы были повреждены.

    3. Здоровье вычислений (Правила и Алерты)

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

    Математика отказа проста. Пусть — время выполнения запроса, а — интервал запуска (например, 60 секунд). Если , следующее выполнение правила ставится в очередь. Очередь начинает расти, потребление CPU упирается в 100%, и система перестает генерировать новые алерты.

    Метрики для отслеживания: * prometheus_rule_evaluation_duration_seconds — гистограмма времени выполнения групп правил. Позволяет найти самые медленные запросы. * prometheus_rule_group_iterations_missed_total — счетчик пропущенных итераций. Если он больше нуля — у вас классическое перекрытие правил.

    !Подвигайте ползунки — и увидите, как тяжелые запросы ломают систему мониторинга, вызывая бесконечный рост очереди задач.

    Практический траблшутинг: Сценарии из жизни

    Рассмотрим типичные инциденты и алгоритмы их решения.

    Сценарий 1: Внезапный OOM (The Poison Pill)

    Симптомы: Prometheus потребляет всю доступную память, падает, перезапускается, читает данные с диска, снова потребляет всю память и падает. Графики прерываются.

    Причина: Одно из приложений начало генерировать уникальные лейблы (например, вставило user_id или trace_id в лейбл метрики HTTP-запросов). Это вызвало взрыв кардинальности.

    Решение в состоянии инцидента: Поскольку сервер постоянно падает, вы не можете использовать Grafana для поиска виновника. Вам нужно поймать момент, когда сервер только поднялся, и использовать TSDB Status API.

    TSDB Status API — это встроенный диагностический интерфейс Prometheus (доступен по пути /api/v1/status/tsdb или в веб-интерфейсе Status -> TSDB Status), который анализирует структуры данных в оперативной памяти и показывает топ-10 метрик с наибольшим количеством рядов и лейблов.

    Как только вы увидели в TSDB Status метрику-убийцу (например, http_requests_total с миллионом рядов), вы немедленно добавляете правило в конфигурацию скрейпинга для этого джоба:

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

    Сценарий 2: Утечки памяти и зависания (Профилирование)

    Симптомы: Потребление памяти растет медленно, но верно, в течение нескольких дней, несмотря на то, что количество таргетов и метрик не меняется. TSDB Status не показывает аномалий.

    Причина: Баг в самом коде Prometheus, неоптимальная работа с сетью или Утечка горутин.

    Утечка горутин — это специфическая для языка Go проблема производительности, при которой легковесные потоки (горутины) блокируются в ожидании ресурса (например, сетевого ответа без тайм-аута) и остаются в памяти навсегда, потребляя ресурсы процессора и RAM.

    Решение: Prometheus написан на Go и имеет встроенную поддержку pprof — стандартного профайлера языка. Он позволяет заглянуть внутрь работающего процесса без его остановки.

    Вы можете скачать дамп памяти (heap) прямо с работающего сервера:

    Затем с помощью утилиты go tool pprof проанализировать, какие именно функции в исходном коде Prometheus удерживают память. На собеседованиях упоминание pprof для траблшутинга сложных утечек сразу выделяет кандидата уровня Senior, так как демонстрирует понимание того, как инструмент работает под капотом.

    Сценарий 3: Медленный запуск (Replay WAL)

    Симптомы: После плановой перезагрузки (или падения) Prometheus недоступен по HTTP в течение 10-20 минут. В логах висят сообщения вида Replaying WAL.

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

    Решение:

  • Уменьшить время хранения WAL (по умолчанию данные хранятся до формирования 2-часового блока). Если сервер падает часто, можно настроить более частую компакцию, хотя это увеличит нагрузку на диск.
  • Убедиться, что диск обеспечивает достаточную скорость чтения (IOPS). В облачных средах (AWS EBS, GCP Persistent Disks) скорость чтения часто ограничена размером диска. Увеличение объема диска может парадоксальным образом ускорить запуск Prometheus за счет повышения лимита IOPS.
  • Чек-лист Senior SRE для Prometheus

    Подводя итог, архитектура надежного мониторинга требует проактивного подхода:

  • Разделяйте контуры: Никогда не мониторьте production-кластер тем же Prometheus, который в нем работает. Используйте мета-мониторинг.
  • Защищайтесь от приложений: Всегда устанавливайте sample_limit и body_size_limit в настройках скрейпинга. Лучше потерять метрики одного сломанного пода, чем уронить весь сервер мониторинга.
  • Следите за диском: Метрика wal_fsync_duration_seconds — ваш главный индикатор здоровья хранилища.
  • Контролируйте правила: Настройте алерты на prometheus_rule_group_iterations_missed_total. Если правила пропускают итерации, ваши бизнес-алерты могут не сработать вовремя.
  • Понимание этих внутренних механизмов позволяет не просто реагировать на инциденты, но и проектировать системы, которые способны пережить отказы отдельных компонентов без потери критической информации о состоянии инфраструктуры.

    17. Лучшие практики и антипаттерны мониторинга

    Лучшие практики и антипаттерны мониторинга

    На технических собеседованиях уровня Senior SRE или Lead DevOps разговор неизбежно переходит от механики работы инструментов к методологии их применения. Интервьюера больше не интересует, как написать запрос с функцией rate() — его интересует, зачем вы его пишете, кто будет на него реагировать и что произойдет, если система мониторинга начнет генерировать тысячи уведомлений в минуту.

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

    От метрик к бизнес-надежности: SLI, SLO и SLA

    Фундаментальный антипаттерн начинающих инженеров — попытка мониторить абсолютно все технические показатели (CPU, RAM, IOPS, количество горутин) и настраивать на них критические алерты. Этот подход ведет к тому, что дежурный инженер просыпается в 3 часа ночи из-за того, что на одном из серверов загрузка процессора достигла 95%, хотя система в целом продолжает успешно обслуживать пользователей.

    Зрелый подход, популяризованный инженерами Google SRE, базируется на трех метриках надежности, ориентированных на пользователя.

    SLI (Service Level Indicator) — это количественная мера качества предоставляемого сервиса с точки зрения клиента. Чаще всего это процент успешных взаимодействий от общего числа. Например, для веб-сервиса SLI может вычисляться как доля HTTP-запросов, завершившихся со статусом 2xx или 3xx за последние 5 минут. Для фоновой очереди задач — доля задач, обработанных быстрее 100 миллисекунд.

    SLO (Service Level Objective) — это внутреннее целевое значение для вашего SLI. Это та планка качества, которую команда инженеров обязуется поддерживать. Например, вы договариваетесь с продуктовой командой, что SLO для успешных оплат в интернет-магазине составляет 99.9% в месяц. Важно понимать, что SLO никогда не должно равняться 100%. Стремление к 100% надежности экспоненциально увеличивает стоимость инфраструктуры и полностью останавливает выпуск новых фич (ведь любой релиз — это риск).

    SLA (Service Level Agreement) — это внешний юридический контракт с клиентами, который включает в себя финансовые штрафы (возврат средств) в случае невыполнения обязательств. SLA всегда должно быть ниже, чем SLO. Если ваш SLA перед клиентами составляет 99.9%, ваш внутренний SLO должен быть настроен на 99.95%, чтобы у инженеров был запас времени на реакцию до того, как компания начнет терять деньги.

    Разница между 100% и вашим SLO образует Бюджет на ошибки (Error Budget) — максимально допустимое количество неудачных взаимодействий за отчетный период (обычно месяц или квартал).

    Математика бюджета на ошибки проста: .

    Если ваш сервис обрабатывает 1 000 000 запросов в месяц, а SLO равен 99.9%, ваш бюджет на ошибки составляет 1 000 неудачных запросов. Пока вы не исчерпали этот бюджет, команда может спокойно деплоить новые версии, проводить эксперименты и технические работы. Как только бюджет исчерпан — релизы замораживаются, и все силы бросаются на стабилизацию инфраструктуры.

    !Подвигайте ползунки — и увидите, как изменение SLO всего на доли процента радикально сокращает право на ошибку и время на починку сервиса.

    Антипаттерн: Усталость от алертов

    Усталость от алертов (Alert Fatigue) — это психологическое состояние дежурного инженера, при котором он перестает обращать внимание на уведомления системы мониторинга из-за их огромного количества, низкой значимости или постоянных ложных срабатываний (синдром «мальчика, который кричал: Волки!»).

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

    Чтобы избежать усталости от алертов, каждое правило оповещения должно проходить строгий фильтр из трех вопросов:

  • Это реально? Отражает ли алерт фактическую деградацию сервиса для пользователя, или это просто внутренний всплеск потребления ресурсов, с которым система справилась сама?
  • Требует ли это действий? Если дежурный инженер получает алерт, смотрит на график и говорит: «А, это нормально, само пройдет через 5 минут» — этот алерт должен быть немедленно удален.
  • Это срочно? Нужно ли будить человека ночью ради этой проблемы?
  • Маршрутизация по степени срочности

    Лучшая практика — разделять алерты на две категории с принципиально разными каналами доставки:

    * Критические инциденты (Симптомы): Пользователи не могут авторизоваться, платежи не проходят, бюджет на ошибки сгорает слишком быстро. Эти алерты отправляются в системы On-Call (PagerDuty, Opsgenie) и будят дежурного звонком на телефон. * Предупреждения (Причины): На сервере базы данных осталось 15% свободного места на диске, один из трех узлов кэша недоступен (но кластер работает). Эти события не требуют немедленной реакции в 3 часа ночи. Для них применяется Тикетный алертинг (Ticket-based alerting) — Alertmanager автоматически создает задачу в Jira или YouTrack, которую команда разберет утром в рабочем порядке.

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

    Лучшая практика: Контекст и Плейбуки

    Худший алерт, который может получить инженер ночью, выглядит так: FIRING: HighCPUUsage (Instance: 10.0.5.12)

    Этот текст не дает никакого контекста. Что это за сервер? За какой сервис он отвечает? Насколько это критично? Что нужно сделать?

    Каждое правило оповещения в Prometheus должно сопровождаться ссылкой на Плейбук (Runbook) — пошаговую инструкцию по локализации и устранению конкретного инцидента. Плейбуки обычно хранятся во внутренней Wiki (Confluence, Notion) или прямо в репозитории с кодом.

    Идеальная аннотация алерта должна отвечать на вопросы: * Что сломалось? (Сервис корзины покупок деградировал). * Каково влияние на бизнес? (Пользователи не могут добавлять товары, мы теряем примерно 500 долл. в минуту). * Где посмотреть детали? (Ссылка на конкретный дашборд в Grafana с уже отфильтрованными лейблами). * Что делать прямо сейчас? (Ссылка на плейбук с командами для перезапуска или масштабирования).

    Пример качественной конфигурации в Prometheus:

    ```yaml

  • alert: CheckoutServiceHighErrorRate
  • expr: rate(http_requests_total{service="checkout", status=~"5.."}[5m]) / rate(http_requests_total{service="checkout"}[5m]) > 0.05 for: 2m annotations: summary: "Сервис чекаута возвращает > 5% ошибок" impact: "Пользователи не могут завершить покупку." dashboard: "https://grafana.internal/d/checkout?var-cluster={{ Burn\ Rate = \frac{Current\ Error\ Rate}{Allowed\ Error\ Rate}Burn\ Rate = \frac{1\%}{0.1\%} = 10$.

    Это означает, что ваш бюджет на ошибки сгорает в 10 раз быстрее нормы. Если такая ситуация продлится 72 часа (10% от месяца), вы полностью исчерпаете месячный лимит.

    Вместо того чтобы писать десятки правил для разных порогов, SRE-инженеры настраивают мульти-оконный Burn Rate алертинг. Например: * Если Burn Rate > 14.4 в течение 1 часа (бюджет сгорит за 2 дня) — отправляем критический алерт дежурному (Page). * Если Burn Rate > 1 в течение 3 дней (бюджет сгорает чуть быстрее нормы) — создаем тикет в бэклог команды для расследования деградации.

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

    Резюме инженерного подхода

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

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

    18. Сценарии собеседований: архитектурные вопросы

    Сценарии собеседований: архитектурные вопросы

    На технических собеседованиях уровня Middle+ и Senior интервьюеры редко просят написать простой запрос на PromQL. Их цель — проверить ваше Архитектурное мышление (Architectural thinking). Они хотят увидеть, как вы принимаете решения в условиях неопределенности, как оцениваете компромиссы (trade-offs) и понимаете ли вы физические ограничения систем.

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

    Фреймворк ответа на архитектурный вопрос

    Когда вам дают абстрактную задачу («Спроектируйте мониторинг для глобального e-commerce»), худшее, что можно сделать — сразу начать рисовать квадратики с надписью «Prometheus». Зрелый инженер использует структурированный подход.

  • Уточнение требований (Clarification): Определение масштаба. Сколько дата-центров? Какой объем генерации метрик (Ingestion Rate)? Каковы требования к задержке алертов?
  • Определение Границ наблюдаемости (Observability Boundaries): Разделение системы на локальные зоны (где данные собираются) и глобальные зоны (где данные агрегируются для долгосрочного хранения и анализа).
  • Проектирование потока данных (Data Flow): Описание пути метрики от приложения до дашборда.
  • Анализ узких мест и компромиссов (Trade-offs): Честный рассказ о том, где предложенная архитектура сломается и сколько она будет стоить.
  • Рассмотрим применение этого фреймворка на реальных кейсах.

    Сценарий 1: Эфемерные нагрузки и взрыв кардинальности

    > Вопрос интервьюера: > У нас есть Kubernetes-кластер, в котором крутятся тысячи serverless-функций и CI/CD воркеров. Они живут от 5 до 30 секунд, выполняют задачу и умирают. Как вы организуете сбор метрик с этих подов, учитывая, что стандартный интервал скрейпинга Prometheus — 15-30 секунд?

    Ловушка для новичка

    Кандидат вспоминает, что для кратковременных задач существует Pushgateway, и предлагает отправлять метрики туда.

    Почему это ошибка: Как мы разбирали в первой статье, Pushgateway не предназначен для эфемерных сущностей, которые постоянно меняют свои IP-адреса или идентификаторы. Если тысячи подов будут пушить туда данные со своими уникальными лейблами (например, pod_name), Pushgateway сохранит их все навсегда. При следующем скрейпинге Prometheus заберет весь этот мусор, что приведет к мгновенному OOM (Out of Memory) из-за колоссального Churn rate.

    Решение уровня Senior

    Для решения этой задачи необходимо изменить парадигму сбора. Мы внедряем OpenTelemetry Collector как мост (OTel Bridge).

    Вместо того чтобы заставлять Prometheus гоняться за исчезающими подами, мы настраиваем эфемерные приложения на активную отправку (Push) метрик по протоколу OTLP (OpenTelemetry Protocol) в локальный OTel Collector, развернутый как DaemonSet на каждом узле.

    OTel Collector выполняет роль интеллектуального буфера:

  • Принимает Push-трафик от эфемерных подов.
  • Агрегирует метрики в памяти (например, суммирует счетчики).
  • Выставляет агрегированные данные на свой эндпоинт /metrics.
  • Prometheus спокойно скрейпит OTel Collector раз в 15 секунд.
  • Защита решения (Trade-offs): Плюсы:* Prometheus защищен от взрыва кардинальности; мы не теряем данные короткоживущих подов. Минусы:* Усложнение инфраструктуры (появление нового компонента); потеря гранулярности (мы видим агрегат по узлу, а не метрики конкретного пода, если специально не сохраним тяжелые лейблы).

    Сценарий 2: Глобальный мониторинг нескольких дата-центров

    > Вопрос интервьюера: > Наша компания открывает 5 новых дата-центров на разных континентах. Руководство хочет видеть единый дашборд с метриками со всего мира и получать глобальные алерты. Как вы это спроектируете?

    Ловушка для новичка

    Кандидат предлагает развернуть один гигантский кластер Prometheus (или Mimir) в центральном дата-центре и настроить скрейпинг всех таргетов по всему миру через WAN-каналы.

    Почему это ошибка: Скрейпинг через публичный интернет или межконтинентальные линки крайне нестабилен. Тайм-ауты приведут к появлению «дыр» на графиках. Более того, при падении центрального дата-центра или обрыве трансатлантического кабеля компания полностью ослепнет — локальные команды потеряют возможность мониторить свои сервисы.

    Решение уровня Senior

    Здесь применяется архитектурный паттерн Edge-Агрегация (Edge Aggregation) в сочетании с принципом локальной автономии.

    !Архитектура глобального мониторинга: локальные серверы собирают сырые данные, а в центр отправляются только агрегаты.

  • Локальная автономия: В каждом дата-центре разворачивается свой независимый HA-кластер Prometheus и свой Alertmanager. Локальные команды должны иметь возможность получать алерты и смотреть дашборды, даже если их дата-центр полностью отрезан от внешнего мира.
  • Edge-Агрегация: Локальные серверы собирают сырые метрики с высоким разрешением (например, каждые 10 секунд). С помощью Recording Rules они вычисляют агрегаты (например, средний rate ошибок за минуту на уровне сервиса, отбрасывая лейблы конкретных подов).
  • Глобальный уровень: Только эти легковесные агрегированные метрики отправляются в центральный дата-центр через механизм Remote Write (в Thanos Receiver или Mimir).
  • Защита решения (Trade-offs): Мы экономим до 90% межсетевого трафика (WAN bandwidth) и защищаем систему от сетевых сплитов (Split-brain). Компромисс заключается в том, что на глобальном уровне невозможно провести глубокий траблшутинг (drill-down) до конкретного контейнера — для этого инженеру придется переключиться на локальную Grafana конкретного дата-центра.

    Сценарий 3: Оптимизация стоимости хранения (Cost Optimization)

    > Вопрос интервьюера: > Наш счет за AWS S3, где хранятся исторические метрики за 5 лет, превысил 10 000 долларов в месяц. При этом 99% запросов к Grafana охватывают только последние 7 дней. Как радикально снизить стоимость хранения без потери возможности анализировать тренды год к году?

    Ловушка для новичка

    Предложить просто удалить старые данные (уменьшить Retention) или перевести их в холодное хранилище (AWS Glacier).

    Почему это ошибка: Бизнесу нужны исторические данные для capacity planning (планирования мощностей) и анализа сезонности (например, сравнение трафика в Черную пятницу в этом и прошлом году). Glacier не подходит, так как системы вроде Thanos не умеют работать с хранилищами, требующими часов на извлечение данных.

    Решение уровня Senior

    Оптимизация строится на двух механизмах: Даунсэмплинг (Downsampling) и агрессивная фильтрация на этапе скрейпинга.

    1. Даунсэмплинг в системах LTS (Long-Term Storage) Даунсэмплинг — это процесс искусственного снижения разрешения исторических временных рядов. Нет смысла хранить данные пятилетней давности с шагом в 15 секунд.

    В архитектуре Thanos за это отвечает компонент Compactor. Он берет сырые блоки данных и создает из них новые: * Данные старше 30 дней: разрешение снижается до 5 минут (сжатие в 20 раз). * Данные старше 90 дней: разрешение снижается до 1 часа (сжатие в 240 раз).

    При этом Thanos сохраняет математическую корректность: для каждого часового интервала он сохраняет не просто одну случайную точку, а агрегаты min, max, sum и count. Это позволяет функциям вроде rate() работать корректно даже на даунсэмплированных данных.

    2. Drop-правила на этапе скрейпинга Часто разработчики случайно добавляют в метрики высококардинальные лейблы (например, user_id или session_id). Чтобы эти данные вообще не попадали на диск, настраивается metric_relabel_configs с действием drop для конкретных метрик или labeldrop для удаления токсичных лейблов прямо в оперативной памяти до записи в WAL.

    Сценарий 4: Математика надежности мониторинга

    > Вопрос интервьюера: > Как вы гарантируете, что система мониторинга не упадет вместе с приложением, которое она мониторит?

    Решение уровня Senior

    Здесь необходимо продемонстрировать понимание концепции Изоляции доменов отказа (Failure Domain Isolation).

    Домен отказа — это логическая или физическая граница инфраструктуры, внутри которой сбой одного компонента может привести к отказу других. Если Prometheus запущен на тех же узлах (worker nodes), что и бизнес-приложение, и делит с ним одну сеть и один сторадж, то при падении сети или переполнении диска упадет и приложение, и мониторинг.

    Правильная архитектура требует выноса мониторинга в отдельный домен отказа: * Отдельные выделенные узлы (Dedicated nodes) или отдельный кластер Kubernetes. * Независимая сетевая маршрутизация. * Разные зоны доступности (Availability Zones) облачного провайдера.

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

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

    Подставим значения: .

    Изолировав домены отказа, мы снизили риск «слепого падения» в 100 раз, не улучшая при этом код ни самого приложения, ни системы мониторинга.

    !Подвигайте ползунки надежности приложения и мониторинга — и увидите, как изоляция доменов отказа математически снижает риск пропустить критическую аварию.

    Сценарий 5: Защита от «Громового стада» при рестартах

    > Вопрос интервьюера: > У нас произошел масштабный сетевой сбой. 5000 серверов одновременно потеряли связь с базой данных и начали генерировать миллионы ошибок в секунду. Как только сеть восстановилась, Prometheus упал. Что произошло и как это предотвратить?

    Анализ проблемы

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

    Когда 5000 серверов одновременно начали сыпать ошибками, появились тысячи новых временных рядов (с новыми лейблами типов ошибок). Prometheus попытался одновременно создать структуры для них в Head block (оперативной памяти). Кроме того, резко возросла нагрузка на сеть, так как размер payload'а при скрейпинге увеличился в десятки раз.

    Решение уровня Senior

    Для защиты сервера мониторинга от подобных штормов применяются следующие архитектурные ограничения:

  • Лимиты на размер ответа (Body Size Limits): Настройка параметра sample_limit в конфигурации скрейпинга. Если таргет внезапно отдает больше, например, 10 000 сэмплов за один раз, Prometheus просто отбросит этот ответ, пожертвовав метриками одного сломанного узла ради выживания всего кластера мониторинга.
  • Ограничение длины очереди (Queue Capacity): При использовании Remote Write настраиваются жесткие лимиты на max_shards и capacity. Если внешнее хранилище не справляется с потоком новых ошибок, Prometheus начнет отбрасывать данные (drop data), чтобы не исчерпать оперативную память.
  • Деградация вместо отказа (Graceful Degradation): Внедрение правил, при которых в момент шторма тяжелые Recording Rules автоматически приостанавливаются, высвобождая CPU для базового скрейпинга.
  • Резюме

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

    Вы должны уметь объяснить, почему в одном случае лучше потерять гранулярность данных ради стабильности (Edge-Агрегация), в другом — пожертвовать свежестью исторических данных ради экономии денег (Даунсэмплинг), а в третьем — усложнить инфраструктуру ради сбора метрик с эфемерных задач (OTel Bridge). Именно способность видеть систему целиком, от генерации байта в приложении до счета от облачного провайдера, отличает Senior-инженера.

    19. Сценарии собеседований: задачи на PromQL

    Сценарии собеседований: задачи на PromQL

    На технических собеседованиях уровня Middle+ и Senior проверка знаний Prometheus Query Language редко сводится к просьбе написать простой запрос. Интервьюеры используют PromQL как инструмент для оценки вашего аналитического мышления, понимания структур данных и умения предвидеть поведение системы в граничных случаях (edge cases).

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

    Сценарий 1: Расчет SLA и модификатор bool

    > Интервьюер: > Бизнес требует вывести на дашборд показатель SLA (Service Level Agreement) нашего сервиса за последние 30 дней. Условие SLA: процент времени, в течение которого уровень ошибок (HTTP 5xx) был строго меньше 1%. Как вы напишете такой запрос?

    Ловушка для новичка

    Кандидат пытается использовать агрегацию по времени напрямую на отфильтрованном векторе: avg_over_time(rate(http_requests_total{status=~"5.."}[5m]) < 0.01 [30d:5m])

    Этот запрос выдаст синтаксическую ошибку. Операторы сравнения (<, >) в PromQL по умолчанию работают как фильтры: они просто удаляют из результата те временные ряды, которые не соответствуют условию. Функция avg_over_time не может посчитать долю успешных периодов, потому что неуспешные периоды просто исчезают из выборки.

    Решение уровня Senior

    Для решения этой задачи необходимо использовать Модификатор bool. Если добавить ключевое слово bool после оператора сравнения, PromQL перестает фильтровать ряды. Вместо этого он возвращает 1, если условие истинно, и 0, если ложно.

    Формула расчета SLA выглядит так:

    Шаг 1. Превращаем фильтр в бинарный поток нулей и единиц: rate(http_requests_total{status=~"5.."}[5m]) < bool 0.01

    Шаг 2. Оборачиваем это в сабквери (которое мы разбирали в предыдущих статьях), чтобы получить вектор диапазона за 30 дней с шагом оценки в 5 минут, и вычисляем среднее значение: avg_over_time( (rate(http_requests_total{status=~"5.."}[5m]) < bool 0.01)[30d:5m] ) * 100

    Если сервис соответствовал условию 99% времени, среднее значение нулей и единиц составит 0.99, что после умножения на 100 даст искомые 99% SLA.

    Сценарий 2: Проблема мерцающих графиков и функция topk

    > Интервьюер: > У нас кластер на 500 подов. Я хочу вывести на дашборд график (Time Series panel), показывающий топ-3 пода по потреблению CPU в любой момент времени. Я написал запрос: topk(3, rate(container_cpu_usage_seconds_total[5m])). Но график выглядит ужасно: линии постоянно обрываются, появляются новые, читать это невозможно. Почему так происходит и как это исправить?

    Анализ проблемы

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

    В 12:00 в топ-3 входили поды A, B и C. В 12:01 под D получил всплеск трафика и вытеснил под C. Теперь в топе A, B и D.

    На графике линия пода C резко оборвется, а линия пода D появится из ниоткуда. Это явление называется Проблемой мерцающих графиков (Flapping graphs). Такие визуализации абсолютно бесполезны для анализа трендов.

    Решение уровня Senior

    Кандидат должен объяснить, что topk нельзя использовать для построения линейных графиков. Существует два правильных архитектурных подхода:

  • Изменение типа визуализации: Использовать topk только для таблиц (Table panel) или списков (Bar gauge), где отображается только последнее мгновенное значение (Instant query), а не история за период.
  • Предварительная фильтрация через PromQL: Если линейный график необходим, нужно сначала найти топ-3 пода за весь период, а затем построить графики их потребления, не обрывая линии.
  • Реализация второго подхода через вложенные запросы:

    Примечание: В реальности Grafana не поддерживает динамическую подстановку результатов запроса прямо в селектор лейблов PromQL таким образом. Поэтому Senior-инженер предложит использовать переменные Grafana (Query Variables): создать переменную top_pods".

    Сценарий 3: Обогащение метрик и потеря лейблов

    > Интервьюер: > У нас есть метрика бизнес-транзакций transactions_total{client_id="123"}. И есть отдельная информационная метрика из базы данных client_info{client_id="123", tier="premium", region="eu"}. Напишите запрос, который посчитает суммарный rate транзакций, но сгруппированный по tier (уровню подписки).

    Ловушка для новичка

    Кандидат помнит про сопоставление векторов и модификатор group_left, который мы изучали ранее, и пишет: sum by (tier) (rate(transactions_total[5m]) * on(client_id) group_left(tier) client_info)

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

    Решение уровня Senior

    Информационные метрики (паттерн Info metrics) обычно экспортируются со значением 1. Однако, если значение client_info равно, например, балансу клиента, умножение исказит rate транзакций.

    Безопасный паттерн обогащения требует умножения на 1, извлеченную из информационной метрики:

    sum by (tier) (rate(transactions_total[5m]) on(client_id) group_left(tier) (client_info 0 + 1))

    !Механика сопоставления векторов: модификатор group_left переносит лейбл tier из информационного вектора в вектор с метриками, сохраняя математическую корректность исходных данных.

    Еще более элегантный способ, показывающий глубокое знание PromQL — использование Функции label_replace. Эта функция позволяет создавать, изменять или переносить значения лейблов внутри одного временного ряда с помощью регулярных выражений.

    Синтаксис: label_replace(v instant-vector, dst_label string, replacement string, src_label string, regex string)

    Хотя label_replace не решит задачу объединения двух разных метрик (здесь нужен именно group_left), интервьюер часто просит продемонстрировать знание этой функции для задач форматирования. Например, извлечь версию API из пути: label_replace(http_requests, "api_version", "$1", "path", "/api/(v[0-9]+)/.*").

    Сценарий 4: Сдвиг во времени и жесткая привязка

    > Интервьюер: > Вы расследуете инцидент. Вам нужно сравнить текущее потребление памяти с тем, что было ровно неделю назад. А затем сравнить его с потреблением в момент релиза (1 января 2024 года в 12:00). Какие инструменты PromQL вы используете?

    Решение уровня Senior

    Для первой части задачи (сравнение с прошлой неделей) используется модификатор offset, который сдвигает окно оценки в прошлое относительно текущего момента выполнения запроса: process_resident_memory_bytes offset 1w

    Однако offset всегда относителен. Если вы обновите дашборд через час, историческое окно тоже сдвинется на час вперед.

    Для второй части задачи (привязка к моменту релиза) применяется Модификатор @ (At modifier). Он позволяет жестко зафиксировать время вычисления вектора, используя Unix timestamp, независимо от того, когда выполняется запрос.

    process_resident_memory_bytes @ 1704110400 (где 1704110400 — это Unix-время для 01.01.2024 12:00).

    Комбинируя эти модификаторы, можно строить сложные аналитические запросы. Например, узнать, насколько текущее потребление превышает потребление в момент релиза: process_resident_memory_bytes - process_resident_memory_bytes @ 1704110400

    !Подвигайте ползунок времени — и увидите разницу между относительным сдвигом (offset) и жесткой привязкой (@ modifier).

    Сценарий 5: Ловушка функции absent

    > Интервьюер: > У нас есть cron-джоба, которая делает бэкап базы данных каждый час и пушит метрику backup_last_success_timestamp_seconds{db="users", env="prod"}. Напишите алерт, который сработает, если метрика перестанет поступать.

    Ловушка для новичка

    Кандидат пишет классический запрос для проверки отсутствия данных: absent(backup_last_success_timestamp_seconds{db="users", env="prod"})

    Почему это приведет к катастрофе на проде: Функция absent возвращает вектор со значением 1, если переданный ей вектор пуст. Но есть критический нюанс: если исходный вектор пуст, absent генерирует новый ряд, сохраняя только те лейблы, которые были явно указаны в селекторе с оператором точного совпадения (=).

    Если вы напишете absent(backup_last_success_timestamp_seconds{env=~"prod|stage"}), сгенерированный алерт вообще не будет иметь лейбла env, так как использовался оператор регулярного выражения (=~). Alertmanager получит «голый» алерт и не сможет смаршрутизировать его нужной команде (например, отправить prod-алерт дежурному, а stage-алерт — в Slack разработчикам).

    Решение уровня Senior

    Проблема решается двумя путями.

    Путь 1: Использование absent_over_time Если метрика отправляется редко (cron-джобы), мгновенный вектор может быть пустым просто между запусками. Нужно проверять отсутствие данных за период: absent_over_time(backup_last_success_timestamp_seconds{db="users", env="prod"}[2h])

    Путь 2: Сохранение контекста через умножение Чтобы гарантированно сохранить все инфраструктурные лейблы (даже те, которые мы не указывали в селекторе, например instance или cluster), опытные инженеры используют трюк с умножением на существующую метрику up:

    up{job="backup_service"} == 0

    Вместо того чтобы искать отсутствие специфичной метрики бэкапа, мы мониторим доступность самого сервиса, который ее генерирует. Метрика up всегда присутствует в системе (со значением 0 или 1) и содержит полный набор мета-лейблов.

    Искусство написания запросов

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

    Когда вы используете bool, вы превращаете непрерывные графики в дискретную логику для расчета бизнес-показателей. Когда вы отказываетесь от topk в пользу предварительной агрегации, вы спасаете браузеры пользователей от зависания при рендеринге мерцающих графиков. А понимание разницы между относительным offset и абсолютным @ позволяет проводить точный post-mortem анализ инцидентов.

    2. Модель данных и типы метрик

    Как мы разобрали ранее, сервер мониторинга самостоятельно опрашивает целевые системы и сохраняет полученные данные в локальную базу временных рядов. Но что именно он собирает? Чтобы писать эффективные запросы, настраивать алерты и не «положить» сервер мониторинга избыточными данными, необходимо досконально понимать, как структурирована информация внутри системы.

    На технических собеседованиях уровня middle и senior вопросы о структуре данных и выборе правильного типа метрики звучат в 100% случаев. Неправильный выбор типа метрики на этапе проектирования микросервиса приведет к тому, что вы не сможете получить нужную аналитику, а ошибки в проектировании размерности данных могут вызвать отказ всего кластера мониторинга.

    Многомерная модель данных

    Фундамент Prometheus — это многомерная модель данных (multi-dimensional data model). Любая информация, попадающая в базу, представляет собой временной ряд (time series), который однозначно идентифицируется двумя вещами: именем и набором пар ключ-значение.

    Базовый синтаксис выглядит так:

    имя_метрики{лейбл1="значение1", лейбл2="значение2"} значение временная_метка

    Разберем анатомию этой записи:

  • Имя метрики (Metric name) — описывает общую суть того, что измеряется. Имя должно быть говорящим и соответствовать стандартам именования. Например, http_requests_total (общее количество HTTP-запросов) или node_memory_Active_bytes (объем активной памяти в байтах).
  • Лейблы (Labels) — это те самые измерения (dimensions), которые делают модель многомерной. Они позволяют детализировать метрику. Например, запросы можно разделить по HTTP-методам (method="GET") и кодам ответа (status="200").
  • Значение (Value) — само числовое значение. Внутри базы данных Prometheus абсолютно все значения хранятся как 64-битные числа с плавающей запятой (float64).
  • Временная метка (Timestamp) — время с точностью до миллисекунды, когда было получено значение.
  • > Сила Prometheus заключается в его способности фильтровать и агрегировать данные по лейблам. Вы можете одним запросом получить сумму всех ошибок 500 по всем микросервисам, просто отфильтровав данные по лейблу status. > > DeepWiki

    Кардинальность: главный враг производительности

    Самый важный концепт, который проверяют на интервью при обсуждении модели данных — это кардинальность (Cardinality).

    Кардинальность — это общее количество уникальных временных рядов в вашей базе данных. Каждый уникальный набор лейблов создает совершенно новый временной ряд, который занимает место в оперативной памяти (в Head-блоке) и на диске.

    Для расчета кардинальности одной метрики используется формула перемножения:

    Где: * — итоговая кардинальность (количество уникальных рядов). * — количество уникальных значений для лейбла . * — общее количество лейблов у данной метрики.

    Пример из практики: Вы создали метрику http_requests_total. Вы добавили лейбл method (GET, POST, PUT — 3 значения) и лейбл status (200, 404, 500 — 3 значения). Кардинальность составит: временных рядов.

    Это отличный, безопасный дизайн. Но теперь представим типичную ошибку новичка: разработчик решает добавить лейбл user_id, чтобы знать, какой пользователь делает запросы. В системе 500 000 активных пользователей.

    Новая кардинальность: временных рядов только для одной метрики на одном экземпляре микросервиса!

    !Инфографика — взрыв кардинальности (Cardinality Explosion). Слева метрика 'http_requests'. От нее отходят 3 стрелки к лейблу 'method' (GET, POST, PUT). От каждого метода отходят по 3 стрелки к 'status' (200, 404, 500) — получается 9 комбинаций. От одной из них отходит огромный сноп из тысяч красных стрелок к лейблу 'user_id', ведущий к красному блоку 'Out of Memory (OOM)'. Подписи: 'Имя метрики', 'Лейблы с низкой кардинальностью', 'Лейбл с высокой кардинальностью'.

    Такая ситуация называется взрывом кардинальности (Cardinality Explosion). Она гарантированно приведет к исчерпанию оперативной памяти (OOM — Out of Memory) на сервере мониторинга.

    Золотое правило: никогда не используйте в качестве значений лейблов данные с высокой степенью изменчивости (ID пользователей, email-адреса, публичные IP-адреса клиентов, полные URL с параметрами запроса). Лейблы предназначены для конечного, небольшого набора состояний.

    Типы метрик: иллюзия на стороне клиента

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

    Клиентские библиотеки поддерживают четыре основных типа метрик. Формат передачи этих данных стандартизирован и сегодня чаще всего описывается спецификацией OpenMetrics (эволюция оригинального текстового формата Prometheus).

    1. Counter (Счетчик)

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

    Когда использовать: * Количество обработанных HTTP-запросов. * Количество возникших исключений (errors). * Общий объем отправленных байтов по сети. * Количество выполненных задач.

    Как это выглядит в формате OpenMetrics:

    Особенности работы (вопрос для собеседования): На графиках мы никогда не смотрим на абсолютное значение счетчика. Нам не интересно, что сервис обработал 1027 запросов с момента своего запуска три дня назад. Нам интересно, сколько запросов он обрабатывает прямо сейчас в секунду (RPS — Requests Per Second).

    Для этого в языке запросов используется функция rate(). Но что произойдет, если под в Kubernetes перезапустится? Значение счетчика упадет с 1027 до 0, а затем снова начнет расти (1, 2, 3...). Это называется сбросом счетчика (Counter reset).

    Функция rate() умеет распознавать такие сбросы. Если новое значение меньше предыдущего (), Prometheus понимает, что произошел рестарт, и математически компенсирует этот провал, складывая старое и новое значения, чтобы график скорости (RPS) оставался ровным и корректным.

    !Подвигайте ползунки — сырой счетчик постоянно растет и иногда сбрасывается в ноль при рестарте, но посмотрите, как функция rate() превращает этот хаос в ровный график нагрузки (RPS).

    2. Gauge (Датчик / Шкала)

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

    Когда использовать: * Текущее использование оперативной памяти или CPU. * Количество активных соединений с базой данных. * Размер очереди сообщений (например, в RabbitMQ или Kafka). * Температура процессора.

    Как это выглядит:

    Типичная ошибка: Применение функции rate() к типу Gauge. Так как Gauge может естественно уменьшаться (например, память освободилась после сборки мусора), функция rate() воспримет это уменьшение как сброс счетчика (рестарт) и выдаст абсолютно некорректный, завышенный результат. Для Gauge используются функции усреднения, такие как avg_over_time().

    3. Histogram (Гистограмма)

    Гистограмма — самый сложный и мощный тип метрик. Она используется для измерения распределения значений (чаще всего — времени ответа или размера запроса).

    Вместо того чтобы просто считать среднее время ответа (которое скрывает выбросы и реальные проблемы), гистограмма распределяет все наблюдения по заранее заданным корзинам — кумулятивным бакетам (Cumulative buckets).

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

  • _count — общее количество наблюдений (работает как Counter).
  • _sum — сумма всех наблюдаемых значений.
  • Множество рядов с суффиксом _bucket и лейблом le (less than or equal — меньше или равно).
  • Как это работает на практике: Допустим, мы измеряем время ответа API. Мы настроили бакеты: 0.1 сек, 0.5 сек, 1.0 сек. Приходит запрос, который выполнялся 0.25 секунды. Клиентская библиотека делает следующее: * Попадает ли 0.25 в бакет le="0.1"? Нет. * Попадает ли 0.25 в бакет le="0.5"? Да. Счетчик этого бакета увеличивается на 1. Попадает ли 0.25 в бакет le="1.0"? Да (0.25 меньше 1.0). Счетчик этого бакета тоже* увеличивается на 1. * Попадает ли 0.25 в бакет le="+Inf" (бесконечность)? Да. Увеличивается на 1.

    Почему бакеты кумулятивные (включают в себя значения предыдущих)? Это позволяет серверу легко вычислять процентили (например, 95-й процентиль — время, быстрее которого обрабатываются 95% запросов) с помощью функции histogram_quantile(), а также безопасно отбрасывать часть бакетов на этапе сбора данных (Metric Relabeling) для экономии места, не ломая математику оставшихся бакетов.

    4. Summary (Сводка)

    Сводка, как и гистограмма, используется для измерения распределений. Но она работает принципиально иначе: она вычисляет процентили (квантили) прямо на стороне клиента (в памяти вашего приложения), а серверу отдает уже готовые значения.

    Сравнение: Histogram против Summary

    Это классический вопрос на интервью для senior-инженеров: «У нас микросервисная архитектура, 50 реплик одного сервиса. Нам нужно измерять 99-й процентиль времени ответа. Что вы выберете: Histogram или Summary, и почему?»

    Правильный ответ: Только Histogram.

    Главная проблема Summary заключается в математическом законе: нельзя вычислить среднее значение от процентилей.

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

    Гистограммы же отдают сырые счетчики попаданий в бакеты. Сервер мониторинга может легко просуммировать счетчики одинаковых бакетов со всех 50 реплик (агрегация), и уже от этой глобальной суммы вычислить точный 99-й процентиль.

    | Характеристика | Histogram | Summary | | :--- | :--- | :--- | | Где считаются процентили? | На сервере (через PromQL) | На клиенте (в приложении) | | Агрегация по нескольким серверам | Да, математически корректна | Нет, невозможна | | Нагрузка на клиент (приложение) | Низкая (просто инкремент счетчиков) | Высокая (сложные алгоритмы в памяти) | | Нагрузка на сервер мониторинга | Высокая (вычисление квантилей) | Низкая (просто хранение чисел) | | Настройка бакетов | Требуется заранее угадать диапазоны | Не требуется |

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

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

    20. Сценарии собеседований: траблшутинг и алертинг

    Сценарии собеседований: траблшутинг и алертинг

    На позициях уровня Middle+ и Senior инженеру недостаточно уметь проектировать системы с нуля. В реальном мире системы ломаются, и бизнес теряет деньги каждую минуту простоя. Для проверки навыков работы в критических ситуациях компании используют особый формат технического собеседования — Troubleshooting-интервью.

    В отличие от классического System Design, где вы строите идеальную архитектуру по заданным требованиям, здесь вам дают уже работающую (и внезапно сломавшуюся) production-систему. Ваша цель — локализовать проблему, устранить её и предложить архитектурные изменения для предотвращения рецидивов.

    Анатомия Troubleshooting-интервью

    На таком собеседовании оценивается не только знание инструментов, но и ваш инженерный подход к неизвестности. Интервьюер ожидает увидеть структурированный процесс расследования, который в индустрии называется Forensic-последовательность (от англ. forensics — криминалистика).

    Этот процесс состоит из пяти строгих шагов:

  • Фиксация симптома (что именно сломалось с точки зрения пользователя).
  • Формирование гипотезы (предположение о технической причине).
  • Проверка гипотезы (какие метрики или логи нужно посмотреть).
  • Подтверждение или опровержение.
  • Действие (исправление или переход к новой гипотезе).
  • !Дерево гипотез при расследовании инцидента

    Чтобы не утонуть в терабайтах логов и тысячах графиков, опытные инженеры применяют Триаж сигналов — процесс жесткой фильтрации входящей информации, при котором приоритет отдается высокоуровневым RED-метрикам, а низкоуровневые показатели (CPU, диск) рассматриваются только для подтверждения конкретной гипотезы.

    Рассмотрим четыре классических сценария, которые часто встречаются на собеседованиях.

    Сценарий 1: Расследование «тихого» инцидента

    > Интервьюер: > Ночью упал критичный микросервис обработки платежей. Пользователи жаловались в поддержку, но дежурный инженер не получил ни одного уведомления в Slack. Утром сервис подняли вручную. Расскажите по шагам, как вы будете искать причину отсутствия алерта?

    Ловушка для новичка

    Кандидат начинает хаотично перечислять возможные проблемы: «Наверное, токен Slack протух», «Может, Alertmanager упал», «Или метрики не собирались». Такой ответ показывает отсутствие системного мышления. Интервьюер будет отвечать «нет» на каждую догадку, и кандидат быстро зайдет в тупик.

    Решение уровня Senior

    Кандидат должен продемонстрировать понимание конвейера (pipeline) прохождения алерта в экосистеме Prometheus. Триаж сигналов в этом случае заключается в последовательной проверке каждого узла цепи слева направо.

  • Генерация метрик (Таргет): Проверяем исторические данные. Были ли вообще метрики ошибок (HTTP 5xx) в момент инцидента? Если нет — проблема в инструментировании приложения или экспортере.
  • Сбор метрик (Prometheus Scrape): Проверяем метрику up{job="payments"} за время инцидента. Если она равна 0, Prometheus не мог достучаться до сервиса. Если метрик нет вообще — возможно, отвалился Service Discovery.
  • Вычисление правила (Rule Evaluation): В Prometheus есть встроенный временной ряд ALERTS. Выполняем запрос ALERTS{alertname="HighErrorRate"} за период инцидента. Если мы видим состояние Pending, но не видим Firing, значит, инцидент длился меньше, чем заданный в правиле параметр for.
  • Отправка в Alertmanager: Проверяем метрику prometheus_notifications_errors_total. Если она растет, Prometheus не смог отправить данные по сети в Alertmanager.
  • Маршрутизация (Alertmanager): Идем в UI Alertmanager или смотрим его логи. Проверяем дерево маршрутизации. Не перехватил ли этот алерт другой route без флага continue? Не было ли активных сайленсов (Silences) или правил ингибирования (Inhibition), которые подавили уведомление?
  • Доставка (Receiver): Проверяем метрику alertmanager_notifications_failed_total. Если она отлична от нуля, проблема на стороне интеграции со Slack (неверный вебхук, rate limits от API Slack).
  • Такой ответ показывает интервьюеру, что вы не гадаете, а методично сужаете зону поиска, используя внутренние метрики самой системы мониторинга.

    Сценарий 2: Шторм алертов и цена одного лейбла

    > Интервьюер: > У нас в Kubernetes-кластере моргнула сеть. В результате дежурный получил 5000 писем на почту за 2 минуты и пропустил среди них действительно важный алерт о падении базы данных. Как вы это исправите?

    Анализ проблемы

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

    Количество отправленных уведомлений можно описать математической формулой:

    Где: * — общее количество отправленных писем/сообщений. * — общая продолжительность инцидента в минутах. * — параметр конфигурации Alertmanager (минимальное время между отправкой обновлений). * — количество уникальных групп, сформированных на основе параметра group_by.

    Если кластер состоит из 5000 подов, и сеть моргнула у всех, Prometheus сгенерирует 5000 уникальных алертов.

    Решение уровня Senior

    Кандидат должен указать на ошибку в конфигурации группировки (Grouping) в Alertmanager.

    Если в блоке route указано group_by: ['pod', 'alertname'], то Alertmanager создаст отдельную группу для каждого из 5000 подов. Поскольку , дежурный получит 5000 писем.

    Правильный архитектурный подход — группировать алерты по логическим доменам отказа (Failure domains).

    Кандидат предложит изменить конфигурацию на group_by: ['cluster', 'namespace', 'alertname']. В этом случае все 5000 упавших подов в одном неймспейсе схлопнутся в одну группу (). Дежурный получит ровно одно письмо с заголовком «Упали поды (5000 шт) в кластере prod, namespace billing».

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

    Сценарий 3: Деградация без видимых причин

    > Интервьюер: > Пользователи жалуются, что API поиска работает очень медленно (задержка выросла с 200 мс до 5 секунд). Вы открываете дашборд сервиса: утилизация CPU — 15%, RAM — 30%, сеть не перегружена, ошибок 5xx нет. Ваши действия?

    Анализ проблемы

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

    Кандидат должен продемонстрировать навык Изоляции корня проблемы — умение отличать первопричину (Root cause) от вторичных эффектов. Низкое потребление CPU при высоких задержках — это яркий симптом того, что приложение не выполняет вычисления, а чего-то ждет (блокировка).

    Решение уровня Senior

    Кандидат строит дерево гипотез вслух:

    Гипотеза 1: Проблема с нижележащими зависимостями (Downstream services). Проверка:* Смотрим метрики длительности исходящих HTTP/gRPC запросов от сервиса поиска к другим микросервисам или базе данных. Если подтверждается:* Идем разбираться с базой данных.

    Гипотеза 2: Исчерпание пула соединений (Connection Pool Exhaustion). Проверка:* Смотрим метрики db_connections_active и db_connections_idle. Если активные соединения уперлись в лимит (например, 100 из 100), новые запросы выстраиваются во внутреннюю очередь приложения, ожидая освобождения коннекта. CPU при этом простаивает. Действие:* Увеличить пул соединений или найти тяжелый запрос, который удерживает коннекты.

    Гипотеза 3: Троттлинг на уровне рантайма (например, GOMAXPROCS или сборщик мусора). Проверка:* Анализируем метрики go_gc_duration_seconds (паузы сборщика мусора) или метрики троттлинга CPU в cgroups (container_cpu_cfs_throttled_periods_total). Контейнер может потреблять 15% CPU в среднем за минуту, но упираться в жесткий лимит квоты на микросекундных интервалах.

    Гипотеза 4: Исчерпание пула потоков веб-сервера (Worker Pool). Проверка:* Если используется Tomcat или Gunicorn, проверяем количество занятых воркеров. Если все воркеры заняты обработкой медленных клиентов, новые запросы ждут в очереди на уровне балансировщика.

    Интервьюер оценивает не то, угадали ли вы правильный ответ с первой попытки (правильный ответ в голове у интервьюера), а то, насколько логично и исчерпывающе вы покрываете возможные классы проблем.

    Сценарий 4: Проектирование mitigation и защита от рецидивов

    > Интервьюер: > Мы нашли причину из предыдущего сценария — это был тяжелый аналитический запрос к базе данных, который заблокировал таблицу и исчерпал пул соединений. Мы убили запрос, сервис восстановился. Инцидент закрыт. Что вы будете делать дальше как SRE-инженер?

    Ловушка для новичка

    Кандидат говорит: «Напишу алерт на долгие запросы к БД и пойду пить кофе». Это реактивный подход. Алерт сработает, когда инцидент уже начнется снова.

    Решение уровня Senior

    Опытный инженер переходит к этапу Проектирования mitigation — разработке комплекса мер по снижению влияния подобных сбоев в будущем. Mitigation делится на три горизонта планирования:

  • Краткосрочный (Workaround):
  • * Добавить таймауты на уровне приложения для всех запросов к БД. Если запрос выполняется дольше 2 секунд, обрывать его. Это предотвратит исчерпание пула соединений (Fail-fast подход). * Настроить алерт на рост очереди ожидающих соединений (db_pool_wait_duration_seconds), чтобы узнавать о проблеме до того, как деградирует пользовательский опыт.

  • Среднесрочный:
  • * Разделить пулы соединений: выделить отдельный пул (и отдельную read-only реплику БД) для тяжелых аналитических запросов, изолировав их от транзакционного трафика (паттерн Bulkhead).

  • Долгосрочный (Root cause fix):
  • * Передать задачу команде разработки для оптимизации самого SQL-запроса (добавление индексов, изменение структуры данных).

    На собеседовании важно подчеркнуть разницу между Workaround (костыль, который спасает систему прямо сейчас) и Root cause (устранение фундаментальной причины). Prometheus и алертинг — это инструменты, которые помогают автоматизировать Workaround и собрать данные для поиска Root cause.

    Итоги

    Troubleshooting-интервью проверяет вашу способность сохранять холодный рассудок под давлением. Использование структурированных фреймворков (Forensic-последовательность, Дерево гипотез) показывает вашу инженерную зрелость. Умение читать внутренние метрики Prometheus для отладки самого конвейера мониторинга отличает уверенного пользователя от эксперта, понимающего систему под капотом.

    3. Экспортеры и сбор метрик с узлов

    Микросервисы, написанные вашей командой, могут самостоятельно отдавать метрики благодаря встроенным клиентским библиотекам. Но инфраструктура состоит не только из вашего кода. Базы данных (PostgreSQL, MySQL), брокеры сообщений (Kafka, RabbitMQ), аппаратные маршрутизаторы и сами операционные системы Linux не умеют общаться в формате OpenMetrics.

    Для решения этой проблемы в экосистеме мониторинга существует концепция экспортеров (Exporters). Экспортер — это легковесный прокси-сервис. Его единственная задача — подключиться к сторонней системе, извлечь из нее данные в ее родном формате (через SQL-запросы, API, чтение файлов или системные вызовы), перевести эти данные в многомерную модель и выставить их на HTTP-эндпоинте /metrics для последующего скрейпинга.

    Node Exporter: стандарт мониторинга Linux-серверов

    Самым популярным и фундаментальным экспортером является Node Exporter. Он официально поддерживается командой Prometheus и предназначен для сбора аппаратных и системных метрик ядер *nix-подобных систем.

    На технических собеседованиях часто задают вопрос: «Откуда именно Node Exporter берет данные о процессоре или памяти, если он не интегрирован в ядро?»

    Правильный ответ кроется в архитектуре Linux. Ядро операционной системы предоставляет виртуальные файловые системы: /proc (информация о процессах и системе) и /sys (информация об оборудовании). Node Exporter просто читает эти текстовые файлы, парсит их и преобразует в метрики.

    !Архитектура сбора системных метрик. Слева блок 'Linux Kernel', от него стрелки к файлам '/proc' и '/sys'. От этих файлов стрелки идут к блоку 'Node Exporter', внутри которого показан процесс парсинга. От Node Exporter идет HTTP-ответ с метриками к правому блоку 'Prometheus Server'.

    Особенности запуска и безопасность

    Node Exporter обычно запускается как системная служба (systemd) или в виде контейнера. Здесь кроется классический подводный камень, который проверяют на интервью уровня middle.

    Проблема при запуске в Docker: Если вы просто запустите образ prom/node-exporter, он начнет читать директорию /proc внутри своего изолированного контейнера. Вы получите метрики контейнера, а не хост-машины.

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

    Безопасность: Node Exporter имеет доступ к чувствительной системной информации. Золотое правило безопасности: экспортер никогда не должен запускаться от имени пользователя root. Ему достаточно прав на чтение (read-only) виртуальных файловых систем. Для этого в системе создается выделенный пользователь без права входа в оболочку (shell).

    Анатомия метрик: почему CPU — это счетчик?

    Понимание того, как Node Exporter отдает данные, критически важно для написания запросов. Самая частая ошибка новичков — попытка найти метрику вроде node_cpu_usage_percent (загрузка процессора в процентах). Такой метрики не существует.

    Node Exporter отдает метрику node_cpu_seconds_total. Как мы помним, суффикс _total указывает на тип Counter. Эта метрика показывает, сколько секунд процессор провел в определенном режиме (user, system, idle, iowait) с момента загрузки ОС.

    Почему используется Counter, а не Gauge (текущий процент загрузки)? Если бы экспортер отдавал процент загрузки в момент скрейпинга (раз в 15 секунд), мы бы пропустили кратковременные всплески нагрузки (спайки), которые произошли между опросами. Счетчик же накапливает все процессорное время без потерь.

    Чтобы получить привычный процент загрузки CPU, мы должны вычислить скорость изменения этого счетчика, исключив время простоя (режим idle).

    Формула расчета загрузки CPU выглядит так:

    Где: * — прирост счетчика времени простоя за интервал времени. * — общий прирост времени по всем режимам процессора за тот же интервал.

    !Подвигайте ползунки — и увидите, как из постоянно растущих счетчиков времени формируется привычный процент загрузки процессора.

    Textfile Collector: кастомные метрики без программирования

    Что делать, если вам нужно собирать метрики с bash-скрипта, который делает резервное копирование базы данных раз в сутки? Писать полноценный экспортер на Go или Python, который будет постоянно висеть в памяти ради одного события в день — избыточно.

    Для таких задач в Node Exporter встроен Textfile Collector (Сборщик текстовых файлов). Это мощный инструмент, который позволяет интегрировать любые скрипты в систему мониторинга.

    Как это работает:

  • Вы запускаете Node Exporter с флагом --collector.textfile.directory=/var/lib/node_exporter/textfile_collector.
  • Ваш cron-скрипт выполняет свою задачу (например, бэкап).
  • В конце работы скрипт записывает результат в текстовый файл с расширением .prom в указанную директорию, строго соблюдая синтаксис OpenMetrics.
  • Пример записи из bash-скрипта:

    Инженерная хитрость: Обратите внимание на использование временного файла с суффиксом .$$ (PID процесса) и последующую команду mv. Это атомарная операция в Linux. Она гарантирует, что Node Exporter не попытается прочитать файл в тот момент, когда скрипт записал его только наполовину, что привело бы к ошибке парсинга.

    Blackbox Exporter: взгляд снаружи

    Node Exporter обеспечивает мониторинг по принципу «белого ящика» (Whitebox monitoring) — мы смотрим на систему изнутри. Но сервер может показывать идеальные метрики CPU и памяти, в то время как пользователи не могут открыть сайт из-за проблем с DNS или истекшего SSL-сертификата.

    Для решения этой задачи используется Blackbox Exporter. Он реализует синтетический мониторинг (Synthetic monitoring).

    Вместо того чтобы собирать метрики о самом себе, Blackbox Exporter по команде сервера мониторинга выполняет сетевые проверки (пробы) целевых ресурсов. Он поддерживает протоколы HTTP, HTTPS, DNS, TCP и ICMP (ping).

    Типичные задачи Blackbox Exporter: * Проверка доступности внешних API. * Контроль срока действия SSL-сертификатов (метрика probe_ssl_earliest_cert_expiry). * Измерение времени ответа DNS-сервера. * Проверка успешности прохождения HTTP-аутентификации.

    Архитектурный нюанс для Senior-специалистов: В отличие от других экспортеров, Blackbox Exporter не привязан к конкретному узлу. Вы можете развернуть один экземпляр Blackbox Exporter в Европе, а другой в Азии, и заставить их оба проверять ваш основной сайт. Это позволит измерять сетевые задержки и доступность сервиса из разных географических точек.

    Оптимизация и масштабирование сбора метрик

    Когда ваша инфраструктура вырастает до тысяч серверов, сбор метрик начинает потреблять значительные ресурсы. Node Exporter по умолчанию включает десятки коллекторов (модулей сбора), многие из которых вам никогда не понадобятся (например, метрики файловой системы ZFS, если вы используете ext4, или метрики InfiniBand в обычной облачной среде).

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

    Лучшие практики оптимизации:

  • Отключение лишнего: Используйте флаг --collector.disable-defaults, а затем точечно включайте только нужные коллекторы через --collector.<name>. Это снизит размер HTTP-ответа экспортера с сотен килобайт до десятков.
  • Паттерн Sidecar: В среде Kubernetes экспортеры часто развертываются с использованием паттерна Sidecar (Sidecar pattern). Экспортер запускается как дополнительный контейнер в том же поде (Pod), что и основное приложение. Они делят общее сетевое пространство (localhost) и жизненный цикл, что упрощает Service Discovery и повышает безопасность (порт экспортера не нужно выставлять наружу).
  • Тюнинг таймаутов: Время скрейпинга (scrape timeout) всегда должно быть меньше интервала скрейпинга (scrape interval). Если интервал равен 15 секундам, таймаут должен быть установлен, например, на 10 секунд. Это предотвращает наложение запросов друг на друга в случае зависания сети или высокой нагрузки на экспортер.
  • Понимание того, как работают экспортеры, позволяет интегрировать в систему мониторинга абсолютно любой компонент инфраструктуры. Выбор правильного инструмента — будь то готовый Node Exporter, легковесный Textfile Collector или внешний Blackbox Exporter — отличает опытного инженера от новичка.

    4. Инструментирование кода приложений

    Инструментирование кода приложений

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

    Процесс внедрения в исходный код приложения специальных библиотек для сбора и отправки метрик называется инструментированием (Instrumentation). На технических собеседованиях уровня middle и senior вопросы об инструментировании звучат почти всегда, так как именно здесь разработчики совершают архитектурные ошибки, способные «положить» сервер мониторинга.

    RED-метод: золотой стандарт мониторинга микросервисов

    Когда вас просят спроектировать мониторинг для нового микросервиса, с чего вы начнете? Неопытный разработчик начнет перечислять метрики процессора и памяти. Опытный инженер назовет RED-метод.

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

  • Rate (Интенсивность) — количество запросов в секунду.
  • Errors (Ошибки) — количество завершившихся с ошибкой запросов.
  • Duration (Длительность) — время, затраченное на обработку запроса.
  • > RED-метод фокусируется на пользовательском опыте. Пользователю неважно, что ваш CPU загружен на 90%, если его запрос обрабатывается за 50 миллисекунд без ошибок.

    Как RED-метод ложится на типы метрик Prometheus, которые мы изучили ранее? * Rate и Errors реализуются через один и тот же тип метрики — Counter. Вы создаете счетчик http_requests_total и добавляете к нему лейбл status. Запросы с кодом 200 пойдут в успешные, а с кодами 5xx — в ошибки. Скорость (Rate) вычисляется на стороне сервера с помощью функции rate(). * Duration реализуется через Histogram. Вы записываете время выполнения каждого запроса, а гистограмма распределяет их по кумулятивным бакетам, позволяя позже вычислить 95-й или 99-й процентиль.

    Практика: Middleware для метрик

    Внедрять код сбора метрик прямо в бизнес-логику — плохая практика. Это нарушает принцип единственной ответственности (Single Responsibility Principle).

    В современных веб-фреймворках (на Go, Python, Java, Node.js) инструментирование реализуется через Middleware для метрик — промежуточный слой кода, который оборачивает обработчики HTTP-запросов или gRPC-вызовов. Middleware перехватывает входящий запрос, запускает таймер, передает управление бизнес-логике, а после получения ответа фиксирует статус и останавливает таймер.

    Пример реализации на языке Go (наиболее популярном в экосистеме Prometheus):

    Ловушка на собеседовании: взрыв кардинальности в URL

    Посмотрите внимательно на код выше. В нем скрыта критическая ошибка, которую часто дают найти на лайвкодинге. Мы передаем r.URL.Path в качестве значения лейбла.

    Представьте, что у вас есть REST API для получения профиля пользователя: /api/users/123, /api/users/457, /api/users/999. Если вы запишете полный путь в лейбл, каждый новый пользователь создаст новый уникальный временной ряд в TSDB. При 100 000 пользователей вы получите 100 000 метрик только для одного эндпоинта. Это классический взрыв кардинальности.

    Правильное решение: Вместо сырого пути (URL Path) необходимо использовать шаблон маршрута (Route template). Ваш фреймворк должен передавать в метрику строку вида /api/users/{id}. Таким образом, миллионы запросов к разным пользователям агрегируются в один предсказуемый временной ряд.

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

    Наблюдаемость (Observability) и границы Prometheus

    На позициях Senior от вас ожидают понимания того, где заканчивается зона ответственности Prometheus. Для этого нужно знать концепцию Наблюдаемости (Observability), которая строится на трех столпах:

  • Метрики (Metrics) — агрегированные числовые показатели системы во времени (Prometheus).
  • Логи (Logs) — детализированные текстовые записи о конкретных событиях (Elasticsearch, Loki).
  • Трассировки (Traces) — путь конкретного запроса через множество микросервисов с таймингами каждого шага (Jaeger, OpenTelemetry).
  • !Схема трех столпов наблюдаемости. Слева блок 'Метрики (Prometheus)' — показывает общую картину и алерты. Посередине 'Трассировки (Jaeger)' — показывает путь запроса между сервисами. Справа 'Логи (Elastic)' — показывает детали конкретной ошибки. Стрелки показывают процесс дебаггинга: от метрик к трейсам, от трейсов к логам.

    Типичный вопрос на интервью: «У нас есть сложный процесс оплаты. Я хочу записывать ID каждой транзакции и email пользователя в Prometheus, чтобы потом искать зависшие платежи. Это хорошая идея?»

    Правильный ответ: Нет, это антипаттерн. Prometheus предназначен для агрегированных метрик, а не для хранения высококардинальных данных или логов. Запись ID транзакции (уникального для каждого запроса) мгновенно уничтожит производительность TSDB.

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

    Правила именования метрик (Best Practices)

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

    Официальные рекомендации Prometheus требуют использовать базовые единицы измерения (Base units).

    * Никаких миллисекунд, минут или часов. Только секунды. * Никаких мегабайт или килобайт. Только байты.

    Почему так? Prometheus и Grafana спроектированы для работы с базовыми единицами. Если ваша метрика отдает секунды, Grafana сама автоматически переведет их в миллисекунды, минуты или дни в зависимости от масштаба графика. Если вы отдадите миллисекунды, вам придется вручную делить значения на 1000 в каждом PromQL-запросе.

    Структура имени метрики должна состоять из трех-четырех частей: namespace_subsystem_name_unit

    Таблица примеров:

    | Плохое имя | Хорошее имя | Почему хорошее лучше? | | :--- | :--- | :--- | | memory_usage | process_resident_memory_bytes | Указана подсистема (process), конкретный тип памяти (resident) и базовая единица (bytes). | | http_req_time | http_request_duration_seconds | Стандартное слово duration, базовая единица seconds. | | errors_count | http_requests_total{status="500"} | Использование суффикса _total для счетчиков, ошибки выделены лейблом, а не отдельной метрикой. |

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

    5. Service Discovery в динамических средах

    Service Discovery в динамических средах

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

    Однако в современных облачных средах и кластерах оркестрации контейнеров инфраструктура эфемерна. Поды в Kubernetes могут создаваться и удаляться каждую минуту в ответ на изменение нагрузки. Виртуальные машины в AWS Auto Scaling Group появляются и исчезают динамически. Если мы попытаемся управлять конфигурацией мониторинга вручную, она устареет еще до того, как мы сохраним файл.

    Для решения этой проблемы используется Service Discovery (SD) — механизм автоматического обнаружения таргетов (целей для скрейпинга). На технических собеседованиях уровня middle и senior глубокое понимание механизмов SD и релейблинга (relabeling) является обязательным требованием.

    Анатомия Service Discovery

    Prometheus не просто «пингует» сеть в поисках метрик. Он интегрируется с API различных платформ (Kubernetes, AWS EC2, Consul, Azure, GCP) и подписывается на события изменения состояния инфраструктуры.

    Когда механизм Service Discovery обнаруживает новый объект (например, запущенный контейнер), он не сразу начинает его скрейпить. Сначала он формирует внутреннее представление этого объекта, присваивая ему набор мета-лейблов (Meta-labels).

    Мета-лейблы — это временные системные метки, которые всегда начинаются с двойного подчеркивания (например, __meta_kubernetes_pod_name). Они содержат всю сырую информацию об объекте, которую удалось получить из API: IP-адрес, порты, аннотации, названия неймспейсов, теги облачного провайдера.

    > Важное правило: все лейблы, начинающиеся с __, автоматически удаляются Prometheus перед выполнением HTTP-запроса к таргету. Если вы хотите сохранить какую-то информацию из мета-лейбла (например, имя пода) в итоговой метрике, вы должны явно переименовать его на этапе релейблинга.

    Исключением являются два зарезервированных системных лейбла, которые определяют, куда именно Prometheus будет отправлять запрос: * __address__ — хост и порт таргета (например, 10.0.0.5:8080). * __metrics_path__ — HTTP-путь для скрейпинга (по умолчанию /metrics).

    !Жизненный цикл таргета: от обнаружения до сохранения метрик в базу данных.

    File-Based Service Discovery: мост между статикой и динамикой

    Прежде чем погружаться в сложные интеграции с Kubernetes, рассмотрим самый универсальный и недооцененный механизм — file_sd_configs.

    Часто на собеседованиях задают вопрос: «У нас есть самописная устаревшая CMDB (база данных конфигураций), у которой нет готового плагина для Prometheus. Как нам настроить автоматическое обнаружение серверов?»

    Ответ кроется в использовании файлового Service Discovery. Вы пишете простой скрипт (например, на Python или Bash), который раз в минуту опрашивает вашу CMDB и формирует JSON-файл со списком таргетов:

    В конфигурации Prometheus вы указываете путь к этому файлу:

    В чем магия? Prometheus использует подсистему ядра Linux под названием inotify. Эта подсистема позволяет приложениям подписываться на события изменения файлов. Как только ваш скрипт перезапишет targets.json, ядро Linux мгновенно уведомит Prometheus. Prometheus прочитает новый файл и обновит список таргетов в памяти без перезапуска процесса и без необходимости отправлять ему сигнал SIGHUP.

    Kubernetes Service Discovery

    Интеграция с Kubernetes (kubernetes_sd_configs) — самый популярный сценарий использования Service Discovery. Prometheus обращается к Kubernetes API и может обнаруживать объекты в разных ролях (roles):

  • node — обнаруживает узлы (серверы) кластера. Используется для сбора метрик с Node Exporter.
  • service — обнаруживает сервисы Kubernetes. Полезно для Blackbox-мониторинга.
  • pod — обнаруживает все поды.
  • endpoints / endpointslice — обнаруживает конечные точки, стоящие за сервисами.
  • Разница между pod и endpoints — частая тема для обсуждения. Роль pod найдет абсолютно все поды, даже те, которые находятся в состоянии CrashLoopBackOff или не прошли проверки готовности (Readiness probe). Роль endpoints найдет только те поды, которые успешно прошли проверки и готовы принимать трафик.

    Для мониторинга самого приложения лучше использовать pod, так как вы хотите собрать метрики (или хотя бы зафиксировать недоступность через метрику up) даже с тех подов, которые работают некорректно.

    Мастер-класс по Relabeling (Релейблинг)

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

    Представьте, что kubernetes_sd_configs с ролью pod нашел в вашем кластере 5000 подов. Но вам нужно скрейпить только 50 из них — те, которые явно разрешили мониторинг. Если Prometheus попытается опросить все 5000, он потратит ресурсы впустую, получив тысячи ошибок 404 (Not Found) или Connection Refused.

    Релейблинг позволяет отфильтровать таргеты до того, как к ним будет выполнен HTTP-запрос. Он настраивается в блоке relabel_configs.

    !Интерактивный симулятор Relabeling: проверьте, как регулярные выражения меняют лейблы таргета

    Рассмотрим классический паттерн, который ожидают увидеть от вас на интервью. Разработчики добавляют к своим подам аннотации: prometheus.io/scrape: "true" prometheus.io/port: "8080" prometheus.io/path: "/metrics/custom"

    Как научить Prometheus понимать эти аннотации? С помощью следующих действий (actions) релейблинга:

    1. Action: keep (Фильтрация)

    Действие keep означает: «сохранить таргет, только если значение указанных лейблов совпадает с регулярным выражением, иначе — отбросить таргет».

    Здесь мы проверяем мета-лейбл, содержащий значение аннотации prometheus.io/scrape. Если оно равно true, таргет остается в конвейере. Все остальные 4950 подов немедленно отбрасываются.

    2. Action: replace (Замена)

    Действие replace (используется по умолчанию, если не указано иное) берет значения из source_labels, применяет к ним регулярное выражение с группами захвата и записывает результат в target_label.

    Давайте динамически изменим порт для скрейпинга, если разработчик указал его в аннотации:

    Разберем эту сложную конструкцию: * Мы берем два лейбла: текущий адрес (например, 10.0.0.5:80) и порт из аннотации (например, 8080). Prometheus склеивает их через точку с запятой: 10.0.0.5:80;8080. * Регулярное выражение ([^:]+) захватывает IP-адрес в первую группу 2. * В replacement мы формируем новую строку 2 (получается 10.0.0.5:8080) и перезаписываем системный лейбл __address__.

    3. Action: labelmap (Массовое переименование)

    Часто нам нужно перенести все бизнес-лейблы пода (например, app, env, team) в итоговые метрики. Писать replace для каждого лейбла долго. Используется labelmap:

    Это правило найдет все мета-лейблы, начинающиеся с __meta_kubernetes_pod_label_, отбросит этот префикс (благодаря группе захвата (.+)) и создаст обычные лейблы. Так __meta_kubernetes_pod_label_env="prod" превратится в env="prod".

    Ловушка на собеседовании: relabel_configs vs metric_relabel_configs

    Это, пожалуй, самый важный вопрос для проверки уровня Senior.

    Ситуация: Вы настроили Service Discovery. Prometheus успешно скрейпит приложение. Внезапно разработчик выкатывает релиз, в котором случайно добавляет в метрику http_requests_total лейбл user_id (идентификатор пользователя). Происходит взрыв кардинальности, база данных Prometheus начинает потреблять 100 ГБ оперативной памяти и падает. Откатить релиз приложения прямо сейчас нельзя. Как спасти мониторинг?

    Неопытный кандидат скажет: «Напишу правило в relabel_configs, чтобы удалить этот лейбл». Это ошибка. Блок relabel_configs работает до скрейпинга. Он ничего не знает о самих метриках, он оперирует только таргетами (IP-адресами и мета-данными серверов).

    Правильный ответ: использовать metric_relabel_configs.

    Этот блок выполняется после того, как Prometheus скачал текст с метриками по HTTP, но до того, как сохранил их в базу данных (TSDB).

    Это правило полностью отбросит (drop) проблемную метрику http_requests_total, спасая сервер от падения. Альтернативно, можно использовать действие labeldrop, чтобы удалить только конкретный лейбл user_id, сохранив саму метрику.

    > Подводный камень: metric_relabel_configs — очень ресурсоемкая операция. Если relabel_configs выполняется один раз при обнаружении таргета, то metric_relabel_configs применяется к каждому сэмплу при каждом скрейпинге. Сложные регулярные выражения здесь могут перегрузить процессор самого сервера Prometheus.

    Шардирование таргетов (Target Sharding) при высоких нагрузках

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

    Один сервер Prometheus может собирать сотни тысяч метрик в секунду. Но что, если у вас гигантский кластер на 50 000 подов? Один сервер упрется в лимиты CPU и сети. Нам нужно запустить 4 сервера Prometheus. Но как заставить их разделить работу так, чтобы они не скрейпили одни и те же поды дважды?

    В Prometheus нет встроенной кластеризации для распределения задач. Вместо этого используется элегантный математический подход — Шардирование таргетов с помощью действия hashmod.

    Мы настраиваем все 4 сервера абсолютно одинаково (они все видят все 50 000 подов через Kubernetes API), но добавляем в relabel_configs следующее правило:

    Как это работает:

  • Действие hashmod берет IP-адрес таргета (например, 10.0.5.12:8080).
  • Вычисляет от него хеш-сумму (MD5).
  • Применяет математическую операцию деления по модулю (остаток от деления): , где — количество серверов (в нашем случае 4).
  • Результат всегда будет равен 0, 1, 2 или 3. Это значение записывается во временный лейбл __tmp_hash.
  • Следующее действие keep оставляет таргет только в том случае, если остаток от деления совпадает с порядковым номером текущего сервера Prometheus.
  • В результате 50 000 подов детерминированно и равномерно распределяются между 4 серверами. Никакой координации между серверами не требуется. Если один под перезапустится и получит новый IP-адрес, его хеш изменится, и он автоматически будет назначен одному из серверов.

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

    6. Основы языка запросов PromQL

    Основы языка запросов PromQL

    PromQL (Prometheus Query Language) — это функциональный язык запросов, созданный специально для извлечения и обработки данных из базы временных рядов Prometheus. В отличие от SQL, который оперирует таблицами и строками, PromQL работает с потоками данных, распределенными во времени. На технических собеседованиях уровня middle и senior умение писать сложные и оптимизированные запросы на PromQL проверяется почти всегда, так как именно этот язык лежит в основе всех дашбордов и правил алертинга.

    Главная парадигма PromQL заключается в том, что любой запрос — это математическое выражение. Вы не пишете инструкции SELECT или WHERE. Вы описываете, какие данные хотите получить, и применяете к ним математические функции. Результатом выполнения любого выражения в PromQL всегда является один из четырех базовых типов данных.

    Четыре типа данных в PromQL

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

  • Мгновенный вектор (Instant vector) — набор временных рядов, где каждый ряд содержит ровно один сэмпл (значение), актуальный на заданный момент времени. Представьте, что вы поставили систему на паузу и сделали срез данных. Запрос http_requests_total вернет мгновенный вектор: текущее значение счетчика для каждого эндпоинта.
  • Вектор диапазона (Range vector) — набор временных рядов, где каждый ряд содержит массив значений за определенный промежуток времени. Запрос http_requests_total[5m] вернет вектор диапазона: все значения счетчика, собранные за последние 5 минут. Векторы диапазона нельзя напрямую нарисовать на графике (так как для одной точки времени есть множество значений), они используются как входные данные для функций агрегации.
  • Скаляр (Scalar) — простое число с плавающей точкой. У скаляра нет лейблов. Например, число 100 или результат вычисления 10 / 2.
  • Строка (String) — простое строковое значение. В реальной практике мониторинга используется крайне редко.
  • Селекторы и фильтрация (Matchers)

    Чтобы выбрать нужные временные ряды из тысяч существующих, используются селекторы лейблов (Label matchers). Они указываются в фигурных скобках сразу после имени метрики.

    PromQL поддерживает четыре оператора фильтрации: * = (Точное совпадение): env="production" * != (Несовпадение): env!="testing" * =~ (Совпадение по регулярному выражению): status=~"5.." (найдет все статусы 500, 501, 502 и т.д.) * !~ (Несовпадение по регулярному выражению): method!~"GET|HEAD"

    > Важное правило: Запрос должен содержать хотя бы один селектор, который не является пустым. Запрос {job=~"(.*)"} крайне неэффективен и может привести к падению сервера Prometheus, так как заставит базу данных поднять в оперативную память абсолютно все существующие метрики.

    Вычисление скорости: rate() против irate()

    Это классический вопрос на любом собеседовании по мониторингу: «В чем разница между rate и irate, и когда что использовать?»

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

    Функция rate() вычисляет среднюю скорость роста за весь указанный период. Она берет первое и последнее значение в окне, учитывает возможные сбросы счетчика и делит разницу на время.

    Математически это выглядит так:

    Где — последнее значение счетчика в окне, — первое значение, а — их соответствующие временные метки в секундах.

    Функция irate() (instant rate) вычисляет мгновенную скорость роста, используя только два последних сэмпла в указанном временном окне. Остальные данные в векторе диапазона игнорируются (они нужны только для того, чтобы гарантированно найти эти две точки).

    Где — самый последний сэмпл, а — предпоследний сэмпл.

    Когда что использовать? * Используйте rate() для алертинга и графиков, показывающих общие тренды. Эта функция сглаживает кратковременные всплески. Если микросервис на секунду получил в 10 раз больше запросов, rate за 5 минут размажет этот пик, и алерт не сработает ложно. * Используйте irate() для детального расследования инцидентов (troubleshooting) на графиках с высоким разрешением. Она покажет реальные, острые пики нагрузки, которые rate бы скрыл.

    !Интерактивный график: сравнение rate и irate на пульсирующей нагрузке

    Агрегация данных

    В микросервисной архитектуре один сервис может быть запущен в 50 экземплярах (подах). Запрос rate(http_requests_total[5m]) вернет 50 линий на графике. Чтобы получить общую картину, данные нужно агрегировать.

    Основные операторы агрегации: * sum() — сумма всех значений. * avg() — среднее арифметическое. * max() / min() — максимальное и минимальное значения. * topk(n, ...) — возвращает рядов с наибольшими значениями.

    Агрегация уничтожает лейблы. Если вы сделаете sum(rate(http_requests_total[5m])), вы получите одно число без лейблов pod, status или method. Чтобы сохранить нужные измерения, используются модификаторы by (сохранить указанные лейблы) или without (удалить указанные лейблы, сохранив остальные).

    Пример: мы хотим узнать общее количество запросов в секунду, сгруппированное по HTTP-статусам: sum by (status) (rate(http_requests_total[5m]))

    Сопоставление векторов (Vector Matching)

    Мы подошли к самой сложной теме, которая отличает Senior-инженера от Middle. В PromQL нет оператора JOIN, как в SQL. Вместо этого используется Сопоставление векторов (Vector matching).

    Представьте задачу: вам нужно вычислить процент ошибок. У вас есть метрика http_requests_total. Процент ошибок — это количество ошибок, деленное на общее количество запросов.

    sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))

    Этот запрос сработает, только если слева и справа от оператора деления / находятся векторы с абсолютно идентичным набором лейблов. Оператор / берет элемент слева, ищет элемент справа с точно такими же лейблами и делит их значения. Это называется сопоставлением один-к-одному (One-to-One).

    Но что, если наборы лейблов отличаются? Например, слева у нас есть лейбл status="500", а справа (где мы считали все запросы) мы лейбл status убрали при агрегации. PromQL не найдет совпадений и вернет пустой результат.

    Игнорирование лейблов при сопоставлении

    Чтобы заставить PromQL сопоставить векторы с разными лейблами, используются модификаторы ignoring или on.

    sum by (service, status) (rate(http_requests_total{status=~"5.."}[5m])) / ignoring(status) sum by (service) (rate(http_requests_total[5m]))

    Здесь мы говорим: «Раздели левое на правое, но при поиске пары игнорируй лейбл status». Теперь PromQL найдет совпадения по лейблу service.

    Сопоставление многие-к-одному (Many-to-One)

    Самый частый кейс на собеседованиях — обогащение метрик метаданными.

    Допустим, у вас есть метрика потребления памяти контейнером: container_memory_usage_bytes{pod="api-123"}. В ней нет лейбла с версией приложения. Версия приложения хранится в специальной информационной метрике от kube-state-metrics: kube_pod_info{pod="api-123", version="v2.0"}. Значение этой метрики всегда равно 1.

    Как получить график потребления памяти, сгруппированный по версиям приложения? Нам нужно «приклеить» лейбл version из kube_pod_info к метрике container_memory_usage_bytes.

    Для этого используются модификаторы group_left или group_right.

    container_memory_usage_bytes * on(pod) group_left(version) kube_pod_info

    Разберем эту магию по шагам:

  • Мы умножаем потребление памяти на 1 (значение kube_pod_info), поэтому сами цифры памяти не меняются.
  • on(pod) указывает, что векторы нужно сопоставлять только по лейблу pod.
  • Слева у нас много контейнеров (один под может иметь несколько контейнеров), а справа — только одна запись информации о поде. Это связь «многие-к-одному».
  • group_left(version) говорит: «Вектор слева имеет бóльшую кардинальность. Возьми лейбл version из правого вектора и добавь его к результату левого вектора».
  • !Схема работы group_left: как лейблы из информационной метрики переносятся в метрику производительности

    Сдвиг во времени (Time Shifting)

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

    Для этого используется модификатор offset. Он позволяет сдвинуть окно оценки вектора диапазона в прошлое.

    Запрос текущей нагрузки: sum(rate(http_requests_total[5m]))

    Запрос нагрузки ровно неделю назад: sum(rate(http_requests_total[5m] offset 1w))

    Вы можете комбинировать их, чтобы найти разницу: sum(rate(http_requests_total[5m])) - sum(rate(http_requests_total[5m] offset 1w)) Если результат сильно больше нуля, значит, сегодня запросов значительно больше, чем в этот же день на прошлой неделе.

    Освоение PromQL требует практики. Начните с простых агрегаций, разберитесь с поведением rate и постепенно переходите к векторному сопоставлению. Понимание того, как работают group_left и ignoring, позволит вам строить аналитические запросы любой сложности и уверенно отвечать на вопросы интервьюеров.

    7. Продвинутые функции и агрегации в PromQL

    Продвинутые функции и агрегации в PromQL

    Освоив базовые типы данных, вычисление скорости и сопоставление векторов, вы заложили фундамент для работы с Prometheus. Однако в реальной практике Site Reliability Engineering (SRE) и на технических собеседованиях уровня Senior от вас потребуют решения более сложных задач. Как вычислить 99-й процентиль задержки запросов? Как предсказать, когда закончится место на диске? Как написать алерт, который срабатывает только при соблюдении нескольких условий одновременно?

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

    Вычисление процентилей: histogram_quantile

    В распределенных системах среднее арифметическое (функция avg) — это худшая метрика для оценки производительности. Если 99 запросов выполняются за 10 мс, а один зависает на 10 секунд, среднее время составит около 110 мс. Эта цифра не показывает ни реального опыта большинства пользователей (10 мс), ни наличия серьезной проблемы (10 с).

    Для объективной оценки используются процентили (percentiles). Например, 95-й процентиль (p95) показывает значение, в которое укладываются 95% всех запросов.

    В Prometheus для этого используется функция histogram_quantile. Она принимает два аргумента: целевой квантиль (от 0 до 1, где 0.95 — это 95-й процентиль) и вектор диапазона метрики типа Histogram.

    > Золотое правило PromQL: никогда не усредняйте процентили. Вы должны сначала агрегировать сырые данные гистограммы со всех подов, и только потом вычислять процентиль от этой суммы.

    Классический вопрос на собеседовании: «Напишите запрос для вычисления p99 задержки HTTP-запросов по всем инстансам сервиса».

    Правильный ответ выглядит так: histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))

    Разберем этот запрос изнутри наружу:

  • http_request_duration_seconds_bucket[5m] — берем сырые данные кумулятивных бакетов за 5 минут.
  • rate(...) — вычисляем скорость прироста каждого бакета в секунду. Это необходимо, так как гистограмма — это набор счетчиков, которые только растут.
  • sum by (le) (...) — суммируем скорости одинаковых бакетов со всех инстансов сервиса. Лейбл le (less than or equal) критически важен — он определяет верхнюю границу каждого бакета. Если его удалить при агрегации, функция не сможет построить кривую распределения.
  • histogram_quantile(0.99, ...) — математически аппроксимирует значение 99-го процентиля на основе агрегированных бакетов.
  • !Подвигайте ползунки количества бакетов и их границ — и увидите, как точность вычисления процентиля зависит от настройки гистограммы в коде приложения

    Сабквери (Subqueries): запросы в запросах

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

    Представьте задачу: вам нужно узнать максимальную скорость HTTP-запросов за последние 24 часа.

    Вы не можете написать max_over_time(http_requests_total[24h]), потому что это даст максимальное значение самого счетчика (которое всегда будет в конце периода). Вы не можете написать max_over_time(rate(http_requests_total[5m])), потому что rate возвращает мгновенный вектор, а max_over_time требует вектор диапазона.

    Здесь на помощь приходят сабквери (Subqueries). Они позволяют выполнить внутренний запрос с заданным шагом (разрешением) за указанный период времени.

    Синтаксис сабквери: <запрос>[<диапазон>:<шаг>]

    Решение нашей задачи: max_over_time(rate(http_requests_total[5m])[24h:1m])

    Как это работает:

  • Prometheus берет окно в 24 часа (24h).
  • Каждую 1 минуту (1m) он вычисляет rate за 5 минут ([5m]).
  • Из полученного массива значений (1440 точек за сутки) функция max_over_time выбирает самое большое.
  • Сабквери — мощный, но ресурсоемкий инструмент. Используйте его осторожно, так как слишком мелкий шаг (например, :1s на интервале в неделю) может привести к нехватке оперативной памяти на сервере мониторинга.

    Предиктивные функции: заглядываем в будущее

    Мониторинг должен быть проактивным. Если вы получаете алерт «Место на диске закончилось» — уже поздно, сервис лежит. Правильный алерт звучит так: «Место на диске закончится через 4 часа при текущей скорости записи».

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

    Синтаксис: predict_linear(v range-vector, t scalar)

    Пример алерта на исчерпание дискового пространства: predict_linear(node_filesystem_free_bytes{mountpoint="/data"}[1h], 4 * 3600) < 0

    В этом выражении: * [1h] — обучающая выборка. Prometheus смотрит, как менялось свободное место за последний час. 4 3600 — горизонт прогнозирования в секундах (4 часа). * < 0 — условие срабатывания. Если линия тренда пересечет нулевую отметку в течение 4 часов, выражение вернет значение, и сработает алерт.

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

    Логические операторы множеств (Set Operators)

    В сложных правилах алертинга часто требуется комбинировать условия. В PromQL для этого существуют операторы множеств: and, or и unless. Они работают с мгновенными векторами и фильтруют их на основе совпадения лейблов.

    Оператор and (Пересечение)

    vector1 and vector2 возвращает элементы из vector1, для которых существуют элементы в vector2 с абсолютно идентичным набором лейблов. Значения из vector2 игнорируются — они служат только фильтром.

    Пример: мы хотим получить алерт о высоком проценте ошибок (более 5%), но только если общий трафик превышает 10 запросов в секунду (чтобы исключить ложные срабатывания ночью, когда 1 ошибка на 2 запроса дает 50%).

    Оператор unless (Исключение)

    vector1 unless vector2 возвращает элементы из vector1, для которых нет совпадений по лейблам в vector2.

    Пример: показать загрузку CPU всех узлов, кроме тех, на которых запущен процесс резервного копирования (где высокая загрузка ожидаема).

    Оператор or (Объединение)

    vector1 or vector2 возвращает все элементы из vector1, а также те элементы из vector2, которые не имеют совпадений по лейблам в vector1.

    Оптимизация агрегации: without против by

    Мы уже упоминали модификаторы агрегации. На уровне Senior важно понимать архитектурные последствия выбора между ними.

    Модификатор by (label1, label2) сохраняет только указанные лейблы, удаляя все остальные. Модификатор without (label1, label2) удаляет только указанные лейблы, сохраняя все остальные.

    В динамических средах (например, Kubernetes) к метрикам постоянно добавляются новые мета-лейблы: region, cluster, team, version.

    Если в дашборде вы написали sum by (service) (rate(http_requests_total[5m])), и завтра инфраструктурная команда добавит лейбл region, ваш запрос схлопнет данные со всех регионов в одну линию. Вы потеряете контекст.

    Если же вы напишете sum without (pod, instance) (rate(http_requests_total[5m])), вы явно укажете: «убери эфемерные идентификаторы конкретных контейнеров, но оставь всю остальную бизнес-логику и топологию». При добавлении лейбла region ваш график автоматически разделится на несколько линий по регионам, что сделает мониторинг более устойчивым к изменениям инфраструктуры.

    !Схема потока данных при агрегации: как модификатор without сохраняет новые инфраструктурные лейблы, предотвращая потерю контекста, в отличие от жесткой фильтрации через by

    Использование without вместо by считается best practice при написании переиспользуемых правил записи (recording rules) и библиотек дашбордов.

    8. Правила записи метрик

    Правила записи метрик (Recording Rules)

    В высоконагруженных микросервисных архитектурах сбор данных — это лишь половина задачи. Настоящие проблемы начинаются на этапе визуализации и алертинга. Представьте дашборд в Grafana, который должен отобразить 99-й процентиль времени ответа API за последние 30 дней по кластеру из 500 подов. Как мы выяснили ранее, для этого потребуется вычислить histogram_quantile поверх агрегированных рейтов всех бакетов.

    При каждом обновлении страницы Prometheus будет вынужден поднимать из базы данных миллионы сэмплов, вычислять скорости, суммировать их и аппроксимировать квантили на лету. Это приведет к таймаутам запросов, зависанию дашбордов и пиковым нагрузкам на процессор сервера мониторинга. Для решения этой архитектурной проблемы применяются Правила записи (Recording Rules).

    Концепция предварительных вычислений

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

    Ближайшая аналогия из мира реляционных баз данных — Материализованное представление (Materialized View). Вместо того чтобы выполнять тяжелый JOIN и GROUP BY при каждом запросе пользователя, база данных периодически вычисляет результат и сохраняет его как физическую таблицу. Чтение из такой таблицы происходит мгновенно.

    В Prometheus этот процесс работает следующим образом:

  • Вы определяете тяжелый запрос (например, агрегацию по всем дата-центрам).
  • Задаете интервал оценки (например, каждую 1 минуту).
  • Prometheus выполняет запрос, получает результат (мгновенный вектор) и записывает его в TSDB под новым именем метрики.
  • Grafana запрашивает уже готовую новую метрику, тратя на это миллисекунды.
  • !Схема конвейера обработки данных: сырые метрики поступают в TSDB, фоновый процесс Recording Rules считывает их, агрегирует и записывает обратно в TSDB как новые метрики, к которым затем обращается Grafana

    Синтаксис и структура конфигурации

    Правила записи описываются в конфигурационных файлах формата YAML. Они объединяются в Группы правил (Rule Groups). Группировка имеет критическое значение для производительности и логики выполнения, о чем мы поговорим чуть позже.

    Базовый пример конфигурации:

    Разберем ключевые параметры: * name — уникальное имя группы. * intervalevaluation_interval (интервал оценки). Определяет, как часто Prometheus будет выполнять запросы из этой группы. Если не указан, используется глобальный параметр из prometheus.yml. * record — имя новой метрики, которая будет создана. * expr — само PromQL-выражение, результат которого будет сохранен. * labels — статические лейблы, которые будут принудительно добавлены к новому временному ряду (опционально).

    Стандарт именования: Паттерн level:metric:operations

    Обратите внимание на странное имя новой метрики в примере выше: job:http_requests_total:rate5m. Это не случайность, а строгий индустриальный стандарт, описанный в официальной документации и повсеместно спрашиваемый на собеседованиях уровня Senior.

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

    Структура паттерна:

  • Level (Уровень агрегации): Показывает, по какому признаку сгруппированы данные. Если вы агрегировали данные до уровня сервиса (удалив поды), уровень будет service или job. Если метрика глобальная (схлопнуто всё), используется слово global или cluster.
  • Metric (Исходная метрика): Оригинальное имя метрики без изменений. Это сохраняет преемственность и базовые единицы измерения.
  • Operations (Операции): Список примененных математических функций в порядке их выполнения.
  • Примеры правильного именования: * instance:node_cpu_seconds_total:rate1m — загрузка CPU, вычисленная через rate за 1 минуту, сгруппированная по инстансам. * namespace:http_request_duration_seconds_bucket:rate5m — скорость прироста бакетов гистограммы, агрегированная до уровня неймспейса. * cluster:http_request_duration_seconds:p99 — 99-й процентиль задержки, вычисленный для всего кластера целиком.

    > Использование стандартизированного именования предотвращает хаос в крупных инсталляциях. Если разработчик назовет агрегированную метрику my_service_fast_cpu, инфраструктурная команда никогда не догадается, что это за данные, за какой период взят rate и можно ли их использовать для глобальных алертов. > > BookStack

    Архитектурные нюансы и подводные камни

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

    1. Цепочки правил (Rule Chaining) и порядок выполнения

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

    Внутри одной группы правил (Rule Group) все правила выполняются строго последовательно, сверху вниз. Это означает, что второе правило в списке может безопасно использовать метрику, созданную первым правилом в том же цикле оценки.

    Если бы эти правила находились в разных группах, они выполнялись бы параллельно и независимо. В таком случае Шаг 2 мог бы использовать устаревшие данные от Шага 1 (из предыдущего цикла оценки), что привело бы к рассинхронизации графиков.

    2. Взрыв кардинальности при записи

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

    Если вы напишете правило: record: bad_rule:http_requests:rate5m expr: rate(http_requests_total[5m])

    И у вас есть 1000 подов, каждый из которых отдает метрику с 50 уникальными URL-путями, это правило будет каждую минуту генерировать и записывать в TSDB новых временных рядов. Вместо оптимизации вы создадите колоссальную паразитную нагрузку на запись.

    Золотое правило: Правила записи всегда должны сопровождаться агрегацией (операторами sum, avg, max и т.д. с модификаторами). Цель правила — уменьшить кардинальность, а не просто скопировать данные.

    !Подвигайте ползунки количества сырых метрик и сложности запроса — и увидите, как Правила записи снижают время загрузки дашборда, но увеличивают фоновую нагрузку на CPU сервера Prometheus

    3. Проблема выравнивания времени (Staleness и Race Conditions)

    Представьте ситуацию: evaluation_interval равен 15 секундам. Скрейпинг таргетов также настроен на 15 секунд.

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

    Для минимизации этой проблемы в сложных системах применяют сдвиг во времени с помощью модификатора offset.

    Например, если мы хотим гарантировать, что все данные за последнюю минуту точно дошли до сервера мониторинга перед агрегацией, мы можем написать: expr: sum(rate(http_requests_total[5m] offset 1m))

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

    4. Математическая корректность агрегаций

    Классическая ошибка, которую допускают инженеры — попытка усреднить уже усредненные данные.

    Допустим, у вас есть правило, которое считает среднее время ответа по каждому поду: record: pod:http_duration:avg expr: rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m])

    Затем вы хотите получить среднее время ответа по всему кластеру и пишете второе правило: expr: avg(pod:http_duration:avg)

    Это математически некорректно. Если один под обрабатывает 1000 запросов в секунду за 10мс, а второй под обрабатывает 1 запрос в секунду за 5000мс, функция avg сложит мс. Вы получите искаженную картину, так как avg не учитывает вес (количество запросов) каждого пода.

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

    expr: sum(rate(http_request_duration_seconds_sum[5m])) / sum(rate(http_request_duration_seconds_count[5m]))

    Оценка производительности: когда вводить правила?

    Не стоит создавать правила записи для каждого графика. Каждое правило потребляет оперативную память и CPU сервера Prometheus на постоянной основе.

    Математическая модель нагрузки выглядит примерно так: Пусть — время выполнения правила, а — количество обрабатываемых временных рядов. Зависимость линейная: .

    Если начинает превышать evaluation_interval (например, запрос выполняется 65 секунд, а интервал оценки стоит 60 секунд), Prometheus начнет пропускать циклы выполнения (Missed evaluations). На графиках появятся разрывы (гэпы).

    Внедряйте Recording Rules только если:

  • Дашборд загружается дольше 3-5 секунд.
  • Запрос сканирует более 10 000 временных рядов (можно проверить в Query Inspector в Grafana).
  • Метрика используется в критичных алертах, где задержка вычисления недопустима.
  • Использование правил записи — это баланс между скоростью чтения (для пользователей Grafana) и стоимостью записи (для инфраструктуры мониторинга). Грамотное проектирование иерархии правил отличает простого пользователя Prometheus от архитектора систем наблюдаемости.

    9. Настройка Alertmanager и маршрутизация уведомлений

    Настройка Alertmanager и маршрутизация уведомлений

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

    Когда PromQL-выражение в правиле алерта (Alerting Rule) возвращает результат, Prometheus понимает: что-то сломалось. Однако сам Prometheus не умеет отправлять письма, писать в Slack или звонить дежурному инженеру. Для этого в экосистеме существует отдельный компонент — Alertmanager.

    На собеседованиях уровня Middle/Senior вопросы по Alertmanager часто становятся камнем преткновения. Интервьюеры проверяют не знание синтаксиса YAML, а понимание того, как управлять хаосом: как сделать так, чтобы при падении дата-центра дежурный получил одно понятное сообщение, а не 10 000 писем, от которых зависнет почтовый клиент.

    Архитектура: почему Alertmanager — отдельный сервис?

    Первый вопрос, который может задать архитектор на интервью: «Зачем нам отдельный бинарник Alertmanager? Почему разработчики Prometheus не встроили отправку уведомлений прямо в сервер мониторинга?»

    Ответ кроется в принципах отказоустойчивости и разделения зон ответственности (Separation of Concerns).

    Представьте High Availability (HA) инсталляцию: у вас работают два независимых сервера Prometheus, которые скрейпят одни и те же таргеты. Если падает база данных, оба сервера Prometheus одновременно вычислят правило алерта и сгенерируют событие. Если бы они сами отправляли уведомления, вы бы получали дубликаты каждого алерта.

    Вынесение логики в Alertmanager решает эту проблему. Оба сервера Prometheus отправляют сырые события в Alertmanager. Alertmanager принимает их, видит, что это один и тот же инцидент (по совпадению лейблов), производит дедупликацию и отправляет дежурному ровно одно уведомление.

    !Схема конвейера алертинга: два сервера Prometheus отправляют сырые алерты в кластер Alertmanager. Внутри Alertmanager алерты проходят через блоки Дедупликации, Группировки, Ингибирования и Маршрутизации, после чего уходят в Slack, Email и PagerDuty.

    Аналогия из жизни: Prometheus — это датчики дыма в здании. Alertmanager — это диспетчерский пульт. Если на этаже сработали сразу пять датчиков, диспетчер не звонит пожарным пять раз. Он объединяет информацию и делает один звонок: «Пожар на третьем этаже, сработали датчики в комнатах 1-5».

    Жизненный цикл алерта: Конвейер обработки

    Когда сырой алерт попадает в Alertmanager, он проходит через строгий конвейер (Pipeline). Понимание этого конвейера критически важно для траблшутинга.

    1. Группировка (Grouping)

    Группировка — это механизм объединения множества связанных алертов в одно сводное уведомление. Это главная защита от «шторма алертов» (Alert Storm).

    Допустим, у вас пропала сеть в стойке, где работают 50 микросервисов. Prometheus мгновенно сгенерирует 50 алертов InstanceDown. Без группировки ваш телефон взорвется от уведомлений. С группировкой по лейблу rack (стойка) вы получите одно сообщение: «Упали сервисы (список из 50 штук) в стойке A».

    За группировку отвечают три важнейших временных параметра. Их разницу часто просят объяснить на собеседованиях:

    | Параметр | Описание | Типичное значение | Зачем нужен? | | :--- | :--- | :--- | :--- | | group_wait | Время ожидания перед отправкой первого уведомления для новой группы. | 30s - 1m | Дает время другим связанным алертам «долететь» до Alertmanager, чтобы отправить их единым пакетом. | | group_interval | Время ожидания перед отправкой обновления для уже существующей группы. | 5m - 10m | Если группа уже отправлена, но в нее добавились новые упавшие сервисы, Alertmanager подождет это время, прежде чем прислать апдейт. | | repeat_interval | Как часто повторять уведомление, если проблема не решена. | 3h - 12h | Напоминание дежурному. Если алерт все еще активен, уведомление придет снова через это время. |

    Математическая логика конвейера: если время с момента создания группы , алерт буферизуется. Как только , формируется и отправляется сообщение.

    !Подвигайте ползунки времени и нажимайте «Сгенерировать алерт» — и увидите, как Alertmanager собирает их в корзину (группирует) и отправляет единым блоком только по истечении group_wait.

    2. Ингибирование (Inhibition)

    Ингибирование — это автоматическое подавление одних алертов при наличии других, более критичных.

    Классический кейс: у вас есть алерт NodeDown (упал физический сервер) и алерты PostgresDown, RedisDown (упали базы данных на этом сервере). Очевидно, что если сервер обесточен, базы данных на нем тоже недоступны. Нет смысла будить администратора баз данных, если проблема на уровне железа — нужно будить системного администратора.

    Правило ингибирования в конфигурации выглядит так:

    Как это читается: «Если активен алерт NodeDown с критичностью critical (источник), то подавить все алерты с критичностью warning (цель), при условии, что у них совпадает значение лейбла instance».

    3. Сайленсы (Silences)

    В отличие от ингибирования (которое работает автоматически по правилам), Сайленс — это ручное, временное отключение уведомлений для определенного набора лейблов.

    Сайленсы создаются через веб-интерфейс Alertmanager или API. Они незаменимы при плановых технических работах (Maintenance). Если вы собираетесь перезагружать кластер баз данных с 02:00 до 04:00, вы заранее создаете сайленс на лейбл service="database" на это время. Prometheus продолжит фиксировать падение, Alertmanager будет получать алерты, но на этапе сайленсинга они будут отброшены, и дежурный сможет спать спокойно.

    Дерево маршрутизации (Routing Tree)

    После того как алерты сгруппированы и не были подавлены, Alertmanager должен решить, кому их отправить. За это отвечает блок route — дерево маршрутизации.

    Маршрутизация строится по принципу дерева с одним корневым узлом и множеством дочерних ветвей. Каждый алерт начинает путь от корня и спускается вниз, проверяя совпадение своих лейблов с условиями (matchers) каждой ветви.

    Пример конфигурации:

    Важный нюанс: параметр continue

    По умолчанию, как только алерт находит первую подходящую ветвь, маршрутизация останавливается. Алерт отправляется указанному receiver, и дальнейшие ветви не проверяются.

    Однако иногда нужно отправить алерт в несколько мест. Например, алерт от фронтенда с критичностью critical. Мы хотим, чтобы он ушел и команде фронтенда в Slack (Ветвь 2), и дежурному инженеру в PagerDuty (Ветвь 3).

    Для этого используется флаг continue: true. Он говорит Alertmanager: «Отправь уведомление сюда, но не останавливайся, продолжай искать совпадения ниже по дереву». Это частый вопрос на траблшутинг: «Почему алерт ушел в Slack, но не создал инцидент в PagerDuty?» — обычно забывают поставить continue: true.

    Получатели (Receivers) и современные практики

    Блок receivers описывает конкретные интеграции: куда именно стучаться Alertmanager'у. Он поддерживает множество протоколов из коробки: email, Slack, PagerDuty, OpsGenie, Telegram, Webhook.

    Антипаттерн: Прямая отправка в Telegram-чат

    Начинающие инженеры часто настраивают отправку всех алертов в общий Telegram-чат команды. Для небольшого пет-проекта это нормально, но в Enterprise-среде это считается антипаттерном.

    Почему прямая отправка в мессенджер — плохая идея:

  • Эффект свидетеля (Bystander effect): Когда алерт видят 20 человек в чате, каждый думает, что им займется кто-то другой. В итоге инцидент игнорируется.
  • Отсутствие эскалации: Если дежурный спит, Telegram не позвонит его руководителю через 15 минут.
  • Невозможность управления графиками: Мессенджер не знает, кто сегодня дежурит (On-Call).
  • Best Practice: Alertmanager должен отправлять критичные уведомления в специализированные системы управления инцидентами (Grafana OnCall, PagerDuty, OpsGenie). Эти системы хранят расписание дежурств. Alertmanager отправляет webhook в OnCall, а уже OnCall смотрит в календарь, видит, что сегодня дежурит инженер Боб, и звонит Бобу на телефон. Если Боб не нажал кнопку «Acknowledge» (Подтвердить) за 5 минут, система автоматически эскалирует инцидент — звонит лиду команды.

    > Использование промежуточного звена для маршрутизации по расписанию дежурств — стандарт индустрии. Alertmanager маршрутизирует по смыслу алерта (база данных или сеть), а система On-Call маршрутизирует по времени и людям (кто сейчас на смене). > > DevTrends

    High Availability кластер Alertmanager

    Мы уже упоминали, что для отказоустойчивости запускают несколько серверов Prometheus. Но что если упадет сам Alertmanager? Мы останемся слепыми. Поэтому Alertmanager тоже запускают в HA-режиме (обычно 2 или 3 реплики).

    И здесь кроется фундаментальное архитектурное отличие, которое обожают спрашивать на собеседованиях на позицию Senior.

    Prometheus HA — это архитектура Shared Nothing (ничего не разделяют). Серверы Prometheus не знают о существовании друг друга. Они просто делают одну и ту же работу параллельно.

    Alertmanager HA работает совершенно иначе. Реплики Alertmanager обязаны общаться друг с другом, иначе они не смогут выполнять дедупликацию (каждая реплика отправит свое уведомление).

    Для синхронизации состояния Alertmanager использует Gossip-протокол (протокол сплетен) поверх библиотеки memberlist.

    Как это работает:

  • При запуске вы указываете каждому Alertmanager адреса его соседей через флаг --cluster.peer=alertmanager2:9094.
  • Узлы постоянно обмениваются короткими сообщениями (сплетнями) по UDP/TCP.
  • Они синхронизируют между собой три типа данных:
  • - Журнал отправленных уведомлений (Notification Log) — чтобы знать, кто уже отправил письмо. - Активные Сайленсы (Silences) — чтобы ручное отключение на одном узле применилось ко всем. - Состояние кластера (кто жив, кто мертв).

    Проблема Split-Brain

    Что произойдет, если между двумя репликами Alertmanager пропадет сеть, но обе они сохранят доступ к интернету и серверам Prometheus? Возникнет ситуация Split-Brain (расщепление мозга).

    Обе реплики подумают, что сосед умер. Обе получат алерт от Prometheus. Так как они не могут синхронизировать Notification Log, обе реплики отправят уведомление дежурному.

    В контексте систем мониторинга это считается правильным поведением (Fail-Safe). Архитектура Alertmanager построена на принципе: «Лучше отправить дубликат уведомления, чем потерять критический алерт».

    Резюме настройки

    Настройка Alertmanager требует баланса. Слишком агрессивная группировка (group_wait = 10 минут) приведет к тому, что вы узнаете о падении продакшена слишком поздно. Слишком слабая группировка — и вы утонете в спаме.

    Правильный подход к внедрению:

  • Начинайте с широких маршрутов и отправляйте некритичные алерты в Slack.
  • Настройте ингибирование для базовых инфраструктурных зависимостей (Хост -> Контейнер).
  • Интегрируйте систему On-Call для критичных инцидентов.
  • Регулярно проводите ревью алертов: если алерт срабатывает часто, но не требует действий человека — удалите правило алерта или переведите его в разряд логов/дашбордов.