Как мониторить распределённую систему микросервисов
4. Saturation (Насыщение): Насколько загружен самый узкий ресурс системы (память JVM, ядра CPU, свободные коннекты в HikariCP, заполненность диска).
🟢 Junior Level
Мониторинг и наблюдаемость (Observability) в микросервисной архитектуре — это комплекс практик и инструментов, позволяющий в реальном времени понимать внутреннее состояние системы на основе её внешних данных: метрик, логов и трассировок.
┌──────────────────────────────────────────────┐
│ Три кита Observability (The Three Pillars) │
└──────┬───────────────┬───────────────┬───────┘
│ │ │
▼ ▼ ▼
[ МЕТРИКИ ] [ ЛОГИ ] [ ТРАССИРОВКИ ]
(Prometheus) (Loki / ELK) (Jaeger / OTel)
«Что и когда «Почему это «Где именно
сломалось?» произошло?» возникла задержка?»
Четыре золотых сигнала (Google SRE Golden Signals):
- Latency (Задержка): Время, необходимое для обработки запроса (отдельно для успешных запросов и запросов с ошибками).
- Traffic (Трафик): Мера входящего спроса на систему (запросов в секунду — RPS, транзакций в секунду — TPS).
- Errors (Ошибки): Частота запросов, завершившихся неудачей (HTTP 5xx, тайм-ауты, выброшенные исключения).
- Saturation (Насыщение): Насколько загружен самый узкий ресурс системы (память JVM, ядра CPU, свободные коннекты в HikariCP, заполненность диска).
🟡 Middle Level
1. Методологии мониторинга: RED vs USE
В микросервисах применяются две взаимодополняющие методологии:
- RED Method (для сервисов и API):
- Rate: Количество запросов в секунду.
- Errors: Количество ошибок в секунду.
- Duration: Распределение времени выполнения запросов (перцентили).
- USE Method (для инфраструктуры и ресурсов — CPU, RAM, диски):
- Utilization: Процент времени, когда ресурс был занят полезной работой.
- Saturation: Очередь задач к ресурсу (глубина очереди потоков, Load Average).
- Errors: Физические ошибки оборудования или сбои интерфейсов.
2. Сбор метрик в Java: Spring Boot Actuator + Micrometer
Micrometer — это фасад для сбора метрик в Java-приложениях (аналог SLF4J для логирования), транслирующий данные в Prometheus:
@RestController
@RequiredArgsConstructor
public class CheckoutController {
private final MeterRegistry registry;
private final OrderService orderService;
private Counter checkoutSuccessCounter;
private Counter checkoutFailCounter;
private Timer checkoutTimer;
@PostConstruct
void initMetrics() {
this.checkoutSuccessCounter = Counter.builder("checkout_orders_total")
.description("Количество успешно оформленных заказов")
.tag("status", "success")
.register(registry);
this.checkoutFailCounter = Counter.builder("checkout_orders_total")
.description("Количество неудачных оформлений")
.tag("status", "failure")
.register(registry);
this.checkoutTimer = Timer.builder("checkout_duration_seconds")
.description("Время оформления заказа")
.publishPercentiles(0.5, 0.95, 0.99)
.register(registry);
}
@PostMapping("/checkout")
public OrderResponse checkout(@RequestBody OrderRequest req) {
return checkoutTimer.record(() -> {
try {
OrderResponse response = orderService.process(req);
checkoutSuccessCounter.increment();
return response;
} catch (Exception ex) {
checkoutFailCounter.increment();
throw ex;
}
});
}
}
3. Базовые PromQL-запросы для алертинга
Расчет процента ошибок сервиса за последние 5 минут:
sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
/
sum(rate(http_server_requests_seconds_count[5m])) * 100 > 5
Расчет 99-го перцентиля задержки (p99 latency):
histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le))
🔴 Senior Level
1. Взрыв кардинальности (Metrics Cardinality Explosion)
Самая опасная ошибка при работе с TSDB (Time Series Database — Prometheus / VictoriaMetrics):
- Каждая уникальная комбинация имени метрики и её лейблов (tags) создаёт отдельный временной ряд (Time Series), хранящийся в оперативной памяти Prometheus.
- АНТИПАТТЕРН:
// КАТАСТРОФА! Поле userId или orderId имеет миллионную кардинальность! registry.counter("order_created", "user_id", req.getUserId()).increment(); - Результат: 1 000 000 пользователей $\to$ 1 000 000 новых time series в Prometheus $\to$ сервер мониторинга падает с
OutOfMemoryError, а вся наблюдаемость компании исчезает. - Правило: Лейблы должны быть низкокардинальными (Low Cardinality): код статуса (
status=SUCCESS), тип операции (payment_type=CARD), имя сервиса. Любые динамические ID (userId,orderId) передаются только в логи и трейсы.
2. SLI, SLO, SLA и концепция Error Budget (Бюджет ошибок)
SLA (Юридический договор): 99.0% доступности (штраф при нарушении)
▲
│ (Запас безопасности)
SLO (Внутренняя цель инженеров): 99.9% доступности
▲
│ (Фактическое измерение по метрикам)
SLI (Индикатор): (Успешные запросы / Всего запросов) за 30 дней
- Error Budget (Бюджет ошибок): Допустимый объем брака. При SLO 99.9% бюджет ошибок составляет $100\% - 99.9\% = 0.1\%$ от всех запросов в месяц.
- Если система укладывается в бюджет ошибок — продуктовые команды имеют право быстро деплоить новые рискованные фичи.
- Если бюджет ошибок исчерпан за первую неделю месяца — релизы замораживаются, и все силы направляются на стабилизацию архитектуры и устранение техдолга.
3. Multi-Window Multi-Burn-Rate Alerts (Google SRE)
Классические алерты «Если ошибка > 1% за 5 минут» страдают двумя крайностями:
- Либо они спамят ложными тревогами при кратковременном всплеске (Alert Fatigue).
- Либо не замечают медленное выедание месячного бюджета ошибок при деградации в 0.2%.
Промышленный стандарт Google SRE: Мониторинг скорости расходования бюджета ошибок (Burn Rate) на перекрывающихся окнах:
- Если сжигается 14.4x бюджета за 1 час (2% месячного бюджета сгорит за час) $\to$ Критический алерт (Page / Звонок дежурному в PagerDuty).
- Если сжигается 3x бюджета за 6 часов (5% бюджета сгорит за 6 часов) $\to$ Тикет / Уведомление в Slack в рабочее время.
4 Tricky Questions
1. Почему среднее время ответа (Average Latency) категорически непригодно для оценки качества сервиса?
Ответ: Среднее арифметическое математически скрывает катастрофические проблемы у меньшинства пользователей (эффект средней температуры по больнице):
- Если 99 пользователей получили ответ за 10 мс, а 1 VIP-пользователь с гигантской корзиной завис на 10 000 мс: \(\text{Average} = \frac{99 \times 10 + 10\,000}{100} = 109.9\text{ мс}\) Инженеры видят на графике «отличные» 110 мс и считают, что всё в порядке. При этом самый платежеспособный клиент бросил корзину из-за десятисекундного зависания. Только перцентили (p95, p99, p99.9) показывают реальный худший пользовательский опыт (Tail Latency).
2. Почему нельзя усреднять значения перцентилей между разными серверами (Average of p99)?
Ответ:
Перцентили не аддитивны. Нельзя вычислить $\frac{p99{node1} + p99{node2}}{2}$ и получить общий p99 кластера:
Если Сервер 1 обработал 10 000 запросов с $p99 = 100\text{ мс}$, а Сервер 2 обработал 10 запросов с $p99 = 5000\text{ мс}$, математическое среднее $(100 + 5000) / 2 = 2550\text{ мс}$ даст абсолютно искаженную картину.
Правило: В Prometheus для корректного вычисления квантилей агрегируются гистограммы (Histogram Buckets) по всем узлам кластера через sum(...) by (le), и только затем применяется функция histogram_quantile(0.99, ...).
3. В чем разница между Pull-моделью (Prometheus) и Push-моделью (StatsD / Datadog)?
Ответ:
- Pull-модель (Prometheus): Сервер мониторинга сам периодически (каждые 15 сек) опрашивает микросервисы по HTTP (
GET /actuator/prometheus). Плюсы: Легко обнаружить смерть сервиса (если под не отвечает на скрейп — он упал); защита сервера мониторинга от перегрузки (он сам регулирует частоту опроса). - Push-модель (StatsD / Telegraf): Микросервис сам шлет UDP/TCP пакеты на сервер мониторинга при каждом событии. Плюсы: Идеально для короткоживущих задач (Serverless / AWS Lambda / Batch Jobs), которые могут завершиться до прихода скрейпера Prometheus.
4. Что такое «Alert Fatigue» (Усталость от алертов) и по каким критериям алерт считается качественным (Actionable Alert)?
Ответ: Alert Fatigue возникает, когда дежурные инженеры получают десятки нерелевантных уведомлений в день. Со временем инженеры перестают реагировать на оповещения и игнорируют критическую аварию. Критерии качественного алерта (Actionable):
- Срабатывает только при нарушении пользовательского опыта (симптоматический алерт: упал SLO, растут 5xx ошибки), а не по внутренним причинам (например, «CPU > 80%» — не алерт, если сервис отвечает за 5 мс).
- Требует немедленного вмешательства человека.
- Имеет понятную инструкцию (Runbook / Playbook), где описаны первые три шага диагностики и локализации проблемы.
🎯 Шпаргалка для интервью
Главное:
- Три кита Observability: Метрики (Prometheus), Логи (Loki / OpenSearch), Трейсы (Jaeger / OpenTelemetry).
- 4 Golden Signals (Google): Latency, Traffic, Errors, Saturation.
- RED-метод для сервисов: Rate, Errors, Duration.
- USE-метод для серверов/ресурсов: Utilization, Saturation, Errors.
- Оценивайте скорость по перцентилям (p95, p99), а не по среднему арифметическому.
- Опасность кардинальности (Cardinality Explosion): Никогда не передавайте динамические ID (
userId,orderId) в теги метрик Prometheus! - Стандарт в Java — Spring Boot Actuator + Micrometer.
- Управление надежностью строится вокруг SLI / SLO и скорости выгорания Error Budget (Burn Rate).
Красные флаги (Чего говорить НЕЛЬЗЯ):
- ❌ «Мы измеряем скорость работы микросервисов по Average Latency» (Среднее скрывает катастрофические задержки хвоста распределения).
- ❌ «Мы пишем
userIdв теги метрик Prometheus для детальной аналитики» (Убьет память Prometheus за счет взрыва кардинальности). - ❌ «Чем больше алертов настроено в Grafana — тем надежнее система» (Приведет к Alert Fatigue и пропуску реальных инцидентов).