LLM WikiAccess-protected knowledge portal
← 스터디 홈
7편 · 약 24분

스트리밍 관측성: lag, watermark, throughput, backpressure, end-to-end latency

스트리밍 장애는 “느려졌다”가 아니라 “어디에서 시간이 쌓이는가”로 봐야 한다

배치 파이프라인은 보통 실행 단위가 분명하다. 특정 DAG run이 실패했는지, 어느 task가 오래 걸렸는지, 어느 partition publish가 누락됐는지를 추적하면 된다. 스트리밍 파이프라인은 다르다. job은 계속 살아 있고, 일부 partition만 밀리거나, watermark만 멈추거나, sink만 느려져 upstream이 backpressure를 받는 식으로 부분적인 지연이 먼저 나타난다.

그래서 스트리밍 관측성은 단일 지표 하나로 끝나지 않는다. consumer lag는 Kafka consumer가 뒤처졌다는 사실은 잘 보여주지만, 왜 뒤처졌는지는 말해 주지 않는다. watermark는 event time 진행을 보여 주지만, 처리량이나 sink 병목을 직접 설명하지 않는다. backpressure는 downstream 병목의 신호지만, source lag와 checkpoint 지연을 함께 봐야 사고 범위가 보인다.

운영자는 다음 다섯 축을 함께 봐야 한다.

  1. lag: source에 처리되지 않은 backlog가 쌓이는가?
  2. watermark: event time이 정상적으로 앞으로 움직이는가?
  3. throughput: 입력·출력 record/byte rate가 어느 operator에서 떨어지는가?
  4. backpressure: downstream 병목이 upstream으로 전파되는가?
  5. end-to-end latency: 이벤트가 발생한 뒤 최종 sink에 반영되기까지 얼마나 걸리는가?
Producer
event_time 기록
Kafka topic
offset / retention
Flink source
watermark 생성
Operator chain
state / window / join
Sink
DB / lakehouse / API
ingest timestamp
consumer lag
log end - committed
currentInputWatermark
numRecordsIn/Out
busy / idle / backpressured
commit latency
sink error rate
End-to-end latency = sink 반영 시각 - event 발생 시각. 중간 구간별 timestamp를 남겨야 원인을 분해할 수 있다.
스트리밍 지연을 분해하는 관측성 지도

1. Lag: backlog의 크기와 방향을 따로 본다

Kafka 기반 스트리밍에서 가장 먼저 보는 지표는 대개 consumer lag다. Confluent 문서는 consumer lag를 “partition의 최신 produced offset과 consumer가 마지막으로 consumed/committed한 offset 사이의 차이”로 설명한다. 즉 lag는 아직 consumer group이 처리했다고 인정하지 않은 record 수다.

하지만 lag를 숫자 하나로만 보면 오판하기 쉽다.

질문봐야 하는 이유해석
lag가 증가 중인가, 안정적인가, 감소 중인가?backlog가 쌓이는지 catch-up 중인지 구분증가: 처리 능력 부족 또는 장애, 안정: steady backlog, 감소: 복구 중
특정 partition만 높은가?key skew 또는 partition assignment 문제 확인한 partition만 증가하면 parallelism을 늘려도 해결이 제한적일 수 있음
lag가 retention 시간 안에 소화 가능한가?데이터 유실 위험 판단lag time이 retention을 넘으면 unprocessed record가 삭제될 수 있음
rebalance 중인가?lag 수집값이 멈추거나 튈 수 있음Confluent Cloud Metrics API는 rebalance 중 lag 값 갱신 방식에 주의 필요
offset lag인가, 시간 지연인가?record 수와 사용자 체감 지연은 다름작은 record 10만 개와 큰 record 10만 개의 처리 시간은 다름

운영 대시보드에는 최소한 다음을 분리해 둔다.

  • records-lag-max 또는 group/topic/partition 단위 lag
  • partition별 log end offset, committed offset
  • consumer rebalance count 또는 pending rebalance time
  • input records per second와 output records per second
  • topic retention 대비 “현재 lag를 처리하는 데 걸리는 예상 시간”

특히 Confluent 문서가 지적하듯, offset lag emitter는 offset 차이를 제공할 뿐 “마지막 record가 fetch된 뒤 지난 시간”까지 자동으로 설명하지 않는다. 그래서 “lag 50,000”이라는 값만으로는 SLA 위반 여부를 판단할 수 없다. 초당 100,000건을 안정적으로 처리하는 consumer에게 50,000건은 짧은 backlog일 수 있지만, 초당 100건 처리하는 결제 보정 consumer에게는 심각한 지연이다.

