DarkRiDDeR14 мин

Разбор метрик: как проверить учебный порог по сырой серии

НаблюдаемостьPrometheusОтладка

Симптом в разборе обычно звучит слишком широко: «на графике есть запросы, но непонятно, что ухудшилось». Цена поспешного ответа — шумный порог, который игнорируют, или изменение timeout без доказательства, что проблема вообще в этом endpoint. Одна точка counter не показывает, сколько ошибок появилось за окно; общий request count не показывает операцию; отдельный stack trace не показывает, повторяется ли случай. Пока эти три вещи смешаны, график украшает разбор, но не направляет действие.

Ниже — не отчёт о реальном инциденте. Это контролируемая серия для одной метрики store_http_requests_total{operation="checkout",outcome="error"} и один учебный вопрос: «пересекло ли число новых ошибок checkout за десять минут границу двух?» Значения, timestamps, threshold и графическая форма выдуманы для проверки порядка рассуждений. Нет настоящего Prometheus server, scrape, нагрузки, alerting, SLO или production-значений.

Начинаем не с графика, а с различимой ветки

Первое решение — выбрать сигнал. В нашем случае это counter завершённых ошибок, потому что операция имеет чёткий конец, а интересует число событий, накопленных за окно. Причина выбора не в том, что counter «самый популярный». Он позволяет сравнить два момента и узнать, появлялись ли новые error outcomes. Если задача была бы про число активных соединений в конкретную секунду, подошёл бы gauge. Если задача была бы про распределение длительностей, нужна отдельная metric family с seconds и выбранным типом распределения. Один тип не должен притворяться ответом на все вопросы.

Второе решение — оставить labels достаточными, но не уникальными. operation="checkout" говорит, о какой логической работе идёт речь. outcome="error" отделяет ошибки от успешных завершений. Они не содержат пользователя, URL с ID или request ID. Поэтому при пересечении порога можно открыть журнал по времени и операции, а не искать одну уникальную series. Если нужен конкретный запрос, его корреляционный идентификатор берут из лога; попытка добавить его в counter разрушила бы агрегирование ещё до расследования.

Учебная диагностика: сигнал ведёт к следующей проверке, но не заменяет её
НаблюдениеЧто оно действительно говоритЧего из него нельзя вывестиСледующее ограниченное действие
increase(...error...[10m]) = 0в выбранной серии нет новых error increments в окнечто все пользователи получили успех и что scrape не пропущенсверить, соответствует ли operation исходной жалобе; не объявлять систему здоровой
increase(...error...[10m]) = 1в учебной серии появилась одна новая ошибкамасштаб, причину и необходимость pagingпроверить один связанный лог или повторить изолированный сценарий
increase(...error...[10m]) = 2учебный порог пересечёнproduction severity, SLO или допустимую долю ошибокоткрыть ветку диагностики checkout и зафиксировать контекст
Общий requests_total растётобработчик завершал какие-то запросыкакая operation или outcome измениласьдобавить bounded group-by, не менять timeout по одной общей линии

Таблица намеренно отделяет факт от решения. Ноль в одном counter не доказывает отсутствие проблемы: metric может не покрывать нужный путь, scrape может отсутствовать, а браузер может отвалиться до приложения. Две ошибки не доказывают, что нужно будить человека ночью: это всего лишь порог учебной серии. Чтобы порог стал реальным правилом, проект должен отдельно назвать окно, владельца, стоимость ложного срабатывания и связь с фактическим пользователем. В этом материале мы останавливаемся раньше и проверяем форму логики.

Сырые snapshots полезнее легенды о «пике»

Для counter важен прирост, а не последняя цифра. В синтетической записи ниже значение равно нулю, затем единице и двум. За десять минут разница между первой и последней точкой равна двум. Такая арифметика подходит для controlled fixture, где мы заранее запретили reset и знаем все точки. Она не заменяет реализацию increase() в Prometheus: настоящий движок работает с range vector, точками scrape и правилами обработки counter. Поэтому в документе рядом существуют две вещи — PromQL-формулировка вопроса и более простая in-memory проверка того же порога.

# Синтетические snapshots одного counter, не production-метрика.
# metric: store_http_requests_total{operation="checkout",outcome="error"}
2020-08-01T10:00:00Z  0
2020-08-01T10:05:00Z  1
2020-08-01T10:10:00Z  2

# Для упражнения: delta = 2, поэтому порог >= 2 пересечён.
# Фикстура намеренно не моделирует scrape delay, counter reset или PromQL extrapolation.

Ошибка здесь часто начинается с фразы «на графике был пик». Пик без имени series, окна и сравниваемой величины не даёт следующего действия. Правильная запись короче: «для operation=checkout и outcome=error в учебных snapshots прирост за 10 минут равен двум; в упражнении это открывает проверку одного лога». В ней не содержится догадки о базе, сетевом hop-е или пользователе. Эти причины проверяются после того, как signal сузил вход в разбор.

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

