Prometheus: или как не утонуть в метриках

Введение: почему PromPull — это не просто слово, а философия

Prometheus не просто собирает метрики — он меняет парадигму мониторинга. В отличие от традиционных пуш-систем (Zabbix, Nagios), где агент сам отправляет данные на сервер, Prometheus работает по принципу pull: сервер сам запрашивает метрики у целей (targets) по HTTP-эндпоинту /metrics. Это кажется простым, но на практике приводит к трем ключевым проблемам:

  1. Латентность обнаружения проблем — если цель падает, Prometheus не узнает об этом сразу, пока не истечет таймаут запроса (по умолчанию 1 минута).
  2. Нагрузка на цели — если у вас тысячи микросервисов, каждый из которых генерирует сотни метрик, то даже при оптимизированном polling нагрузка может стать критической.
  3. Управление состоянием — в pull-системе состояние цели определяется не сервером, а клиентом. Это значит, что если ваш сервис перестал отвечать, Prometheus не знает об этом, пока не получит таймаут.

Вывод: Prometheus — это не просто инструмент, а система, требующая правильной настройки и понимания trade-offs. Если вы просто развернете его "из коробки", вы получите либо ложное чувство безопасности (метрики есть, но они не отражают реальное состояние), либо производительность, падающую под нагрузкой.


Проблема: когда Prometheus становится камнем на шее

Мы видели проекты, где Prometheus:

  • Заедал CPU на 90%+ из-за неоптимизированных запросов к Thanos или Cortex.
  • Падало под нагрузкой из-за неверно настроенного scrape_configs (например, слишком частый polling для тяжелых целей).
  • Генерал метрик в миллиарды точек, из-за чего PromQL-запросы выполнялись по 30 секунд.
  • Не обнаруживал критических ошибок из-за неверно настроенных alertmanager-ов.

Причина почти всегда одна: отсутствие понимания, что Prometheus — это не только сам сервер, а экосистема, включающая:

  • Цели (targets) с правильно экспортируемыми метриками.
  • Scrape-конфигурации с учетом латентности и нагрузки.
  • Storage (Prometheus сам по себе хранит данные только 15 дней, дальше нужны Thanos, Cortex или VictoriaMetrics).
  • Alerting с правильно настроенными правилами и тишиной (silences).
  • Visualization (Grafana, но не только — иногда проще писать свои дашборды на PromQL+Python).

Практика: как не завалить Prometheus на старте

1. Настройка scrape-конфигураций: не все цели равны

Ошибка №1: Одинаковый интервал polling для всех целей. Решение: Настройте разные интервалы в зависимости от типа цели:

  • Легкие сервисы (API-гейты, кэши) — 15s.
  • Тяжелые БД (PostgreSQL, MongoDB) — 60s.
  • Микросервисы с высокой латентностью30s.
  • Системные метрики (node_exporter) — 15s.

Пример конфига prometheus.yml для разных типов целей:

scrape_configs:
  - job_name: 'api_gateway'
    scrape_interval: 15s
    static_configs:
      - targets: ['api-gateway-1:8080', 'api-gateway-2:8080']

  - job_name: 'database'
    scrape_interval: 60s
    static_configs:
      - targets: ['postgres-master:5432', 'postgres-replica:5432']

  - job_name: 'system_metrics'
    scrape_interval: 15s
    static_configs:
      - targets: ['node-exporter-1:9100', 'node-exporter-2:9100']

Лайфхак: Используйте scrape_timeout (по умолчанию scrape_interval), но уменьшайте его для тяжелых целей. Например, если polling идет каждые 60 секунд, а цель отвечает за 45 секунд, оставьте scrape_timeout: 55s, чтобы избежать накопления задержек.


2. Метрики: не все равны, и их нужно фильтровать

Ошибка №2: Сбор всех метрик без фильтрации. Решение: Используйте metric_relabel_configs для отсеивания ненужного шума.

Пример: Отфильтруйте метрики process_* для легких сервисов, оставив только критические:

scrape_configs:
  - job_name: 'light_service'
    scrape_interval: 15s
    metric_relabel_configs:
      - source_labels: [__name__]
        regex: 'process_.*'
        action: drop  # Убираем все process-метрики
    static_configs:
      - targets: ['light-service:8080']