간단한 runbook은 다음처럼 잡을 수 있다.

  1. group 전체 lag가 증가하는지 확인한다.
  2. partition별 lag 분포를 본다.
  3. consumer instance 수와 partition 수, assignment를 확인한다.
  4. rebalance가 반복되는지 본다.
  5. consumer 처리 시간, 외부 I/O, sink error/retry를 본다.
  6. retention 전에 backlog를 소화할 수 없으면 scale-out, rate limit, DLQ, replay window 조정 같은 완화책을 먼저 적용한다.

2. Watermark: event time이 멈추면 window 결과도 멈춘다

Flink에서 event time은 wall clock이 아니라 이벤트 안에 들어 있는 발생 시각을 기준으로 한다. Flink 문서는 watermark를 “해당 stream의 event time이 시각 t까지 도달했으며, timestamp가 t 이하인 더 이상한 record는 오지 않을 것이라는 선언”으로 설명한다. window, event-time timer, late data 판단은 이 watermark 진행에 의존한다.

운영 관점에서 watermark는 “정확한 시간 처리의 진행률”이다. source throughput이 있어도 watermark가 멈추면 event-time window가 닫히지 않는다. 반대로 watermark는 잘 움직이는데 lag가 크다면, job은 event-time 기준으로는 진행하지만 source backlog를 따라잡지 못하는 상태일 수 있다.

watermark 문제는 보통 다음 패턴으로 나타난다.

증상가능한 원인확인 방법
currentInputWatermark가 오래 멈춤특정 partition/source가 idle, timestamp 추출 오류, out-of-order 허용 폭 과다source별 watermark, partition별 input rate, idleness 설정 확인
window 결과가 늦게 나옴watermark가 window end를 넘지 못함window operator의 input watermark와 late record count 확인
특정 operator 뒤에서 watermark가 느려짐operator backpressure, slow join/window, skewoperator별 input/output watermark와 busy/backpressured time 비교
replay 후 watermark가 예상과 다름오래된 event time record를 빠르게 재처리replay 구간의 event timestamp 분포 확인

Flink의 WatermarkStrategy는 source에 직접 적용하는 것이 선호된다. Kafka source처럼 partition을 이해하는 connector는 partition/split 단위로 watermark를 더 정확히 추적할 수 있기 때문이다. 후단 operator에서 일괄로 timestamp/watermark를 다시 붙이면 source의 세부 상태를 잃고, 이미 있던 timestamp/watermark를 덮어쓸 수도 있다.

실무에서는 watermark 알림을 단순히 “현재 시각과 watermark 차이가 5분 초과” 같은 절대값으로만 두지 않는다. event-time job의 허용 지연이 원래 10분인지 1시간인지, batch replay 중인지, 특정 source가 밤에는 정상적으로 idle한지에 따라 기준이 다르다. 그래서 job별로 다음 값을 명시한다.

  • 정상 입력 구간에서 기대하는 watermark delay
  • idle source가 있을 때 withIdleness 같은 설정을 쓸지 여부
  • late data를 drop할지, side output/DLQ로 보낼지
  • replay 중 watermark alert를 어떻게 mute하거나 별도 해석할지

3. Throughput: 어디서 record rate가 줄어드는지 본다

throughput은 입력·출력 record 또는 byte rate다. Flink metric에는 numRecordsIn, numRecordsOut, numRecordsInPerSecond, numRecordsOutPerSecond 같은 IO 계열 지표가 있고, Kafka consumer/producer와 sink client도 각자의 fetch/commit/write 지표를 노출한다.

throughput은 lag와 함께 볼 때 의미가 생긴다.

lagthroughput상태 해석
증가input은 높고 output이 낮음처리 병목 또는 sink 병목
증가input도 낮음source fetch 문제, rebalance, broker/network 문제 가능
안정input과 output이 비슷함steady state. 단, latency SLA는 별도 확인 필요
감소output이 input보다 높음catch-up 중
0 근처output도 0정상 idle일 수도 있고 source 장애일 수도 있음

