에디블로그
Engineer's Field Notes

AI 자동화로 매일
한 편씩 쓰는
엔지니어 운영 노트

Claude Code · 자동화 파이프라인 · 사고 회고까지. 잘 굴러간 기록 + 깨진 흔적도 같이 남깁니다.

사람이 할 수 있는 일은,
AI도 할 수 있어야 합니다.
매일 한 편 쓰면서 검증 중.
— 이번 주 가장 많이 읽힌 글 TOP 3
백엔드/메시징

[Kafka] 성능 트러블슈팅 가이드: 컨슈머 lag, 리밸런싱, 프로듀서 타임아웃 증상별 원인과 대응

반응형
[Kafka] 성능 트러블슈팅 가이드: 컨슈머 lag, 리밸런싱, 프로듀서 타임아웃 증상별 원인과 대응

[Kafka] 성능 트러블슈팅 가이드: 컨슈머 lag, 리밸런싱, 프로듀서 타임아웃 증상별 원인과 대응

"메시지 처리가 밀려요", "프로듀서가 가끔 타임아웃 나요" 같은 일이 생기면 카프카의 어디를 봐야 할지 막막해요. 이 글은 백엔드 개발자 입장에서, 카프카 성능 이슈가 생겼을 때 어떤 지표를 보고 어떻게 좁혀 들어가는지를 증상별로 정리해요. 깊은 내용은 시리즈의 해당 글로 연결하는 진단 허브예요.

01. 카프카 성능 트러블슈팅, 순서는 측정 → 좁히기 → 대응

증상이 무엇이든 순서는 같아요. 먼저 지표로 측정하고 패턴으로 원인을 좁혀요. 원인에 맞는 대응을 해요. 추측으로 설정부터 바꾸면 엉뚱한 데를 고쳐요.

카프카 성능 트러블슈팅 진단 순서. consumer lag, latency, rebalance 지표를 측정한 뒤 증상 패턴별로 처리량 부족, 키 쏠림, 리밸런싱 반복, 프로듀서 지연으로 좁혀 대응한다

기본으로 챙기는 지표는 이거예요. 대부분 컨슈머·프로듀서 클라이언트가 JMX로 내보내요.

  • consumer lag — 아직 처리 못 한 메시지 수. 가장 중요해요. 반드시 파티션별로, 추세로 봐요.
  • records-lag-max — 컨슈머가 보는 파티션별 최대 lag.
  • produce latency / request latency — 프로듀서가 전송하고 ack 받기까지 걸린 시간.
  • fetch latency — 컨슈머가 메시지를 가져오는 데 걸린 시간.
  • rebalance 빈도 — 컨슈머 그룹이 얼마나 자주 리밸런싱되는지.

급할 때는 CLI가 가장 빨라요. 컨슈머 그룹의 파티션별 lag을 바로 볼 수 있어요.

kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --describe --group order-service

출력에 파티션별 CURRENT-OFFSET·LOG-END-OFFSET·LAG이 나와서, 어느 파티션이 밀리는지 바로 보여요. 상시 모니터링은 스프링 부트의 Micrometer로 클라이언트 지표를 Prometheus로 내보내 Grafana에 올려요. CLI로 급한 불을 끄고 대시보드로 평소 추세를 보는 식이에요.

함정 하나. lag이 0이라고 항상 정상은 아니에요. 프로듀서가 멈춰서 들어오는 게 없어도 lag은 0이거든요. lag은 "처리가 따라가고 있나"만 말해주니, 유입량(메시지 인입 rate)을 같이 봐야 "시스템이 살아 있나"까지 확인돼요.

02. 증상: 처리가 밀려요 (consumer lag이 쌓임)

가장 흔한 증상이에요. consumer lag이 계속 커지면 유입을 처리가 못 따라가는 거예요. 이때 "전체 lag 합계"만 보면 원인을 못 갈라요. 파티션별 분포를 봐야 해요.

