Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Урок 12 из 25 ~20 мин +20 XP

PromQL: первая пара запросов

Метрики в /metrics растут всегда, и отвечать на вопросы о сервисе умеют только promql запросы: rate prometheus-счётчика превращает накопленное в скорость, sum by группирует серии по лейблам, деление даёт долю ошибок. В этом уроке пишем первые запросы на метриках из предыдущих уроков — от селектора до p99 из гистограммы, с ловушками окон и агрегаций по пути.

Содержание урока · 9 разделов

График сырого счётчика http_requests_total выглядит как линия, которая только растёт: монотонно вверх, после рестарта обрыв к нулю и снова вверх. На такой картинке невозможно увидеть ни всплеск трафика, ни провал: рост счётчика означает «запросы когда-то были», а не «запросы идут сейчас». PromQL решает это двумя механизмами. Первый — селекторы: имя метрики выбирает все её серии, а фигурные скобки с условиями сужают выборку: http_requests_total{status="500"} оставляет только пятисотые, регулярка status=~"5.." — все пятисотые коды разом, а сочетание route="/api",status="200" — конкретную серию из урока про лейблы. Второй механизм — окна: суффикс [5m] превращает мгновенный выбор в диапазон за пять минут, range vector, с которым уже работают функции. Любой запрос дашборда складывается из этих деталей: сначала выбрать серии, затем применить функцию, при необходимости агрегировать. Начнём с самой ходовой связки — имя, окно и rate.

Селекторы: четыре вида matchers#

Условий в фигурных скобках всего четыре вида, и их стоит выучить наизусть, потому что встречаются они в каждом запросе. Точное равенство status="500" оставляет серию с конкретным значением. Неравенство status!="200" выбрасывает успешные ответы и оставляет всё остальное — включая редкие коды, которые вы забыли перечислить. Регулярка со знаком равенства status=~"5.." выбирает по шаблону, а с восклицательной тильдой status!~"2.." — всё, что шаблону не подходит: так собирают трафик неуспеха без перечисления кодов. Важная деталь, о которую спотыкаются пришедшие из других языков: регулярки PromQL полностью заякорены от начала и до конца, то есть 5.. означает ровно три символа, начинающихся с пятёрки, и ничего лишнего. Комбинировать условия можно сколько нужно, перечисляя через запятую: route="/api",status=~"5.." — пятисотые одного маршрута.

PromQL: rate prometheus-счётчика и окно#

rate(counter[5m]) считает среднюю скорость роста счётчика в секунду на пятиминутном окне: берёт значения внутри окна, учитывает сбросы после рестартов и экстраполирует результат на края. Для счётчиков это главный инструмент — rate prometheus рекомендует практически всегда, когда хочется увидеть «сколько в секунду», от запросов до заказов. У окна два жёстких требования. Первое: внутри должно лежать хотя бы две точки, иначе результат пустой — при скрейпе раз в 30 секунд окно короче минуты бессмысленно, а сразу после старта сервиса графики будут молчать пару минут. Второе: длина окна выбирается под скорость изменений, и практический минимум — четыре скрейп-интервала: при интервале 15 секунд берите не меньше минуты, при интервале 30 — не меньше двух. К gauge функция rate неприменима по смыслу: датчик не монотонный, и «скорость роста» температуры ничего не значит. Для gauge есть своё семейство: avg_over_time, max_over_time и min_over_time усредняют или выбирают экстремум по окну, не претендуя на счётную природу значения.

Три запроса, с которых начинается дашборд

GO
# Трафик: все запросы в секунду
sum(rate(http_requests_total[5m]))

# Трафик по маршрутам
sum by (route) (rate(http_requests_total[5m]))

# Пятисотые в секунду
sum(rate(http_requests_total{status=~"5.."}[5m]))

Первый запрос складывает все серии в одно число — линию общего трафика. Второй группирует по лейблу route: вместо одной линии получается по линии на маршрут, и график читается как таблица. Третий срезает серии регуляркой по статусу: matcher со знаком =~ пропускает все значения, подходящие под шаблон 5.. — пятьсотые коды целиком.

Доля ошибок: деление мгновенных векторов#

Доля ошибок — это пятисотые, поделённые на весь трафик:

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

