🐳 Раздел 14 · Вопрос #24

Как мониторить приложения в Kubernetes

Мониторинг — это приборная панель и бортовой самописец современного авиалайнера:


🟢 Junior Level

30-секундный ответ

Комплексный мониторинг (Observability / Наблюдаемость) в Kubernetes строится на концепции «Трёх китов наблюдаемости»:

  1. Метрики (Metrics — Что и когда происходит в цифрах?): числовые временные ряды (загрузка CPU/памяти, RPS, задержки p99, ошибки 5xx). Де-факто стандартом является связка Prometheus + Grafana.
  2. Логи (Logs — Почему это произошло?): текстовые потоки событий из stdout/stderr контейнеров. В Kubernetes их собирают DaemonSet-агенты (Promtail, Fluentbit, Vector) и отправляют в централизованные хранилища (Loki, Elasticsearch / OpenSearch).
  3. Распределённая трассировка (Distributed Tracing — Где застрял запрос?): сквозной путь отдельного HTTP-запроса через десятки микросервисов с замером времени на каждом этапе (OpenTelemetry, Jaeger, Tempo).

Наглядная аналогия из жизни

Мониторинг — это приборная панель и бортовой самописец современного авиалайнера:

  • Метрики: спидометр, высотомер, датчики оборотов двигателей и остатка топлива. Пилот видит общую картину полёта на экране (Grafana).
  • Логи: журнал радиопереговоров и бортовой журнал ошибок (Black Box), куда поминутно записывается каждое действие экипажа.
  • Трейсинг: навигационный трекер пути пассажирского багажа: регистрация $\to$ погрузка на рейс $\to$ пересадка в транзитном аэропорту $\to$ выдача (позволяет мгновенно понять, в каком именно аэропорту потерялась сумка).

Быстрый старт: Spring Boot 3 + Micrometer + Prometheus

В современных Java-микросервисах сбор метрик подключается добавлением двух зависимостей:

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
# application.yaml
management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus # Открыть эндпоинт /actuator/prometheus
  metrics:
    export:
      prometheus:
        enabled: true

🟡 Middle Level

Архитектура стека kube-prometheus-stack (Pull-модель)

[ Физические Ноды ]         [ Kubelet / Cgroups ]        [ API Server ]         [ Java Spring Boot Pods ]
        │                            │                         │                           │
  Node Exporter                   cAdvisor             kube-state-metrics             Micrometer
(Метрики ОС, RAM/CPU)       (Метрики контейнеров)     (Статусы подов, PVC)        (/actuator/prometheus)
        ▲                            ▲                         ▲                           ▲
        └────────────────────────────┼─────────────────────────┼───────────────────────────┘
                                     │ (Scrape каждые 15 секунд: HTTP GET)
                           [ Prometheus Server ] ──► Временные ряды (TSDB)
                                     │
                    ┌────────────────┴────────────────┐
                    ▼                                 ▼
          [ Grafana (Дашборды) ]         [ Alertmanager (Slack/PagerDuty) ]
  • Pull-модель Prometheus: Prometheus сам регулярно опрашивает (scrapes) цели по HTTP каждые 15–30 секунд через обнаружение сервисов (Service Discovery).
  • Разделение источников метрик:
    • Node Exporter (DaemonSet): аппаратные метрики серверов (нагрузка на диск, сеть, ядра).
    • cAdvisor (встроен в kubelet): потребление ресурсов контейнерами из Linux cgroups.
    • kube-state-metrics (Deployment): логическое состояние объектов K8s (почему под упал в CrashLoop, сколько реплик живо, объем PVC).

Декларативный мониторинг через ServiceMonitor (Prometheus Operator)

Вместо ручного редактирования prometheus.yml используется Custom Resource Definition — ServiceMonitor:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: payment-monitor
  namespace: billing
  labels:
    release: prometheus # Лейбл оператора
spec:
  selector:
    matchLabels:
      app: payment-service
  endpoints:
  - port: http-metrics
    path: /actuator/prometheus
    interval: 15s

Топ-5 PromQL запросов для Java-инженера

# 1. Загрузка Heap памяти JVM в процентах
(jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"}) * 100

# 2. Суммарная длительность пауз сборщика мусора (GC Stop-The-World)
rate(jvm_gc_pause_seconds_sum[5m])

# 3. Количество HTTP-запросов в секунду (RPS)
sum(rate(http_server_requests_seconds_count[1m])) by (uri)

# 4. Процент ошибок HTTP 5xx от общего трафика
sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
/ sum(rate(http_server_requests_seconds_count[5m])) * 100

# 5. Время ответа API на 99-м перцентиле (Latency p99)
histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le))

🔴 Senior Level

Катастрофа «Взрывной мощности меток» (Cardinality Explosion)

