PromQL (Prometheus)
PromQL за 13 минут: метрики Prometheus, селекторы по меткам, rate/increase, агрегация, histogram_quantile, типовые запросы и алерты на одной странице.
PromQL (Prometheus Query Language) — язык запросов к системе мониторинга Prometheus. Им вытаскивают и обрабатывают временные ряды (time series): RPS, задержки, ошибки, загрузку CPU и памяти. Весь язык — на одной странице, в плотно закомментированных запросах.
Что такое Prometheus и метрики
Prometheus хранит данные как временные ряды — последовательности точек (метка времени, число), привязанные к имени метрики и набору меток.
# Имя метрики + метки = уникальный временной ряд (time series):
# http_requests_total{job="api", method="GET", code="200"}
# http_requests_total{job="api", method="POST", code="500"}
# Это ДВА разных ряда: одно имя, но разные значения меток.
# Четыре типа метрик:
# counter — счётчик, только растёт (сбрасывается в 0 при рестарте).
# Пример: http_requests_total, errors_total.
# gauge — измеритель, может расти и падать.
# Пример: memory_usage_bytes, temperature, queue_size.
# histogram — распределение по корзинам (buckets) + _sum и _count.
# Пример: http_request_duration_seconds_bucket{le="0.5"}.
# summary — заранее посчитанные квантили на стороне приложения.
# Пример: rpc_duration_seconds{quantile="0.99"}.
# По соглашению counter оканчивается на _total, единицы — в имени (_bytes, _seconds).
Выборка метрик: мгновенный вектор
Самый простой запрос — просто имя метрики. Он возвращает мгновенный вектор (instant vector): по одной свежей точке на каждый ряд.
# Все ряды метрики на текущий момент времени:
node_memory_MemFree_bytes
# Вернётся набор рядов с их актуальными значениями, например:
# node_memory_MemFree_bytes{instance="web-1"} 1.2e9
# node_memory_MemFree_bytes{instance="web-2"} 8.0e8
# Скалярный литерал (одно число без меток):
42
# Строковый литерал (редко, в основном для label_replace):
"hello"
Селекторы по меткам
В фигурных скобках фильтруют ряды по меткам. Поддерживаются четыре оператора сопоставления.
# Точное совпадение метки:
http_requests_total{job="api"}
# Несколько условий — логическое И (все должны выполниться):
http_requests_total{job="api", method="GET", code="200"}
# != — метка НЕ равна значению:
http_requests_total{code!="200"}
# =~ — метка совпадает с регуляркой (RE2), привязка к началу и концу неявная:
http_requests_total{code=~"5.."} # все 5xx: 500, 502, 503...
http_requests_total{job=~"api|web"} # job равен api ИЛИ web
# !~ — метка НЕ совпадает с регуляркой:
http_requests_total{code!~"2.."} # всё, кроме 2xx
# Само имя метрики — это метка __name__, можно фильтровать и так:
{__name__=~"node_.*", instance="web-1"}
Диапазонные векторы
В квадратных скобках указывают окно времени. Получается диапазонный вектор (range vector) — не одна точка, а все точки за период. Его нельзя строить напрямую, он подаётся в функции вроде rate.
# Все точки метрики за последние 5 минут для каждого ряда:
http_requests_total[5m]
# Единицы длительности: ms, s, m, h, d, w, y. Можно комбинировать:
node_cpu_seconds_total[1h30m]
process_cpu_seconds_total[90s]
# Subquery — диапазон поверх выражения, шаг 1m за последний час:
max_over_time( rate(http_requests_total[5m])[1h:1m] )
Операторы
Арифметика, сравнения и логика работают поэлементно, сопоставляя ряды по совпадающим меткам.
# Арифметические: + - * / % ^
node_memory_MemFree_bytes / 1024 / 1024 # байты -> мегабайты
node_filesystem_avail_bytes / node_filesystem_size_bytes * 100 # доля в %
# Сравнения фильтруют: остаются только ряды, где условие истинно.
node_filesystem_avail_bytes < 1e9 # где свободно < 1 ГБ
up == 0 # упавшие цели (up == 0)
http_requests_total > 1000
# С модификатором bool сравнение даёт 0/1 вместо фильтрации:
(node_load1 > bool 4) # 1, если нагрузка > 4, иначе 0
# Логические между векторами: and, or, unless
up == 0 and on(job) (http_requests_total > 0) # пересечение по метке job
slow_requests or fast_requests # объединение рядов
all_targets unless excluded_targets # разность множеств
Функции скорости: rate, irate, increase
Counter сам по себе бесполезен — важна скорость роста. Эти функции считают её по диапазонному вектору и корректно учитывают рестарты (сбросы счётчика).
# rate() — средняя скорость в секунду за окно. Главная функция для counter:
rate(http_requests_total[5m]) # запросов в секунду (RPS), сглажено
# increase() — прирост счётчика за окно (= rate * длина окна):
increase(http_requests_total[1h]) # сколько запросов за последний час
# irate() — мгновенная скорость по двум последним точкам, резко реагирует:
irate(http_requests_total[5m]) # для дашбордов с быстрой реакцией
# Важно: rate/increase применяют ТОЛЬКО к counter и ТОЛЬКО к range vector.
# Окно бери хотя бы вчетверо больше шага сбора (scrape_interval).
Агрегация
Операторы агрегации схлопывают множество рядов в один или в группы. Управление группировкой — через by (оставить метки) и without (убрать метки).
# Базовые: sum, avg, min, max, count, stddev, topk, bottomk, quantile.
# Суммарный RPS по всем инстансам (все метки схлопнуты в один ряд):
sum(rate(http_requests_total[5m]))
# by(...) — сгруппировать, сохранив указанные метки:
sum by(job) (rate(http_requests_total[5m])) # RPS на каждый job
sum by(code) (rate(http_requests_total[5m])) # разбивка по кодам
# without(...) — сгруппировать, убрав указанные метки (остальные сохранить):
sum without(instance) (rate(http_requests_total[5m]))
# Среднее и максимум по группам:
avg by(instance) (node_load1)
max by(datacenter) (node_memory_MemTotal_bytes)
# count — сколько рядов; topk — N наибольших рядов:
count by(job) (up == 1) # живых целей на job
topk(3, sum by(instance) (rate(http_requests_total[5m]))) # топ-3 по RPS
Функции: histogram_quantile, predict_linear, delta
Помимо скорости и агрегации есть функции для перцентилей, прогноза и изменений gauge.
# histogram_quantile() — перцентиль из гистограммы по корзинам _bucket.
# Сначала rate по корзинам, затем агрегация sum by(le), затем квантиль:
histogram_quantile(
0.95,
sum by(le) (rate(http_request_duration_seconds_bucket[5m]))
) # p95 задержки в секундах
# predict_linear() — линейный прогноз gauge на N секунд вперёд.
# Когда кончится место на диске через 4 часа (14400 c)?
predict_linear(node_filesystem_avail_bytes[1h], 4 * 3600) < 0
# delta() — изменение gauge за окно (может быть отрицательным):
delta(node_memory_MemFree_bytes[1h]) # на сколько изменилась память за час
# Родственные: deriv() (производная gauge), idelta(), changes() (число смен),
# resets() (число сбросов counter), *_over_time() по диапазону:
avg_over_time(node_load1[10m])
max_over_time(node_load1[1h])
Временные сдвиги: offset
Модификатор offset сдвигает точку отсчёта в прошлое — удобно сравнивать «сейчас» с «вчера».
# Значение метрики каким оно было 1 час назад:
http_requests_total offset 1h
# Прирост по сравнению с прошлой неделей:
sum(rate(http_requests_total[5m]))
/
sum(rate(http_requests_total[5m] offset 1w))
# offset работает и с диапазонным вектором (внутри функции):
rate(http_requests_total[5m] offset 1d)
# @ — привязка к абсолютному моменту (Unix-время):
http_requests_total @ 1672531200
Типичные запросы
Готовые шаблоны, которые встречаются в большинстве дашбордов.
# RPS (запросов в секунду) по сервисам:
sum by(job) (rate(http_requests_total[5m]))
# Доля ошибок (error rate): 5xx делим на все запросы:
sum(rate(http_requests_total{code=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
# p95 задержки из гистограммы:
histogram_quantile(
0.95,
sum by(le) (rate(http_request_duration_seconds_bucket[5m]))
)
# Загрузка CPU в % (через долю времени НЕ в idle):
100 * (1 - avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])))
# Использование памяти в %:
100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)
# Заполнение диска в %:
100 * (1 - node_filesystem_avail_bytes / node_filesystem_size_bytes)
Алерты: условия кратко
Алерты в Prometheus — это PromQL-выражение, которое возвращает ряды только когда что-то не так. Если результат пуст — всё спокойно. Сравнения >, <, == формируют само условие.
# Цель недоступна (метрика up равна 0):
up == 0
# Высокая доля ошибок: более 5% 5xx:
sum by(job) (rate(http_requests_total{code=~"5.."}[5m]))
/
sum by(job) (rate(http_requests_total[5m]))
> 0.05
# Медленный сервис: p95 задержки больше 1 секунды:
histogram_quantile(0.95,
sum by(le) (rate(http_request_duration_seconds_bucket[5m]))) > 1
# Мало памяти: свободно меньше 10%:
node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.1
# Диск кончится в ближайшие 4 часа (прогноз уходит ниже нуля):
predict_linear(node_filesystem_avail_bytes[6h], 4 * 3600) < 0
# В правиле алерта условие оборачивают в alert / expr / for / labels:
# - alert: HighErrorRate
# expr: ...условие выше...
# for: 10m # держаться 10 минут, чтобы не было ложных срабатываний
# labels: { severity: "critical" }
# annotations: { summary: "Много ошибок 5xx на {{ $labels.job }}" }