LEARN X · ЗА 13 МИН

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 }}" }
Поддержать проект