Помните, когда мониторинг распределённой системы казался попыткой найти определённую песчинку на пляже с завязанными глазами? Да, были такие времена. А теперь представьте, что вы делаете это с тысячами узлов, микросервисами, общающимися друг с другом, как соседи, сплетничающие о происходящем, и задержками в сети, которые создают вам проблемы каждые пять секунд. Добро пожаловать в прекрасный хаос мониторинга производительности распределённых систем.

Правда в том, что без надлежащего мониторинга ваша распределённая система — это, по сути, чёрный ящик — и не тот, который является информативным самописцем. Это тот, который заставляет вас лихорадочно обновлять информационные панели в 3 часа ночи, пока ваша система оповещения молча не срабатывает, потому что, ну, у кого было время настроить её должным образом?

Эта статья поможет вам создать комплексную систему мониторинга производительности, которая превратит вашу распределённую инфраструктуру из загадки, завёрнутой в головоломку, во что-то понятное, отлаживаемое и оптимизируемое.

Понимание ландшафта мониторинга

Прежде чем мы начнём забрасывать проблему инструментами и кодом, давайте определимся, что мы на самом деле пытаемся решить. В распределённых системах производительность не существует в вакууме. Ваша задержка зависит не только от циклов процессора; на неё влияют операции ввода-вывода в сети, постановка сообщений в очередь, попадание в кэш, обратные пути к базе данных и десятки других факторов, которые действуют против вас слаженно.

Эффективный мониторинг производительности требует одновременного измерения нескольких параметров. Вам нужно понимать не только то, что работает медленно, но и где оно работает медленно и почему. Вот разница между интуитивным ощущением («система вялая») и оперативной информацией («запросы к пользовательскому сервису занимают 2,3 секунды, причём 1,8 секунды уходит на ожидание ответов от базы данных из-за исчерпания памяти на узле-7»).

Золотые сигналы: ваша полярная звезда

Прежде чем внедрять всё под солнцем, начните с четырёх золотых сигналов мониторинга от Google. Если вы не измеряете ничего другого, измерьте их:

Задержка измеряет, сколько времени требуется для обработки запроса. В распределённых системах необходимо отслеживать как сквозную задержку, так и задержки отдельных сервисов. Запрос может казаться быстрым для ваших пользователей, но быть мучительно медленным внутри вашей инфраструктуры.

Трафик представляет собой нагрузку на вашу систему — думайте о количестве запросов в секунду, обработанных транзакциях или потреблённой пропускной способности. Этот показатель помогает понять, имеете ли вы дело с постепенным ухудшением или внезапным всплеском.

Ошибки показывают, когда что-то идёт катастрофически неправильно. Частота ошибок важнее, чем вы думаете; даже частота ошибок в 0,1% становится серьёзной проблемой, когда вы обрабатываете миллион запросов в секунду.

Нагрузка указывает на то, насколько заполнены ваши ресурсы. Процессор с загрузкой 85% выглядит нормально, пока вы не поймёте, что это ведущий индикатор того, что всплеск вызовет перегрузку. Нагрузка говорит вам, сколько запаса у вас есть до того, как всё сломается.

Эти четыре показателя дают замечательное представление о состоянии системы, не перегружая вас данными.

Создание архитектуры мониторинга

Давайте визуализируем, как складывается надёжная система мониторинга:

graph TB A["Services Layer
Микросервисы, API
Базы данных, Кэш"] B["Инструментарий
Экспортеры метрик
Пользовательские сборщики"] C["База данных временных рядов
Prometheus"] D["Визуализация
Grafana"] E["Система оповещения
AlertManager"] F["Агрегация журналов
ELK/Loki"] G["Отслеживание APM
Jaeger/Zipkin"] A -->|Экспортировать метрики| B B -->|Push/Scrape| C C -->|Query| D C -->|Rules| E A -->|Отправить журналы| F A -->|Отслеживать запросы| G D -->|Визуализировать| H["Оперативные инженеры"] E -->|Оповещение| H F -->|Поиск/Анализ| H G -->|Расследование проблем| H

Эта архитектура прекрасно разделяет проблемы. Ваши сервисы экспортируют метрики, журналы и трассировки. Системы сбора собирают эти данные телеметрии. Системы хранения организуют их. Наконец, системы визуализации и оповещения делают их актуальными.

Ключевые показатели для отслеживания

Помимо золотых сигналов, вы захотите отслеживать многоуровневые показатели в области инфраструктуры, приложений и бизнеса.

Показатели инфраструктуры отслеживают необработанные ресурсы:

  • Использование ЦП и переключение контекста
  • Потребление памяти и события сборки мусора
  • Задержка и пропускная способность дискового ввода-вывода
  • Пропускная способность сети и потеря пакетов
  • Ограничения ресурсов и фактическое использование контейнеров/подов

Показатели приложений сосредоточены на том, как ведёт себя ваше программное обеспечение:

  • Задержка запросов (и процентили — p50, p95, p99, p99.9)
  • Пропускная способность (запросы в секунду)
  • Частота и типы ошибок
  • Глубина очереди для асинхронной работы
  • Процент попаданий в кэш
  • Исчерпание пула соединений с базой данных
  • Задержка вызовов API сторонних разработчиков

Бизнес-показатели соответствуют организационным целям:

  • Транзакции, обработанные в минуту
  • Коэффициент конверсии (для систем электронной коммерции)
  • Влияние простоев на выручку
  • Доступность функций, видимых пользователям

Магия происходит, когда вы сопоставляете эти показатели. Всплеск задержки запросов к базе данных коррелирует с увеличением использования памяти, что вызывает больше сборки мусора, что вызывает конкуренцию потоков, что вызывает тайм-ауты запросов. Без полной картины вы отлаживаете вслепую.

Реализация Prometheus для сбора метрик

Prometheus стал стандартом де-факто для сбора метрик в облачных системах, и не без причины. Он прост, мощен и хорошо интегрируется со всем.

Сначала настроим конфигурацию Prometheus:

# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets:
- localhost:9093
rule_files:
- 'alert_rules.yml'
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'api-service'
static_configs:
- targets: ['api-1:8080', 'api-2:8080', 'api-3:8080']
metrics_path: '/metrics'
scrape_interval: 10s
relabel_configs:
- source_labels: [__address__]
target_label: instance
- source_labels: [__scheme__]
target_label: scheme
- job_name: 'database'
static_configs:
- targets: ['db-primary:5432']
metrics_path: '/metrics'
scrape_interval: 30s
- job_name: 'node-exporter'
static_configs:
- targets:
- 'node-1:9100'
- 'node-2:9100'
- 'node-3:9100'

Теперь создадим правила оповещения, которые действительно имеют значение:

# alert_rules.yml
groups:
- name: performance_alerts
interval: 30s
rules:
- alert: HighLatency
expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 0.5
for: 2m
labels:
severity: warning
annotations:
summary: "Обнаружена высокая задержка запроса"
description: "95-й процентиль задержки составляет {{ $value }} с для {{ $labels.service }}"
- alert: ErrorRateElevated
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.01
for: 1m
labels:
severity: critical
annotations:
summary: "Частота ошибок выше 1%"
description: "{{ $labels.service }} имеет {{ $value }} ошибок в секунду"
- alert: HighCPUUsage
expr: node_cpu_usage > 85
for: 3m
labels:
severity: warning
annotations:
summary: "Высокое использование ЦП на {{ $labels.instance }}"
description: "ЦП на {{ $value }}%"
- alert: MemoryPressure
expr: (node_memory_used / node_memory_total) > 0.85
for: 2m
labels:
severity: warning
annotations:
summary: "Критическое использование памяти на {{ $labels.instance }}"
description: "Память на {{ $value | humanizePercentage }}"
- alert: QueueDepthBuildup
expr: queue_depth > 10000
for: 5m
labels:
severity: warning
annotations:
summary: "Накопление невыполненной работы в очереди"
description: "В очереди {{ $labels.queue_name }} накопилось {{ $value }} элементов"
- alert: DatabaseConnectionPoolExhaustion
expr: (