Еще один лайфхак: Используйте relabel_configs для добавления дополнительных меток (labels), чтобы группировать цели по типам:

relabel_configs:
  - source_labels: [__address__]
    target_label: env
    regex: '(.*)\.example\.com'
    replacement: '$1'

3. Storage: Prometheus один не вытянет

Ошибка №3: Хранение данных только в Prometheus (максимум 15 дней). Решение: Используйте Thanos или VictoriaMetrics для долговременного хранения.

Пример конфига Thanos для Prometheus:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

storage:
  tsdb:
    path: /prometheus/data
  thanos:
    object_storage_config_file: /etc/prometheus/thanos.yaml

rule_files:
  - /etc/prometheus/rules/*.yaml

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']

Конфиг Thanos для S3:

type: S3
config:
  bucket: "my-prometheus-bucket"
  endpoint: "s3.example.com"
  access_key: "AKIA..."
  secret_key: "secret"
  insecure: true

Важно: Настройте компактность (compaction) в Thanos, чтобы избежать взрыва хранилища:

compaction:
  level: "monthly"
  max_compaction_segment_duration: "30d"

Примеры: как не завалить PromQL-запросы

Пример 1: Запрос, который убивает производительность

Плохой запрос (сканирует все метрики):

sum(rate(http_requests_total[5m])) by (service)

Проблема: Если у вас 1000 сервисов и каждая метрика экспортируется в 10 вариантах (status, method, path), то запрос может обработать миллионы временных серий.

Решение: Уже на уровне экспорта фильтруйте метрики или используйте record для предварительной агрегации:

# В rules.yml
groups:
- name: aggregation
  rules:
  - record: job:http_requests_total:rate5m
    expr: sum(rate(http_requests_total[5m])) by (job, service)

Пример 2: Правильное использование group_left и group_right

Задача: Найти долю ошибок по сервисам.

Плохой запрос (теряет контекст):

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

Решение: Используйте group_left для сохранения контекста:

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

Типичные ошибки и как их избежать

Ошибка Причина Решение
Prometheus падает под нагрузкой Слишком частый polling тяжелых целей Увеличьте scrape_interval, добавьте scrape_timeout
Alertmanager шлет дубликаты Неправильно настроенные group_by в alert rules Используйте group_by: [alertname, cluster]
Метрики не обновляются Целевой сервис не экспортирует /metrics Проверьте targets, логи сервиса, сетевые правила
PromQL-запросы висят 30+ секунд Слишком много временных серий Фильтруйте метрики на уровне экспорта, используйте record
Thanos съедает весь диск Не настроена компактизация Настройте compaction в thanos.yaml
Ложные тревоги Неправильные условия в alert rules Используйте for и unless, тестируйте в Grafana

Вывод: как сделать Prometheus полезным, а не обузой

  1. Настройте scrape-конфигурации под реальные нужды — не все цели должны поллиться одинаково часто.
  2. Фильтруйте метрики на уровне экспорта — не собирать все, что можно собрать.
  3. Используйте долговременное хранилище (Thanos/VictoriaMetrics) — Prometheus один не справится с длительным ретеншном.
  4. Оптимизируйте PromQL-запросы — не пишите запросы, которые сканируют миллионы серий.
  5. Настраивайте alerting правильно — используйте group_by, for, unless, и тестируйте правила в Grafana перед развертыванием.
  6. Мониторьте сам Prometheus — следите за prometheus_target_scrapes_total, prometheus_tsdb_head_series, prometheus_tsdb_wal_flushes_total.

Финальный совет: Prometheus — это не "развернул и забыл". Это живая система, которая требует:

  • Регулярной оптимизации (чистка ненужных метрик, настройка интервалов).
  • Тестирования (проверяйте alert rules в Grafana, а не в продакшене).
  • Документирования (какие метрики критические, какие — информационные).

Если вы сделаете это правильно, Prometheus станет одним из самых полезных инструментов в вашем стеке. Если нет — он превратится в черную дыру метрик, которая съедает ресурсы и не дает никакой ценной информации.