🐳 Розділ 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.

Пов’язані питання