Самая частая причина падения серверов Prometheus в крупных компаниях — High Cardinality:

  • Теория: Каждая уникальная комбинация пар label=value порождает в базе данных Prometheus TSDB новую отдельную временную серию.
  • Катастрофическая ошибка разработчика:
    // ❌ УБИЙСТВО ПРОМЕТЕУСА: запись уникального ID в лейбл метрики
    meterRegistry.counter("orders.created", 
        "userId", order.getUserId(),      // 10 000 000 уникальных ID
        "orderId", order.getOrderId()     // 50 000 000 уникальных ID
    ).increment();
    
  • Последствие: за несколько часов число серий подскакивает с 200 000 до 50 000 000. Prometheus TSDB съедает десятки гигабайт оперативной памяти, падает по OOMKilled, а при перезапуске бесконечно висит на чтении WAL-лога (Write-Ahead Log).
  • Правило: лейблы должны содержать строго ограниченный набор значений (Low Cardinality): метод HTTP (GET/POST), статус ответа (200/500), имя сервиса или тип ошибки (ValidationError/TimeoutException). Динамические идентификаторы клиентов (userId, orderId) должны писаться исключительно в логи или спаны трассировки (Tracing)!

Долгосрочное хранение и масштабирование: Thanos и VictoriaMetrics

Встроенная база данных Prometheus TSDB рассчитана на локальное хранение данных на SSD-диске в течение 15–30 дней. Она не умеет масштабироваться горизонтально. В Highload архитектурах применяются надстройки:

  • Thanos: разворачивает thanos-sidecar рядом с каждым Prometheus. Sidecar сжимает блоки метрик и непрерывно заливает их в дешевое объектное хранилище S3 (AWS S3, MinIO, Ceph). Thanos Querier позволяет выполнять сквозные PromQL-запросы по истории метрик за 3–5 лет с автоматическим понижением дискретизации (Downsampling).
  • VictoriaMetrics: легковесная альтернатива Prometheus TSDB с многократной экономией оперативной памяти (в 4–7 раз) и нативной поддержкой протокола PromQL и долгосрочного хранения.

Накладные расходы OpenTelemetry Tracing в High-Load

В распределенных микросервисах прохождение каждого запроса генерирует цепочку спанов с уникальным traceparent (стандарт W3C TraceContext):

  • Проблема Overhead: при потоке в 50 000 RPS сохранение 100% трейсов создаёт гигантскую нагрузку на CPU сериализации, переполняет буферы оперативной памяти JVM и забивает сеть кластера терабайтами телеметрии.
  • Архитектурное решение — Tail-Based Sampling (Сэмплирование на выходе):
    • Микросервисы отправляют трейсы на выделенный кластер OpenTelemetry Collector.
    • Collector временно держит весь жизненный цикл запроса в оперативной памяти.
    • Если запрос завершился с кодом HTTP 200 за 15 мс, он отбрасывается (drop).
    • Если запрос завершился с ошибкой HTTP 500 или задержка превысила 1 секунду, этот трейс сохраняется со всеми деталями (100% сохранение аномалий)!

4 Tricky Questions

1. Почему Prometheus использует Pull-модель сбора метрик по умолчанию, и как с его помощью мониторить короткоживущие задачи (CronJobs, Serverless Functions), завершающиеся быстрее интервала сбора?

Ответ:

  • Преимущества Pull: сервер Prometheus сам контролирует темп сбора данных (Rate Control), что предотвращает отказ мониторинга от наплыва входящих сообщений (DDoS от собственных приложений). Кроме того, если сервис перестал отвечать на scrape-запрос, Prometheus моментально фиксирует статус up == 0 (Health Check из коробки).
  • Решение для короткоживущих CronJobs: если контейнер запускается, считает задачу за 3 секунды и умирает, стандартный опрос (раз в 15 с) никогда его не поймает.
  • Для этого в стек внедряется промежуточный шлюз Prometheus Pushgateway:
    1. Короткоживущий джоб при завершении сам отправляет (Push) свои финальные метрики в Pushgateway через HTTP POST.
    2. Pushgateway сохраняет метрики в памяти.
    3. Prometheus спокойно опрашивает Pushgateway по стандартной Pull-модели в своем регулярном ритме.

2. В чём состоит принципиальная разница между cAdvisor и kube-state-metrics? Какая из систем покажет, почему Под ушёл в CrashLoopBackOff?