operator별 throughput을 보면 병목 위치를 좁힐 수 있다. 예를 들어 source의 numRecordsOutPerSecond는 높은데 enrichment operator의 numRecordsOutPerSecond가 낮고 busy time이 높다면 CPU/serialization/state 접근 병목을 의심한다. enrichment operator는 빠른데 sink 직전부터 backpressure가 높다면 외부 DB write, object storage commit, API rate limit을 의심한다.

throughput 대시보드에는 평균뿐 아니라 분포와 불균형도 필요하다. parallel subtask 32개 중 하나만 입력이 몰리면 전체 평균은 괜찮아 보여도 해당 key group이 SLA를 깨뜨릴 수 있다. 따라서 다음을 함께 둔다.

  • operator/subtask별 records in/out rate
  • bytes in/out rate
  • busy/idle/backpressured time
  • key별 또는 partition별 skew 상위 N개
  • sink batch size, retry count, commit duration

4. Backpressure: 느린 곳은 보통 경고가 뜬 곳보다 downstream에 있다

Flink 문서는 backpressure를 downstream operator가 upstream보다 느리게 소비할 때 발생하는 현상으로 설명한다. record는 source에서 sink 방향으로 흐르지만, backpressure는 반대 방향으로 전파된다. 그래서 source에 High backpressure가 보인다고 해서 source가 원인이라고 단정하면 안 된다. 실제 병목은 downstream sink 또는 중간 operator일 수 있다.

Flink의 각 subtask는 세 가지 시간을 노출한다.

지표의미운영 해석
backPressuredTimeMsPerSecondoutput buffer 부족 등으로 backpressure를 받은 시간downstream이 못 받아 주는 신호
idleTimeMsPerSecond처리할 input을 기다린 시간upstream 입력 부족, source idle, skew 가능성
busyTimeMsPerSecond실제 처리로 바빴던 시간CPU/serialization/state/I/O 작업량

이 세 값은 대략 1000ms로 합산된다. 단, 몇 초 단위 평균이므로 순간 spike를 그대로 보여 주지는 않는다. Flink Web UI는 subtask의 backpressure/busy 최대값을 JobGraph에 표시하고, 상태를 OK, LOW, HIGH로 분류한다. HIGH는 backpressured 시간이 50%를 초과하는 상태로 볼 수 있다.

backpressure 조사 순서는 downstream에서 upstream 방향으로 거슬러 올라가는 편이 안전하다.

  1. sink write latency, error, retry, external quota를 본다.
  2. sink 직전 operator의 backpressured time을 본다.
  3. 한 단계 upstream operator의 busy time과 output rate를 본다.
  4. 특정 subtask만 busy/backpressured인지 확인해 skew를 찾는다.
  5. checkpoint alignment time과 start delay가 함께 증가하는지 본다.

마지막 항목이 중요하다. Flink 문서는 heavy backpressure 상태에서 checkpoint barrier가 모든 operator/subtask로 전파되는 시간이 checkpoint duration의 지배 요인이 될 수 있다고 설명한다. 이때 high alignment time이나 start delay가 나타날 수 있다. 즉 backpressure는 단순히 latency만 늘리는 문제가 아니라, 장애 복구 기준점인 checkpoint 안정성까지 흔들 수 있다.

완화책은 원인에 따라 다르다.

  • sink가 느리면 batch size, connection pool, async I/O, write parallelism, 외부 시스템 capacity를 본다.
  • CPU가 부족하면 operator chain, serialization, UDF 비용, parallelism을 본다.
  • skew가 있으면 key 설계, salting, partitioning, hot key 분리 전략을 본다.
  • checkpoint가 backpressure에 막히면 병목 제거가 우선이고, 필요하면 buffer debloating 또는 unaligned checkpoint를 검토한다. 단 unaligned checkpoint는 in-flight data를 checkpoint state에 포함하므로 state storage I/O 비용이 늘 수 있다.

5. End-to-end latency: 사용자에게 보이는 지연은 offset 하나로 설명되지 않는다

end-to-end latency는 이벤트가 실제로 발생한 순간부터 최종 목적지에 관측 가능해지는 순간까지의 시간이다. 이 값은 Kafka lag, Flink 처리 시간, sink commit latency, downstream serving delay를 모두 포함한다.

가장 흔한 실수는 consumer lag를 end-to-end latency처럼 해석하는 것이다. lag는 “남은 record 수”이고 latency는 “시간”이다. 둘은 관련 있지만 같지 않다. record 크기, 처리 비용, batch flush 주기, sink transaction 주기, watermark delay에 따라 같은 lag도 전혀 다른 사용자 지연을 만든다.