파티션별 consumer lag 분포 비교. 전 파티션이 고르게 밀리면 처리량 부족이고, 특정 파티션만 밀리면 키 쏠림이나 poison pill이다

전 파티션이 고르게 밀리면(패턴 A) 처리량 자체가 부족한 거예요. 이 순서로 좁혀요.

  • 처리가 느린가 — 리스너 안 DB·외부 API 호출 시간을 재봐요. 가장 흔한 원인이에요.
  • 컨슈머/파티션이 부족한가 — 트래픽이 늘었는데 그대로면 파티션을 늘리고 컨슈머를 추가해요.

반대로 특정 파티션만 밀리면(패턴 B) 양을 늘려도 안 풀려요. 두 가지를 의심해요.

  • 키 쏠림 — 특정 메시지 키에 트래픽이 몰려 한 파티션만 무거운 경우예요. 키 설계를 봐야 해요.
  • 처리가 막혔나 — 처리 불가능한 메시지(poison pill)가 재시도를 반복하며 파티션을 막는 경우예요. 카프카는 파티션 안에서 순차 처리라서, 한 건이 막히면 그 뒤 메시지가 전부 대기해요.
왜 한 건이 전체를 막을까요. 파티션은 순서가 보장되는 큐라서 컨슈머가 앞 메시지를 건너뛰고 뒤를 처리할 수 없어요. 그래서 poison pill 대응의 정석은 "재시도 횟수 제한 + DLQ로 격리"예요. 격리 없이 무한 재시도를 두면 장애가 그 파티션의 모든 후속 메시지로 번져요.

lag 지표와 진단은 컨슈머 lag 모니터링 글에서, 막힘 제거는 에러 핸들링과 DLQ 글에서 더 다뤘어요.

03. 증상: 동시성을 올렸는데 처리량이 그대로

concurrency를 키웠는데 처리량이 안 오르면, 파티션 수가 천장인 경우가 대부분이에요. 동시 처리 컨슈머 수는 파티션 수를 못 넘어요.

카프카 파티션 수와 컨슈머 동시성 관계. 파티션이 3개면 concurrency를 10으로 설정해도 컨슈머 3개만 파티션을 할당받아 처리하고 나머지 7개는 유휴 상태다

왜 그러냐면, 카프카는 한 파티션을 같은 그룹의 컨슈머 하나에만 할당해요. 파티션 안 순서를 지키기 위한 구조라서, 파티션 3개면 일감 자체가 3줄이에요. 그래서 처리량을 늘리려면 토픽 파티션부터 늘리고 그에 맞춰 컨슈머를 늘려요.

파티션 증설에도 함정이 있어요. 파티션 수는 한 번 늘리면 줄일 수 없고 늘리는 순간 키-파티션 매핑이 바뀌어서 같은 키의 메시지가 증설 전후로 다른 파티션에 들어가요. 키 순서에 의존하는 로직이 있다면 증설 타이밍을 신중하게 잡아야 해요.

자세한 동시성·파티션 관계는 @KafkaListener 설정파티셔닝 글에 있어요.

04. 증상: 컨슈머가 자꾸 멈췄다 가요 (리밸런싱 반복)

처리가 주기적으로 끊기고 로그에 "leaving group"이 보이면 리밸런싱이 반복되는 거예요. 흔한 패턴이 처리 지연이 만드는 악순환이에요.

카프카 리밸런싱 악순환. poll로 가져온 묶음 처리가 max.poll.interval.ms를 초과하면 그룹에서 추방되어 리밸런싱이 발생하고, 파티션 재할당과 재처리로 같은 일이 반복된다

한 번 poll로 가져온 묶음을 max.poll.interval.ms 안에 다 처리하고 다음 poll로 돌아와야 해요. 못 돌아오면 카프카는 "이 컨슈머가 죽었다"고 보고 추방해요. 그런데 추방돼도 처리하던 일은 같으니, 재할당 후 또 초과하고 또 추방되는 루프가 돌아요. max.poll.records를 줄여 한 묶음을 가볍게 만드는 게 첫 번째 대응이에요.