Ответ:

  • cAdvisor (Container Advisor): собирает физические телеметрические метрики использования ресурсов ядра ОС из подсистемы Linux cgroups (фактические наносекунды CPU, байты resident памяти, ввод-вывод диска, переданные сетевые пакеты). Он ничего не знает о концепциях Kubernetes (деплойменты, манифесты).
  • kube-state-metrics: слушает события kube-apiserver через informer’ы и преобразует декларативное состояние объектов Kubernetes в формат метрик.
  • Именно kube-state-metrics сгенерирует метрику kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"} и покажет код возврата exitCode: 137 или 1, а также количество рестартов и готовность реплик в Deployment. cAdvisor причину падения пода показать не способен.

3. Как сборщик мусора HotSpot JVM (GC Stop-The-World) может исказить метрики задержки (Latency), собираемые внутри Java-приложения, и как это обнаружить?

Ответ: Если метрика времени выполнения запроса замеряется внутри кода Java через таймер Micrometer:

  • Во время паузы Full GC (STW) все потоки приложения замораживаются, включая системный таймер приложения.
  • Если клиент отправил запрос, а JVM в этот момент ушла в GC-паузу на 2 секунды, клиент увидит задержку 2010 мс. Однако внутренний замер времени в контроллере Java может начаться только ПОСЛЕ окончания сборки мусора и показать «чистое» время обработки 10 мс!
  • Как обнаружить расхождение: сопоставить внутренние метрики latency приложения с внешними замерами на уровне Ingress Controller (Nginx $request_time) или Service Mesh (Envoy). Разница между Ingress Latency и App Latency точно соответствует длительности паузы STW GC и очереди в сокетах Linux.

4. Каким образом реализуется сквозная передача контекста трассировки (Trace Context Propagation) в асинхронных приложениях на Java (CompletableFuture, Reactive WebFlux, Virtual Threads)?

Ответ: Классические библиотеки трассировки (Sleuth, ранний OpenTelemetry) сохраняли TraceId и SpanId в локальной памяти потока — ThreadLocal:

  • При переключении контекста в асинхронных пулах (CompletableFuture.supplyAsync, Project Reactor / WebFlux) поток исполнения меняется, и данные ThreadLocal теряются, из-за чего единый трейс разрывается на несвязанные куски.
  • Решения:
    1. В реактивном стеке (Spring WebFlux): контекст трассировки оборачивается в иммутабельный реактивный контекст reactor.util.context.Context, распространяемый вверх по стриму операторов.
    2. В OpenTelemetry SDK: использование инструментации байт-кода Java Agent (-javaagent:opentelemetry-javaagent.jar), который перехватывает фабрики потоков Executor и автоматически копирует Context между рабочими потоками.
    3. В Java 21+ с Virtual Threads: легковесные виртуальные потоки корректно наследуют значения через механизм Scoped Values (JEP 446 / JEP 481) или оптимизированные InheritableThreadLocal.

🎯 Шпаргалка для интервью

Главное о мониторинге Kubernetes

  • Три столпа Observability: Метрики (Prometheus), Логи (Loki / ELK), Трейсы (OpenTelemetry / Jaeger).
  • Стандарт метрик: Prometheus (Pull-модель, TSDB, сбор раз в 15–30 с через /actuator/prometheus).
  • Связка агентов:
    • Node Exporter $\to$ железо нод.
    • cAdvisor $\to$ CPU/RAM контейнеров из cgroups.
    • kube-state-metrics $\to$ статусы подов, рестарты, фазы K8s объектов.
  • Главный враг TSDB — Cardinality Explosion: категорически запрещено передавать в метрики Prometheus динамические ID (пользователи, заказы, UUID).
  • Масштабирование: локально 15–30 дней $\to$ для долгосрочного хранения в S3 используются Thanos или VictoriaMetrics.
  • Golden Signals: Latency (p95/p99), Traffic (RPS), Errors (5xx), Saturation (CPU/RAM).

Инструменты мониторинга по категориям

| Направление | Инструменты | Протокол / Язык | Назначение | | :— | :— | :— | :— | | Метрики | Prometheus, VictoriaMetrics | PromQL / Pull | Графики трендов, алертинг в реальном времени | | Логи | Grafana Loki, Fluentbit, Vector | LogQL / Push | Поиск stack trace, аудит событий и ошибок | | Трейсинг | OpenTelemetry, Jaeger, Tempo | OTLP / W3C Trace | Поиск узких мест в цепочках микросервисов |

Красные флаги на собеседовании (Чего говорить нельзя)

  • ❌ «Мы добавляем user_id в лейблы метрик Prometheus, чтобы видеть запросы каждого клиента» — гарантированный Cardinality Explosion и падение мониторинга.
  • ❌ «Prometheus собирает логи приложений из stdout контейнеров» — Prometheus работает исключительно с числовыми временными рядами; для логов нужен Loki или ELK.
  • ❌ «Мы храним метрики в стандартной базе Prometheus за последние 3 года» — стандартный TSDB не предназначен для этого; требуется Thanos или VictoriaMetrics.

Связанные вопросы