따라서 event schema나 metadata에 최소 세 종류의 timestamp를 남기는 것이 좋다.

timestamp기록 위치목적
event_timeproducer 또는 원천 시스템비즈니스 발생 시각, event-time window 기준
ingest_timeKafka publish 또는 ingestion gatewaysource 도착 지연 분리
processed_time / sink_commit_timeFlink sink 또는 downstream writer처리·반영 지연 계산

이렇게 하면 다음처럼 구간별 지연을 나눌 수 있다.

ingest_delay      = ingest_time - event_time
processing_delay  = sink_commit_time - ingest_time
e2e_latency       = sink_commit_time - event_time
watermark_delay   = processing_clock_now - currentInputWatermark

Prometheus 같은 시계열 시스템에서는 latency를 평균만으로 보지 말고 histogram으로 수집하는 편이 좋다. Prometheus 문서는 distribution metric에서 histogram과 summary의 차이를 설명하면서, 가능하면 native histogram을 선호하라고 안내한다. 운영 대시보드에서는 p50보다 p95/p99가 더 중요할 때가 많다. stream job은 대부분 정상이어도 특정 partition, hot key, 외부 API retry 때문에 tail latency가 SLA를 깨는 경우가 흔하기 때문이다.


6. 알림은 “증상 지표”와 “원인 지표”를 묶어야 한다

좋은 alert는 “lag가 큼”에서 끝나지 않고, 운영자가 다음 행동을 바로 선택하게 해 준다. 예시는 다음과 같다.

Alert조건 예시같이 붙일 context
Source backlog 증가group lag가 10분 연속 증가partition별 lag, input/output rate, rebalance 여부
Watermark stalledwatermark delay가 job별 허용치 초과source partition idle 여부, late record rate, window output rate
Chronic backpressure특정 operator backPressuredTimeMsPerSecond가 지속적으로 높음downstream sink latency, busy/idle time, subtask skew
Checkpoint degradedcheckpoint duration/start delay/alignment time 급증backpressure 상태, checkpoint size, storage error
E2E latency SLA 위반p95/p99 latency가 SLO 초과lag, watermark delay, sink commit latency, retry/error rate

알림 임계값은 절대값보다 “추세 + SLO + 복구 여유”로 잡는 것이 낫다. 예를 들어 retention이 24시간인 topic에서 현재 처리 속도로 backlog 소화 예상 시간이 20시간이면, 아직 데이터가 삭제되지는 않았어도 이미 위험하다. 반대로 야간에 source가 정상적으로 idle한 job에서 output rate 0은 장애가 아닐 수 있다.

실무 runbook은 다음 문장으로 시작하면 좋다.

“이 alert는 사용자 지연, 데이터 유실 위험, 복구 가능성 중 무엇을 위협하는가?”

그 답에 따라 조치 순서가 달라진다.

  • 사용자 지연이면 e2e latency와 sink 반영 경로를 먼저 본다.
  • 데이터 유실 위험이면 lag vs retention과 replay 가능성을 먼저 본다.
  • 복구 가능성이면 checkpoint 성공 여부와 state/checkpoint storage를 먼저 본다.

7. 운영 대시보드 구성 예시

스트리밍 플랫폼 공통 대시보드는 계층별로 나누는 편이 읽기 쉽다.

7.1 Kafka/source layer

  • topic별 produce rate, byte rate
  • consumer group lag total/max, partition별 lag 상위 N개
  • rebalance count/pending rebalance time
  • broker fetch latency, network error, throttle
  • retention 대비 backlog 소화 예상 시간

7.2 Flink job layer

  • job uptime, restart count, failed task count
  • operator별 numRecordsInPerSecond, numRecordsOutPerSecond
  • subtask별 busy/idle/backpressured time
  • currentInputWatermark, currentOutputWatermark, watermark delay
  • checkpoint duration, failed checkpoint, alignment time, start delay, checkpoint size
  • state size, RocksDB/local disk 지표가 있는 경우 함께 표시

7.3 Sink/downstream layer

  • write throughput, commit latency, error/retry rate
  • DB connection pool, lock wait, write conflict
  • object storage request latency/throttle
  • external API rate limit, timeout
  • downstream table/file freshness

7.4 Business/SLO layer

  • p50/p95/p99 end-to-end latency
  • late event count/drop count
  • window output freshness
  • DLQ count와 poison message 상위 원인
  • 중요 consumer별 SLA burn rate