구분할 함정. 추방 기준이 두 개라 헷갈려요. session.timeout.ms는 하트비트(생존 신호)가 끊겼을 때고, max.poll.interval.ms는 처리가 느릴 때예요. 하트비트는 별도 스레드가 보내니, 애플리케이션이 처리에 묶여 있어도 하트비트는 멀쩡할 수 있어요. "프로세스는 살아 있는데 추방됐다"면 거의 max.poll.interval.ms 쪽이에요.

다른 원인은 배포예요. 인스턴스가 들고 날 때마다 리밸런싱이 일어나니, static membership과 CooperativeStickyAssignor로 줄여요. 자세한 건 리밸런싱 글에 있어요.

05. 증상: 프로듀서가 느리거나 타임아웃 나요

프로듀서 쪽 지연은 produce latency를 봐요. send가 ack를 받기까지 거치는 단계 중 어디서 느려지는지로 좁혀요.

카프카 프로듀서 send 경로. send 호출 후 buffer.memory 버퍼 적재, linger.ms와 batch.size 배치 대기, 브로커 전송, acks=all 복제 대기를 거쳐 ack를 받으며 단계별로 지연 지점이 다르다
  • acks=all + 복제 지연 — 동기화된 복제본(ISR) 전체를 기다리니, 복제가 밀리면 느려져요. 클러스터 상태를 봐요.
  • 버퍼 가득 참buffer.memory가 차면 send가 블록돼요. 브로커가 느리거나 유입이 버퍼보다 빠른 거예요.
  • 배치가 비효율적linger.ms·batch.size로 배치를 키우고 압축을 켜면 처리량이 올라가요.

안전성(acks)과 처리량(배치·압축)의 균형은 프로듀서 acks와 순서 보장 글에서 다뤘어요. 유실이 안 되는 데이터면 acks를 낮춰 속도를 올리는 건 답이 아니에요.

한 가지 더. send 실패를 처리하는 콜백을 비워두면 최종 실패한 메시지가 조용히 사라져요. "성능"이 아니라 "유실"로 나타나는 거예요. 느린 것보다 모르게 새는 게 더 위험하니, latency가 튈 땐 실패 처리도 같이 점검해요.

06. 증상: 메시지가 중복 처리돼요

이건 성능이라기보다 정합성 이슈인데, lag·리밸런싱과 얽혀 같이 나타나요. at-least-once에서는 처리 후 커밋 사이 크래시나 리밸런싱 재할당으로 중복이 구조적으로 생겨요. 04의 악순환이 돌고 있다면 중복도 같이 늘고 있을 가능성이 높아요.

그래서 "중복을 0으로 만들기"보다 "두 번 처리해도 같게" 멱등성으로 흡수하는 게 정석이에요. 자세한 건 수동 커밋과 멱등성 글에 있어요.

증상별 진단 요약

카프카 성능 이슈는 lag·latency·rebalance 지표로 측정하고 증상 패턴으로 원인을 좁혀 대응하는 게 골격이에요. 추측으로 설정부터 만지지 말고 지표부터 보는 게 핵심이고 증상별로는 이렇게 갈라봐요.

  • 전 파티션이 고르게 밀리면 → 처리량·스케일(파티션·컨슈머)
  • 특정 파티션만 밀리면 → 키 쏠림·poison pill
  • 주기적으로 멈췄다 가면 → 리밸런싱
  • 프로듀서가 느리거나 타임아웃 나면 → acks·버퍼·배치

이렇게 증상 관점에서 묶은 진단 허브라, 깊은 내용은 @KafkaListener 설정·리밸런싱·프로듀서 acks·lag 모니터링 글로 들어가요.

출처: Kafka — Monitoring · Spring for Apache Kafka — Micrometer

반응형

📚 같이 보면 좋은

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 일정액의 수수료를 제공받습니다."