Хитрость в том, что PromQL делит векторы, а не числа: каждой серии левого запроса ищется пара справа по совпадающим лейблам. Если слева серии с лейблами route и status и справа — тоже route и status, пары найдутся не для всех: статуса 500 нет среди серий «всего трафика» с тем же лейблом status, и часть делений развалится. Поэтому сначала суммируют: sum(...) схлопывает векторы до одного элемента без лейблов, и деление становится простым. Хотите долю ошибок по маршрутам — суммируйте с группировкой с обеих сторон: sum by (route) в числителе и в знаменателе, и группировка обязана совпадать, иначе векторы снова не найдут пары. С нулями поведение такое: если знаменатель пуст — серий нет — результат пустой; если знаменатель ноль при ненулевом числителе — плюс бесконечность, и Grafana рисует разрыв. Ночной сервис без трафика с честно разорванным графиком — норма, а не баг.

Агрегации: sum, avg, min, max, count#

sum — не единственная агрегация, и остальные полезно держать в голове, потому что отвечают они на другие вопросы. count(http_requests_total) вернёт число серий: моментально видно, сколько комбинаций лейблов породил сервис — быстрый тест на кардинальность. avg усредняет значения серий и годится для датчиков вроде числа горутин по инстансам, но не для счётчиков: средний счётчик — бессмысленная величина, когда инстансы живут разное время. min и max находят крайности: max по инстансам покажет самого нагруженного узла. Отдельно стоит topk(3, rate(...)) — топ три серии по значению: быстрый способ найти самый горячий маршрут без разглядывания линий. Без группировки все серии складываются в одну. Все агрегации принимают необязательную группировку by или without: без (instance) складывает всё, кроме инстансов, — удобный способ увидеть кластер целиком, не перечисляя остальные лейблы руками.

Латентность: p99 из гистограммы

GO
histogram_quantile(
  0.99,
  sum by (le, route) (rate(http_request_duration_seconds_bucket[5m]))
)

Гистограмма хранит кумулятивные бакеты, поэтому при агрегации лейбл le обязан сохраняться: sum by (le, route) группирует по маршруту, не теряя границы бакетов. histogram_quantile интерполирует квантиль внутри бакета; rate обязателен, как и для любых счётчиков. Подробно про устройство бакетов и сетки — в уроке про гистограммы.

increase, irate и длина окна#

increase(counter[1h]) отвечает на вопрос «сколько за час»: та же скорость, умноженная на длину окна; его место — графики «заказов за день» и «ошибок за неделю». irate(counter[5m]) — мгновенная скорость по двум последним точкам: она реагирует на всплески раньше rate, но и дёргается от каждого скрейпа, поэтому для дашбордов и алертов берут rate, а irate оставляют для разбора инцидента в реальном времени. Отдельного внимания заслуживает поведение после рестарта: счётчик обнуляется, и rate трактует уменьшение как сброс, прибавляя остаток прошлого значения — трафик между последним скрейпом до смерти и первым после подъёма учтён. На коротких окнах это выглядит как единичный выброс, и это честная цена данных: процесс не мог сообщить ничего между падением и подъёмом. С длиной окна связана и самая частая жалоба на свежем дашборде — дрожащая линия. При скрейпе раз в 30 секунд окно в минуту содержит всего две-три точки: каждая новая перестраивает расчёт радикально, и линия скачет. Правило четырёх скрейп-интервалов спасает: окно от двух минут при 30-секундном скрейпе даёт четыре-пять точек, и rate становится устойчивым. Обратная крайность — слишком длинное окно: час сглаживает всё, включая инцидент, и вы увидите аварию через полчаса после пользователей. Рабочая связка: дашборды смотрят на окна пять-пятнадцать минут ради читаемости, алерты считают на окнах одна-две минуты ради скорости обнаружения, а дрожь внутри них гасит for. Если сомневаетесь — поднимите окно вдвое и сравните: изменение формы графика при том же трафике означает, что дело в окне, а не в сервисе.

Функции первых запросов#

ВыражениеНа чём работаетЧто отвечает
rate(counter[5m])счётчикисредняя скорость роста в секунду с учётом сбросов
increase(counter[1h])счётчикиприращение за окно целиком
irate(counter[5m])счётчикимгновенная скорость по двум последним точкам
avg_over_time(gauge[10m])датчикисреднее значение по окну
sum by (route) (...)векторыскладывание серий с сохранением лейбла
histogram_quantile(0.99, ...)бакеты гистограммквантиль латентности из распределения