대시보드의 핵심은 한 화면에 모든 숫자를 넣는 것이 아니다. “lag가 증가했다 → watermark도 멈췄는가? → backpressure는 어느 operator부터 시작되는가? → checkpoint도 위험한가?”처럼 사고 흐름을 따라가게 만드는 것이다.


8. 장애 조사 예시: lag와 backpressure가 동시에 증가한다

상황을 하나 가정해 보자. 주문 이벤트를 Kafka에서 읽어 Flink로 집계한 뒤 PostgreSQL에 upsert하는 job에서 consumer lag가 30분 동안 증가하고, source operator에 backpressure HIGH가 표시된다.

나쁜 접근은 “source가 backpressure니까 Kafka consumer parallelism을 늘리자”로 바로 가는 것이다. 더 안전한 순서는 다음과 같다.

  1. partition별 lag를 본다. 모든 partition이 비슷하게 증가하면 전체 처리 능력 부족이고, 일부만 증가하면 skew다.
  2. operator별 backpressure를 sink 쪽부터 본다. source가 아니라 sink 직전 operator가 먼저 막혔는지 확인한다.
  3. sink commit latency와 DB 지표를 본다. lock wait, connection pool 고갈, slow query, WAL/disk I/O를 확인한다.
  4. Flink busy time을 본다. sink는 idle인데 중간 집계 operator가 busy라면 state access나 hot key 문제다.
  5. checkpoint duration/alignment time을 본다. checkpoint가 실패하기 시작하면 장애 복구 위험도 같이 상승한다.
  6. 완화책을 고른다. DB write 병목이면 write batch 조정, 임시 rate limit, sink parallelism 조정, hot key 분산, downstream scale-up 중 하나를 선택한다.

이 과정을 통해 “Kafka lag”라는 표면 증상을 “DB sink commit latency 증가 → downstream backpressure → source fetch 둔화 → lag 증가 → checkpoint alignment 지연”이라는 경로로 설명할 수 있다. 그래야 재발방지도 “consumer 수를 늘렸다”가 아니라 “sink write path의 capacity와 alert를 보강했다”로 이어진다.


마무리: 스트리밍 관측성은 지표 목록이 아니라 인과관계 지도다

스트리밍 운영에서 중요한 것은 많은 metric을 수집하는 것이 아니라, metric 사이의 인과관계를 읽는 것이다.

  • lag는 backlog를 말한다.
  • watermark는 event time 진행을 말한다.
  • throughput은 처리량과 병목 위치를 말한다.
  • backpressure는 downstream 병목의 전파를 말한다.
  • end-to-end latency는 사용자가 실제로 겪는 지연을 말한다.

이 다섯 가지를 한 번에 보면 “지금 느린가?”보다 더 중요한 질문에 답할 수 있다. 어디서 시간이 쌓이고, 얼마나 오래 버틸 수 있으며, 장애가 나도 어디까지 복구할 수 있는가? 이것이 스트리밍 데이터 플랫폼 운영자가 대시보드를 설계할 때 가져야 할 기본 관점이다.

References

  • Apache Flink Docs, “Timely Stream Processing”: https://nightlies.apache.org/flink/flink-docs-stable/docs/concepts/time/
  • Apache Flink Docs, “Generating Watermarks”: https://nightlies.apache.org/flink/flink-docs-stable/docs/dev/datastream/event-time/generating_watermarks/
  • Apache Flink Docs, “Metrics”: https://nightlies.apache.org/flink/flink-docs-stable/docs/ops/metrics/
  • Apache Flink Docs, “Monitoring Back Pressure”: https://nightlies.apache.org/flink/flink-docs-stable/docs/ops/monitoring/back_pressure/
  • Apache Flink Docs, “Checkpointing under Backpressure”: https://nightlies.apache.org/flink/flink-docs-stable/docs/ops/state/checkpointing_under_backpressure/
  • Confluent Documentation, “Monitor Consumer Lag”: https://docs.confluent.io/platform/current/monitor/monitor-consumer-lag.html
  • Confluent Documentation, “Monitor Kafka Consumer Lag in Confluent Cloud”: https://docs.confluent.io/cloud/current/monitoring/monitor-lag.html
  • Prometheus Docs, “Histograms and summaries”: https://prometheus.io/docs/practices/histograms/