Observability checklist для production Flink
В предыдущем модуле мы видели, как включить Prometheus и какие метрики ключевые. Этот урок — про систему наблюдения как чек-лист: что должно быть настроено для каждого production Flink-джоба ДО того, как он попадёт в боевой контур. Без этого вы будете узнавать о проблемах от пользователей в Slack, а не от Prometheus alert-а.
Зачем нужен полноценный observability stack
В production Flink-джоб умирает не “потому что код плохой” — а потому что:
- Что-то медленно растёт (state, lag) и достигает порога.
- Что-то внешнее (Kafka, S3, downstream sink) на минуту замедляется.
- Deploy внёс регрессию, которая стала видна не сразу.
Без observability вы видите проблему постфактум, когда она уже стала критичной. С observability — заранее, ещё в фазе деградации, есть время отреагировать.
Полный observability stack для Flink покрывает 4 слоя:
Kubernetes observability: Prometheus operator, ServiceMonitor, GrafanaДля большинства команд достаточно слоёв 1, 2 и 4 — Prometheus, Loki, alerts. Tracing — для зрелых стэков с микросервисной архитектурой.
Чек-лист: метрики, которые ДОЛЖНЫ быть на dashboard
Каждый production-джоб должен иметь dashboard, который за 30 секунд отвечает на вопрос: “всё ок или есть проблема?”.
Группа 1: Job health (наверху dashboard-а)
| Метрика | Что показывает | Алерт |
|---|---|---|
flink_jobmanager_job_uptime | Сколько секунд джоб running | < 60s для 1 мин = critical |
flink_jobmanager_job_numRestarts | Counter рестартов | rate > 0 за 10 мин = warning |
flink_jobmanager_numRegisteredTaskManagers | Сколько TM подключено | < expected = warning |
Visualize: stat-панели с current value, цвет (зелёный/жёлтый/красный).
Группа 2: Checkpoint health
| Метрика | Что показывает | Алерт |
|---|---|---|
flink_jobmanager_job_lastCheckpointDuration | мс на последний checkpoint | > 60s = warning |
flink_jobmanager_job_lastCheckpointSize | байты | growing > 1MB/час = warning |
flink_jobmanager_job_numFailedCheckpoints | counter упавших | rate > 0 = critical |
flink_jobmanager_job_lastCheckpointRestoreTimestamp | timestamp последнего successful | для подсчёта lag между checkpoints |
Visualize: time-series панели за последние 1h, 6h, 24h. Полезно overlay графика fail timestamps как vertical lines.
Группа 3: Backpressure heatmap
flink_taskmanager_job_task_backPressuredTimeMsPerSecond
Группировка по task_name. Visualize как heatmap: rows = операторы, columns = time, color = backpressure level. Сразу видно, какой оператор задыхается.
Алерт: > 500 ms/s на любом операторе 10+ минут.
Группа 4: Source lag (Kafka)
# Внешний экспортер kafka-exporter
kafka_consumergroup_lag{consumergroup=~"flink-.*"}
# Или встроенная Flink-метрика для KafkaSource
flink_taskmanager_job_task_operator_currentEmitEventTimeLag
Visualize: time-series, по partition. Если одна partition лагает сильно больше других — это data skew, не общая проблема.
Группа 5: Throughput
sum by (task_name) (
flink_taskmanager_job_task_numRecordsOutPerSecond
)
Visualize: time-series per operator. Должно быть стабильно при стабильной нагрузке. Резкий drop — что-то изменилось.
Группа 6: Resource usage
# JM/TM heap usage
flink_jobmanager_Status_JVM_Memory_Heap_Used
flink_taskmanager_Status_JVM_Memory_Heap_Used
# CPU per TM container
container_cpu_usage_seconds_total{pod=~".*taskmanager.*"}
Если heap > 80% постоянно — пора увеличивать ресурсы.
Логи: структурированный JSON, не stack-trace dump
По умолчанию Flink логирует в plain text через log4j. В production переключите на JSON через log4j2 layout:
<!-- log4j2.xml в conf/ -->
<Configuration>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<JsonLayout complete="false" compact="true" eventEol="true">
<KeyValuePair key="service" value="flink" />
<KeyValuePair key="job" value="${env:JOB_NAME}" />
</JsonLayout>
</Console>
</Appenders>
<Loggers>
<Logger name="org.apache.flink" level="INFO" />
<Root level="WARN">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
После этого каждая строка лога — JSON: {"timestamp":...,"level":"INFO","logger":"...","message":"...","service":"flink","job":"orders-pipeline"}.
Loki/ELK подхватывают такой формат, и можно искать:
{service="flink", job="orders-pipeline"} |= "checkpoint" | json | level="WARN"
Что обязательно логировать в WARN/ERROR
- Checkpoint failures — Flink сам логирует, но добавьте контекст: какой job, какой operator failed.
- Source/sink errors — Kafka connection lost, S3 throttle, JDBC timeout.
- Watermark idle warnings — Flink логирует, когда source долго без watermark.
- Custom business errors — например, неожиданная схема события, превышение лимита.
Что НЕ логировать
- Каждое событие (миллион записей/сек × лог = терабайты).
- Здоровые checkpoint завершения (это метрика).
- Heartbeat-ы.
Логи — для аномалий. Метрики — для нормы.
Alerts: SLO-based, не симптом-based
Самая частая ошибка в alerting — алертить на каждый симптом. Backpressure высокий -> алерт. State growth -> алерт. Checkpoint duration высокий -> алерт. Через неделю в Slack 100 алертов в день, никто их не смотрит.
Лучший подход — SLO-based alerting. Определите Service Level Objective для каждого джоба (например, “99% времени lag меньше 5 минут”). Алертите только когда SLO нарушается.
# SLO: 99% времени Kafka lag должен быть < 1M записей
# Алерт если за 1 час реально SLO < 99%
- alert: FlinkOrdersSLOBreach
expr: |
(
sum_over_time(
(kafka_consumergroup_lag{consumergroup="flink-orders"} < 1000000)[1h:1m]
)
/
60 # минут в часе
) < 0.99
for: 0m
labels:
severity: critical
annotations:
summary: "Flink orders pipeline breached SLO (lag < 1M for 99% of time)"
Симптомы (high backpressure, growing state) — это warning в чате команды, не paging. Они интересны для дебага, но не для будильника.
Минимальный набор production alerts
# Critical (пейджит дежурного):
- FlinkJobNotRunning (uptime == 0 for 1m)
- FlinkSLOBreach (lag > threshold > 99% времени за час)
- FlinkCheckpointsFailing (rate of fails > 0 for 2m)
- FlinkTaskManagerOOM (OOMKilled containers)
# Warning (ticket в очередь):
- FlinkCheckpointDurationHigh (> 60s for 5m)
- FlinkBackpressureSustained (> 500ms/s for 30m)
- FlinkStateSizeGrowing (>1MB/час за 24h)
- FlinkRestartLoop (> 3 restarts in 10m)
- FlinkKafkaLagGrowing (rate of lag growth > 0 for 1h)
Critical = немедленная реакция. Warning = посмотреть на следующий рабочий день.
Runbook: что делать при каждом алерте
Алерт без runbook бесполезен. К каждому alert rule должна быть привязана внутренняя страница (Confluence / Notion / GitOps) с инструкцией:
# FlinkCheckpointsFailing
## Что это значит
Checkpoint-ы джоба <name> падают. Без работающих checkpoint-ов потеря recovery garanties.
## Шаги диагностики
1. Открыть Flink Web UI: <url>
2. Tab "Checkpoints" -> найти последний failed
3. Кликнуть для деталей -> видно операторы и причину
4. Самые частые причины:
- S3 throttling -> "TimeoutException" в trace. Решение: уменьшить frequency или увеличить s3.upload.max.concurrent.uploads
- Backpressure -> "Checkpoint declined" в operator. Решение: включить unaligned checkpoints
- OOM TaskManager -> "Connection lost" в trace. Решение: увеличить memory TM
5. kubectl logs jobmanager-pod-name | grep -i checkpoint | tail -50
## Когда эскалировать
- Failure rate > 50% за 30 минут -> page on-call (sev1)
- Otherwise -> внутренняя коммуникация в #flink-team, разбираться workday
Команды без runbook теряют часы при каждом алерте — каждый раз разбираясь с нуля.
Dashboards: 3 уровня
Делайте 3 уровня dashboards:
Уровень 1: Executive overview (для PM / SRE manager-ов)
- Список всех джобов
- Цветной статус (зелёный/жёлтый/красный) по SLO
- Total throughput всех джобов
- Total state size в кластере
Уровень 2: Job dashboard (для on-call инженера)
- 6 групп метрик из чек-листа выше
- Текущие алерты для этого джоба
- Last 24h trend
Уровень 3: Deep dive (для дебага)
- Per-operator metrics
- RocksDB internals (если используется)
- JVM GC, heap detail
- Network I/O per TM
Большинство времени инженер смотрит на уровень 2. Уровень 3 — только при инциденте, поэтому может быть менее polished.
Tracing: когда нужно (опционально)
Для большинства Flink-джобов tracing избыточен. Метрики и логи покрывают 95% дебаг-нужд. Tracing полезен если:
- У вас множество источников (Kafka + REST API + DB lookup), и нужно понять, где задержка в каждом конкретном event.
- Downstream системы тоже трэйсятся, и нужна end-to-end видимость.
- Бизнес-критичные транзакции с SLA-требованием по latency.
Setup: OpenTelemetry Java agent в Flink-образе, экспорт в Jaeger / Tempo. Trace context propagation через Kafka headers и REST headers.
# В Dockerfile Flink
ENV JAVA_TOOL_OPTIONS="-javaagent:/opt/opentelemetry-javaagent.jar"
ENV OTEL_SERVICE_NAME=flink-orders
ENV OTEL_EXPORTER_OTLP_ENDPOINT=http://tempo:4318
Это не маленький проект. Делайте только если есть конкретная боль, которую tracing решит.
Чек-лист перед деплоем в production
Конкретный чек-лист, который нужно пройти ДО первого production-deploy джоба:
- PrometheusServiceMonitor / scrape config в кластере подхватывает метрики Flink.
- Dashboard в Grafana создан (хотя бы из 14911 template).
- PrometheusRule с 8 базовыми alerts задеплоен.
- Алерты идут в правильный канал (Slack для warnings, PagerDuty для critical).
- Runbook для каждого алерта написан и доступен через annotation
runbook_url. - Логи структурированы в JSON, агрегируются в Loki/ELK.
- SLO определён, alert на breach настроен.
- dashboard URL в README репозитория джоба.
- on-call rotation знает про этот джоб (added to PagerDuty service).
Без этого чек-листа джоб запущенный в проде — это запуск без приборной панели.
Ключевые выводы
-
Observability — 4 слоя: metrics (Prometheus), logs (Loki), alerts, опционально traces. Для большинства Flink-команд хватает первых трёх.
-
6 групп метрик на dashboard: job health, checkpoint, backpressure (heatmap), source lag, throughput, resources.
-
Логи — JSON через log4j2 layout, агрегируются в Loki. Логировать аномалии, не нормальную работу.
-
SLO-based alerts вместо симптом-based: алертить когда нарушается бизнес-обещание, не каждый раз когда что-то отклонилось.
-
Минимум 4 critical + 5 warning alerts покрывает 90% production-нужд.
-
Runbook для каждого алерта — без него инженер заново разбирается каждый раз.
-
3 уровня dashboard-ов: executive overview, job dashboard, deep dive. On-call смотрит на уровень 2 чаще всего.
-
Чек-лист перед production-деплоем обеспечивает, что джоб не уйдёт в прод без приборной панели.