Запрос и fixture проверяют разные части договора

PromQL ниже выражает желаемую форму запроса к серверу метрик: выбрать operation и outcome, взять изменение counter за десять минут и сравнить с границей. В реальном проекте его нужно выполнить на той же версии Prometheus, с известным scrape interval и реальной конфигурацией target. Только тогда можно читать ответ и обсуждать график. В автономном пакете запрос не выполняется: вместо этого runMetricsFixture() возвращает два прозрачных набора snapshots и проверяет, что простая разность даёт true на двух ошибках и false на одной.

# Учебный PromQL-вопрос, а не production-alert:
# «В контролируемом десятиминутном окне стало две или больше ошибок checkout?»
sum(increase(store_http_requests_total{
  operation="checkout",
  outcome="error"
}[10m])) >= 2

# В in-memory fixture вместо PromQL проверяется та же простая граница:
errorDelta(controlledSamples) >= 2 // true
errorDelta(oneErrorSamples) >= 2  // false

Эта разница между expression и fixture не является недостатком. Она защищает от ложного отчёта «PromQL проверен», когда запущен был только Node. Fixture доказывает ограниченный контракт: labels bounded, counter не убывает в модели, две ошибки пересекают учебный threshold, одна — нет. Она не доказывает scrape, storage, alert delivery, restart process или поведение любого exporter. Такой маленький тест полезен для ревью, потому что будущая правка не сможет тихо превратить порог в >= 1 или добавить поле пользователя в labels.

Почему окно и порог выбирают вместе

Окно в десять минут и число два не существуют по отдельности. В маленькой controlled fixture десять минут дают три понятных snapshots, а две ошибки создают ветку, отличную от одной. Если выбрать минуту, но scrape происходит реже, результат может быть пустым или зависеть от случайной точки. Если выбрать сутки, краткий отказ растворится в общей сумме. Эти рассуждения не дают готовое число для production: они требуют сверить частоту scrape, тип операции, возможный burst и того, кто умеет реагировать на сигнал.

Также нельзя заменить counter ошибок общей долей без определения знаменателя. Соотношение error/attempt полезно только тогда, когда оба числа считаются в одном месте и за одно окно. Если success заканчивается на proxy, а error пишется внутри приложения, отношение будет смесью разных границ. Поэтому первая версия текста не рисует процент и не объявляет «норму ошибок». Она оставляет более узкий, проверяемый вопрос о числе завершённых error в одной operation. Следующий metric contract может добавить attempts и проверить их общую точку увеличения.

Маршрут учебного разбора

  1. Записать исходный симптом без диагноза: какой пользовательский путь подозревается и какая цена ошибки, если она повторится.
  2. Выбрать одну metric family и момент увеличения. Для этого упражнения counter увеличивается только после завершения checkout с известным outcome.
  3. Проверить labels: operation и outcome должны быть bounded; request ID и полный URL остаются в журнале, а не в series.
  4. Собрать три синтетические snapshots, явно пометить их учебными и посчитать только ту величину, которую fixture умеет моделировать.
  5. Сформулировать PromQL-вопрос с тем же окном и labels, но не объявлять его выполненным до запуска на реальном сервере метрик.
  6. После пересечения учебного threshold открыть один связанный журнал или изолированный сценарий. Изменять timeout, retry или код только после нового наблюдаемого факта.

Какие ошибки этот порядок останавливает

Такой разбор останавливает четыре распространённые подмены. Он не позволяет принять общую линию запросов за доказательство ошибки checkout. Он не позволяет считать последнюю цифру counter скоростью изменения. Он не даёт уникальному request ID стать label только потому, что его удобно видеть на графике. И он не превращает учебное число два в обещание о production alert. Всё это звучит скромно, но именно эти подмены делают первую метрику бессмысленной: сигнал начинает жить отдельно от вопроса и действия.

В реальном продолжении после такого упражнения понадобится отдельный стенд: endpoint с bounded operation, Prometheus scrape, одно известное изменение в тестовой нагрузке и сохранённый результат запроса. Потом можно обсуждать более широкую систему. Здесь автор августа 2020 года осваивает только связку «операционный вопрос → signal → labels → проверка → действие». Он не выдумывает реальную деградацию, не приписывает себе SLO/error budget и не подменяет контрольную серию производственным измерением.

Проверяемые источники

  • Prometheus: Instrumentation — официальные рекомендации для online-serving систем: считать завершённые запросы, ошибки и latency; не раздувать labels и держать счётчик ошибок рядом с числом попыток
  • Prometheus: Querying basics — официальная справка по selectors, range vectors, агрегации и выражениям PromQL; запросы ниже показаны как учебные формулировки
  • Prometheus: Metric and label naming — официальные правила: одна величина и единица на имя метрики, базовые единицы и отдельная осторожность с label dimensions
  • Prometheus: Metric types — официальное описание counter, gauge, histogram и summary; выбор типа должен следовать форме наблюдаемой величины