Зачем Prometheus
До Prometheus monitoring был push-based: агент на хосте слал метрики в Graphite/Nagios. Проблемы:
- Агент знает адрес сервера, файрвол открыт обратно
- При падении сервера agent копит локально или теряет
- Конфигурация дублируется на каждом хосте
- Service discovery, отдельная боль
Prometheus (2012, SoundCloud, потом CNCF) перевернул модель: сервер сам scrape (HTTP GET) target'ы. Преимущества:
- Один центральный конфиг
- Service discovery встроенный (k8s, Consul, EC2, file)
- Down-detection бесплатно (
up == 0) - Pull можно из интранета через push gateway если нужно
Сегодня Prometheus + Grafana, де-факто стандарт metrics в Kubernetes и cloud-native.
Архитектура
┌─────────────┐ scrape (HTTP /metrics)
│ Prometheus │ ◄─────────────┬──────────┐
│ server │ │ │
│ ┌─────────┐ │ ┌──┴──┐ ┌───┴───┐
│ │ TSDB │ │ │ app │ │ node- │
│ │ (15d) │ │ │ /m │ │ exp │
│ └─────────┘ │ └─────┘ └───────┘
│ ┌─────────┐ │
│ │ rules │ ──► alerts ──► Alertmanager ──► PagerDuty/Slack
│ │ engine │ │
│ └─────────┘ │
│ │
│ remote_write│ ──► VictoriaMetrics / Thanos / Mimir (long-term)
└─────────────┘
▲
│ PromQL HTTP
┌─────┴────┐
│ Grafana │ + ad-hoc queries
└──────────┘
Key components:
- scrape engine, pulls
/metricsкаждые 15-60s - TSDB, local time-series DB, blocks по 2 часа
- rules engine, оценивает recording + alerting rules каждые 15s
- HTTP API,
/api/v1/query,/query_range, для Grafana иpromtool
Pull-модель: scrape
Каждый target экспортит /metrics в text format ([[metric-types|OpenMetrics]]):
# HELP http_requests_total Number of requests
# TYPE http_requests_total counter
http_requests_total{method="GET",status="200"} 12345http_requests_total{method="POST",status="500"} 12Конфиг scrape:
scrape_configs:
- job_name: my-app
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
static_configs:
- targets: ['app1:8080', 'app2:8080']
В реальности, не static, а через [[service-discovery-prometheus|service discovery]] (k8s endpoints, Consul, file_sd).
TSDB, как хранится
Time-series identifier: metric_name{label1=val1,label2=val2}. Каждая
уникальная комбинация = отдельный series.
На диске:
/var/lib/prometheus/
├── 01HABC.../ # 2-часовой block
│ ├── chunks/
│ ├── index # postings: label → series IDs
│ ├── tombstones
│ └── meta.json
├── 01HABD.../
└── wal/ # write-ahead log (current 2h window)
Compaction: 2h blocks → 16h → 5d → 14d, как LSM-tree. После retention (default 15d), удалить.
Memory ≈ 3 KB на active series + WAL. 1M series ≈ 3-4 GB RAM.
PromQL, query language
4 типа запросов:
Instant query, значение в момент времени:
up # все 'up' gauge сейчас
rate(http_requests_total[5m]) # rate за последние 5 мин
Range query, series за период (для графиков):
GET /api/v1/query_range?query=up&start=...&end=...&step=15s
Aggregation через sum, avg, max, min, count, topk:
sum by (job)(up) # сколько up по job
topk(5, rate(http_requests_total[5m])) # топ-5 по rate
histogram_quantile(0.99, sum by (le)(rate(req_duration_bucket[5m])))
Functions:
rate(counter[5m]), per-second rate, handles resetirate(counter[1m]), instant rate, для волатильныхincrease(counter[1h]), total за час (=rate * window)delta(gauge[1h]), изменениеpredict_linear(gauge[1h], 4*3600), прогноз через 4ч (для disk-full)
PromQL векторный: операции on labels:
rate(http_requests_total{status="500"}[5m])/
rate(http_requests_total[5m])
> 0.05 # error rate >5%
Recording rules, pre-compute
Дорогие queries (особенно с histogram_quantile и multi-join) кэшируем
как recording rules, Prometheus сам их вычисляет каждые 15s и пишет
как обычные series:
groups:
- name: app_slo
interval: 30s
rules:
- record: app:http_request_rate:5m
expr: sum by (job, status)(rate(http_requests_total[5m]))
- record: app:http_request_duration:p99:5m
expr: histogram_quantile(0.99,
sum by (job, le)(rate(http_request_duration_bucket[5m])))
Convention: aggregation_level:metric:window. Используется в
dashboards и [[alerting-rules-alertmanager|alerting]].
Alerting rules
groups:
- name: app
rules:
- alert: HighErrorRate
expr: app:http_error_rate:5m > 0.05
for: 5m # должно держаться 5 мин
labels:
severity: critical
annotations:
summary: "Error rate {{ $value | humanizePercentage }}"runbook: "https://wiki/runbooks/high-error-rate"
Если expr возвращает не-empty vector for: 5m, alert fires в
Alertmanager. См. alerting-rules-alertmanager.
Retention и remote_write
Local TSDB, короткое хранение (default 15d, max ~2 месяца). Для долгосрочного, remote_write в системы заточенные под scale:
| Backend | Storage | Tenancy | Compat |
|---|---|---|---|
| Thanos | object (S3/GCS) | per-tenant | PromQL |
| Cortex / Mimir | object | multi-tenant | PromQL |
| VictoriaMetrics | own LSM | YES (cluster) | PromQL+MetricsQL |
| M3DB | own | multi-tenant | PromQL |
| InfluxDB v3 | own | yes | InfluxQL/Flux |
remote_write:
- url: https://victoria-metrics:8480/api/v1/write
queue_config:
max_samples_per_send: 5000
capacity: 50000
Production setup: Prometheus как scraper+short-term, VictoriaMetrics cluster для 1+ year retention и querying.
Federation, иерархия Prometheus
Региональный Prom скрейпит локальные сервисы. Глобальный Prom скрейпит
агрегаты с региональных через /federate:
scrape_configs:
- job_name: federate
honor_labels: true
metrics_path: /federate
params:
match[]:
- '{__name__=~"job:.*"}' # только recording rulesstatic_configs:
- targets: ['region-prom-eu:9090', 'region-prom-us:9090']
Не federate raw метрики, только агрегаты. Иначе cardinality умножится.
Когда что-то пошло не так
- Prometheus OOM, обычно cardinality.
topk(20, count by (__name__)({}))покажет top metrics по series count. См. cardinality-explosion. up == 0для target, scrape failed. Проверь/targetsUI: timeout, DNS, 401, TLS. Логи Prom скажут точнее.rate()возвращает NaN, counter сбросился между samples (рестарт), или нет samples в окне. Расширь окно или проверь scrape.- Расхождение между Prom и Grafana,
stepв range query. Маленький step = больше точек = тяжелее query. - WAL replay долгий после рестарта, 2-3 минуты на 1M series.
Решение:
--storage.tsdb.wal-segment-size=128MBили Thanos sidecar. - Высокий 5xx rate в
/api/v1/query, длинные queries timeout. Используй recording rules или увеличь--query.timeout. out of boundsошибки в logs, clock skew между Prom и target, или target отдаёт sample из будущего. Поправь NTP (chrony-and-ntp).- Метрики прыгают на 0 раз в N минут, scrape duration > scrape interval, target не успевает. Уменьши metrics или увеличь interval.
Альтернативы
| Инструмент | Когда выбирать |
|---|---|
| Prometheus | дефолт для k8s, < 10M active series |
| VictoriaMetrics | 10×lighter RAM, кластер, drop-in PromQL |
| Thanos | хочешь S3-storage без отдельного DB-стэка |
| Mimir | Grafana Labs, multi-tenant SaaS-like |
| InfluxDB | tagged events, tracing-like queries |
| Datadog | hosted, не хочется OPS Prom-стэка |