Prometheus recording rules: предварительная агрегация дорогих запросов
Published: 2026-03-11
Range-запросы Prometheus по меткам с высокой кардинальностью работают медленно. Запрос за 7 дней по сотням подов, выполняемый при каждой загрузке панели Grafana, обходится дорого — и эта нагрузка накапливается. Recording rules решают проблему: результат вычисляется заранее, с заданным интервалом.
Как работают recording rules
Recording rule — это PromQL-выражение, которое выполняется с заданным интервалом и записывает результат как новую метрику с более простым именем. Grafana запрашивает предвычисленную метрику вместо дорогостоящего исходного выражения.
yamlapiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: recording-rules
namespace: observability
labels:
release: kube-prom-stack # должен совпадать с selector Prometheus
spec:
groups:
- name: recording
interval: 30s
rules:
- record: job:http_requests_total:rate5m
expr: |
sum by (job, namespace) (
rate(http_requests_total[5m])
)
interval: 30s — результат обновляется каждые 30 секунд. Панели Grafana отвечают мгновенно без тяжёлого range-запроса при загрузке.
Соглашение об именовании
Стандарт: level:metric:operations
level— уровень агрегации (job,namespace,cluster,instance)metric— имя исходной метрикиoperations— что было сделано (rate5m,sum,p99,ratio)
Примеры:
namespace:container_cpu_usage_seconds:rate5m
job:http_request_duration_seconds:p99
cluster:node_memory_working_set_bytes:sum
job:http_errors:rate5m_ratio
Практические правила для мультикластерной установки
yamlgroups:
- name: cluster-resources
interval: 60s
rules:
- record: namespace:container_cpu_usage_seconds_total:rate5m
expr: |
sum by (namespace, cluster) (
rate(container_cpu_usage_seconds_total{container!=""}[5m])
)
- record: pod:container_memory_working_set_bytes:sum
expr: |
sum by (pod, namespace, cluster) (
container_memory_working_set_bytes{container!=""}
)
- record: job:http_errors:rate5m
expr: |
sum by (job, namespace) (
rate(http_requests_total{status=~"5.."}[5m])
)
/
sum by (job, namespace) (
rate(http_requests_total[5m])
)
- record: job:http_request_duration_seconds:p99
expr: |
histogram_quantile(0.99,
sum by (job, namespace, le) (
rate(http_request_duration_seconds_bucket[5m])
)
)
Скорость сжигания бюджета ошибок SLO
yaml - name: slo-burn
interval: 60s
rules:
- record: job:slo_error_budget_burn:1h
expr: |
(
sum by (job) (rate(http_requests_total{status=~"5.."}[1h]))
/
sum by (job) (rate(http_requests_total[1h]))
) / 0.001
- record: job:slo_error_budget_burn:6h
expr: |
(
sum by (job) (rate(http_requests_total{status=~"5.."}[6h]))
/
sum by (job) (rate(http_requests_total[6h]))
) / 0.001
Мультиоконный алерт:
yaml- alert: HighErrorBudgetBurn
expr: |
job:slo_error_budget_burn:1h > 14
and
job:slo_error_budget_burn:6h > 6
for: 2m
labels:
severity: critical
annotations:
description: "{{ $labels.job }} сжигает бюджет ошибок со скоростью {{ $value | humanize }}x"
Скорость 14x за 1 час означает исчерпание месячного бюджета примерно за 2 дня.
Recording rules для ресурсов Kubernetes
yaml- name: k8s-resource-recording
interval: 60s
rules:
- record: node:cpu_utilization:ratio
expr: |
1 - avg by (node) (
rate(node_cpu_seconds_total{mode="idle"}[5m])
)
- record: node:memory_utilization:ratio
expr: |
1 - (
node_memory_MemAvailable_bytes /
node_memory_MemTotal_bytes
)
- record: persistentvolumeclaim:disk_usage:ratio
expr: |
1 - (
kubelet_volume_stats_available_bytes /
kubelet_volume_stats_capacity_bytes
)
Где размещать PrometheusRule
Recording rules — в base/monitoring/rules/recording.yaml, применяются на всех кластерах через базовую Kustomization. Правила для конкретных окружений (разные SLO-пороги для dev и prod) — в patches/monitoring/rules/.
Один PrometheusRule на задачу: recording, node alerts, SLO alerts. Это минимизирует diff при изменении порогов.
Проверка загрузки правил
bashkubectl port-forward -n observability svc/kube-prom-stack-prometheus 9090:9090
# http://localhost:9090/rules
curl -s 'http://localhost:9090/api/v1/query?query=job:http_requests_total:rate5m' | jq '.data.result | length'
Если правило не появляется, проверьте, что label selector в PrometheusRule совпадает с конфигурацией kube-prom-stack.