Проверка: что возвращает rate

Что возвращает rate(http_requests_total[5m])?

Проверка: срез по маршрутам

Какой запрос покажет трафик отдельными линиями на каждый маршрут?

Что вернёт sum?

Прочитайте код и выберите, что он выведет:

http_requests_total{route="/api",status="200"} 500
http_requests_total{route="/api",status="500"} 12
http_requests_total{route="/health",status="200"} 800

sum(http_requests_total)

Допишите группировку

Допишите группировку в числителе и знаменателе доли ошибок по маршрутам.

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

От селектора к срезу

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

Строки

Ваша программа

Нажимайте на строки слева, чтобы собрать программу

Карточки: первые запросы

Термин

Мгновенный вектор

нажмите, чтобы перевернуть
Определение

Результат селектора без окна: http_requests_total — самое свежее значение каждой серии. С ним работает арифметика и агрегации вроде sum.

1 / 6

Запросы из этого урока — готовый материал для панелей: в уроке про Grafana соберём из них дашборд, а в уроке про алерты превратим запрос доли ошибок в правило с порогом. Практика требует запущенного стека: учебный сервис и Prometheus поднимаются локально, песочница сайта PromQL не исполняет — язык считается сервером, а не приложением. Начните с трёх запросов из урока на своих метриках и посчитайте в уме, сколько серий вернёт каждый: сумма по всем маршрутам — одна линия, по маршрутам — столько, сколько значений отдаёт лейбл route, который вы завели в уроке про лейблы. Затем спросите у count(http_requests_total), сколько серий у вас на самом деле, — и сравните с собственной прикидкой. Полная программа курса — на странице Prometheus.

Песочница урока

Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.

Вывод

Запустите код, чтобы увидеть вывод.

Коротко: что запомнить

  • Селектор выбирает серии, окно превращает его в range vector, функции делают расчёт — так собирается любой запрос.
  • rate(counter[5m]) — средняя скорость в секунду; главный инструмент для счётчиков, для gauge не годится.
  • Окно — минимум четыре скрейп-интервала: две точки обязательны, четыре-пять дают устойчивую линию.
  • sum by (route) сохраняет лейбл в группировке; sum без by складывает всё в одно число.
  • Доля ошибок — деление rate пятисотых на rate всего; группировки по обеим сторонам обязаны совпадать.
  • p99 считается histogram_quantile по бакетам с сохранением лейбла le.
  • Дашборды смотрят на длинные окна, алерты — на короткие; дрожь гасит for из урока про правила.

Домашнее задание

Поднимите учебный сервис и Prometheus локально и воспроизведите три запроса урока: трафик целиком, по маршрутам и пятисотые. Затем посчитайте долю ошибок делением и проверьте, что группировки sum by (route) по обеим сторонам совпадают. Сознательно примените rate к gauge из своего сервиса и найдите бессмысленный результат на графике — теперь вы знаете, почему он такой. Песочница PromQL не исполняет: язык считается сервером Prometheus.

Личные заметкиПусто
Хранятся только в вашем браузере (Prometheus · PromQL: первая пара запросов)

Вопросы и ответы по уроку

Почему rate на свежезапущенном сервисе пустой?

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

Чем sum отличается от avg и когда что брать?

sum складывает серии, avg усредняет. Для трафика и ошибок берут sum: события складываются. Среднее по квантилям гистограмм считать нельзя — сначала rate по бакетам, затем histogram_quantile.

Можно ли писать PromQL в приложении на Go?

Да, через клиент api/prom/v1 — но обычно запросы живут в Grafana и правилах алертов. Приложение только экспортирует метрики; запросы — зона мониторинга, и смешивать их с бизнес-кодом не стоит.

Термины урока

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

Следующая ступень учебного пути

WebSocket25 уроков~13 чпрактика25 уроков уже доступны

WebSocket в Go: real-time чаты и уведомления

Курс WebSocket на Go: gorilla/websocket, эхо-сервер, hub-паттерн, комнаты, авторизация соединений, ping/pong, reconnect и масштабирование. 25 уроков.

  • Поднимать WS-сервер и апгрейдить HTTP-соединения
  • Писать readPump/writePump без гонок на каждое соединение
  • Строить hub: регистрацию, рассылку и комнаты
  • Авторизовывать соединения и ограничивать сообщения