본문 바로가기
Kafka

Kafka Consumer Group

by Rainbound-IT 2025. 9. 29.
반응형

 

카프카에서 “컨슈머 그룹(Consumer Group)”은 확장성·중복제어·장애복구를 한 번에 해결해 주는 핵심 개념이에요. 그냥 “컨슈머 여러 대”로도 읽을 순 있지만, 그룹이 있어야 운영이 쉬워집니다. 이 글은 왜 그룹이 필요한지 → 내부가 어떻게 돌아가는지 → 실습/운영 팁까지 한 번에 정리합니다.


1) 한 문장 요약

  • 같은 그룹 안에서는 한 시점에 한 파티션은 한 컨슈머만 읽는다 → 중복 소비 없음 + 병렬 확장 + 자동 장애복구
  • 다른 그룹이면 독립 구독(팬아웃) → 같은 메시지를 각 그룹이 의도적으로 각각 읽는다

2) 왜 Consumer “Group” 이 필요한가

A. 병렬 처리로 처리량 확장

  • 토픽을 파티션으로 나누고, 그 파티션들을 그룹 내 여러 컨슈머가 나눠 맡아 읽음
  • 컨슈머 수를 늘리면 병렬 처리량↑ (단, 컨슈머 수 ≤ 파티션 수가 기본 룰)

B. 중복 없는 분배 (동시 중복 방지)

  • 같은 그룹 내에서는 Group Coordinator(브로커)가 파티션을 독점 할당
  • 동시에 둘이 같은 파티션을 읽지 않도록 자동 보장

C. 자동 장애복구

  • 컨슈머가 죽거나 새로 합류하면 리밸런스로 파티션을 재분배
  • 마지막 커밋 오프셋 이후부터 이어 읽음 → 공백/중복 최소화

D. 멀티 구독(팬아웃)

  • 그룹이 다르면 서로 독립 오프셋을 가짐
  • 동일 토픽을 서로 다른 서비스가 각자 전체 소비 가능 (예: 검색 인덱싱용 그룹, 빌링용 그룹)

3) 내부 동작: 누가 어떤 메시지를 읽나?

핵심 요소

  • Group Coordinator: 그룹의 파티션 할당/리밸런스/오프셋 커밋 관리
  • 오프셋 저장소: 그룹별 커밋 오프셋은 내부 토픽 __consumer_offsets 에 저장
  • 하트비트 & 세션: 컨슈머는 주기적으로 하트비트를 보내 활성 상태를 증명

파티션 할당 전략(알고리즘)

  • Range / RoundRobin / Sticky / Cooperative-Sticky
  • 현대 운영에서는 (Cooperative) Sticky가 리밸런스 안정성과 파티션 이동 최소화 측면에서 선호

리밸런스가 일어나는 순간

  • 컨슈머 조인/이탈, 구독 토픽 변화, 파티션 수 변화 등
  • Cooperative-Sticky는 단계적 이동으로 스톨 최소화

4) “중복”은 두 종류가 있다 (헷갈리기 쉬운 포인트)

  1. 동시 중복: 같은 레코드를 동시에 두 컨슈머가 소비
    • 같은 그룹에서는 발생하지 않도록 Coordinator가 파티션 독점 할당
  2. 시간차 중복(재처리): 처리 후 커밋 전에 장애가 나 재처리되는 경우
    • 이는 커밋 타이밍/트랜잭션/멱등성으로 다루는 전달 시맨틱 이슈
    • 기본은 At-Least-Once (중복 가능), 필요 시 Exactly-Once(트랜잭션) 설계

5) 같은 그룹 vs 다른 그룹: 동작 차이 한 눈에

구분 같은 그룹(Group=“A”) 다른 그룹(Group=“A”, “B”)

파티션 할당 1:1 독점 (동시 중복 없음) 각 그룹이 각자 1:1 독점
오프셋 저장 그룹별로 공유 그룹마다 독립
소비 형태 부하분산 (한 번 처리) 팬아웃 (그룹 수만큼 처리)
장애 시 자동 리밸런스로 승계 각 그룹이 독립적으로 복구

6) 실습: 메시지 한 번에 이해하기

MSK/네이티브 모두 동일. --bootstrap-server 와 인증 설정만 맞추면 됩니다.

6-1) 준비

# 토픽 생성 (파티션 3)
kafka-topics.sh --bootstrap-server <BOOTSTRAP> \\
  --create --topic demo --partitions 3 --replication-factor 3

6-2) 같은 그룹으로 두 컨슈머 실행 (부하분산)

# Consumer-1 (Group=g1)
kafka-console-consumer.sh \\
  --bootstrap-server <BOOTSTRAP> --topic demo \\
  --group g1 --from-beginning

# Consumer-2 (Group=g1)
kafka-console-consumer.sh \\
  --bootstrap-server <BOOTSTRAP> --topic demo \\
  --group g1 --from-beginning

→ 두 컨슈머가 서로 다른 파티션을 나눠 읽음(동시 중복 없음)

6-3) 다른 그룹으로 하나 더 실행 (팬아웃)

# Consumer-3 (Group=g2)
kafka-console-consumer.sh \\
  --bootstrap-server <BOOTSTRAP> --topic demo \\
  --group g2 --from-beginning

→ g2는 g1과 독립으로 전체 메시지를 다시 읽음

6-4) 그룹 상태/라그(지연) 확인

kafka-consumer-groups.sh \\
  --bootstrap-server <BOOTSTRAP> --group g1 --describe
  • CURRENT-OFFSET / LOG-END-OFFSET / LAG 로 처리 지연을 모니터링

7) 운영 베스트 프랙티스

  • 파티션 수 ≥ 피크 시 동시 컨슈머 수
    • 컨슈머가 파티션보다 많으면 놀게 되는 컨슈머 발생
  • 커밋 타이밍: 처리 후 커밋(At-Least-Once) + 멱등 처리(idempotent) 습관화
    • DB upsert, 중복 키, 트랜잭션 아웃박스 등으로 재처리 안전하게
  • 리밸런스 튜닝
    • max.poll.interval.ms, session.timeout.ms, heartbeat.interval.ms 균형
    • 가능하면 Cooperative-Sticky 사용으로 스톨 완화
  • 백프레셔/배치
    • max.poll.records, fetch.min.bytes, fetch.max.wait.ms 를 워크로드에 맞게 조정
  • 모니터링
    • Consumer Lag(가장 중요한 지표), 리밸런스 빈도, 처리 시간 분포(p50/p99) 추적
  • 여러 서비스가 같은 토픽을 써야 하면
    • 그룹을 분리해 독립 구독(팬아웃) 구성 → 서비스 간 결합도↓, 롤백/스케일 독립성↑

8) 자주 받는 질문(FAQ)

Q. 컨슈머만 여러 대 띄우면 되지, 왜 그룹이 필요해?

A. 그룹이 없으면 누가 어떤 파티션을 읽을지, 장애 시 누가 승계할지, 중복을 어떻게 막을지 전부 직접 코디네이션해야 합니다. 그룹은 이 모든 걸 자동으로 처리합니다.

Q. 같은 그룹인데도 가끔 중복이 눈에 보여요.

A. 그건 “동시 중복”이 아니라 **재처리(시간차 중복)**일 가능성이 큽니다. 커밋 타이밍/트랜잭션/멱등성 설계를 점검하세요.

Q. 그룹 수를 늘리면 처리량도 늘어나나요?

A. 아니요. 처리량은 그룹 내 컨슈머 수 ≤ 파티션 수에서 병렬성이 결정됩니다. 그룹을 늘리면 팬아웃은 되지만 부하분산은 아닙니다.


9) MSK 실무 팁(짧게)

  • AWS MSK에서도 클라이언트 툴은 로컬/EC2/EKS에서 실행
  • kafka-consumer-groups.sh --bootstrap-server ... --describe 로 라그 상시 관측
  • IaC(Terraform) + Kafka UI(AKHQ/Kafka UI)를 보조로 쓰면 운영 효율↑

마무리

Consumer Group 은 카프카의 고성능·고가용성 철학을 운영 친화적으로 구현하는 장치입니다.

  • 같은 그룹 = 부하분산 + 중복 방지 + 자동 복구
  • 다른 그룹 = 독립 구독(팬아웃)
  • 여기에 올바른 커밋/멱등/리밸런스 튜닝만 얹으면, 안정성과 확장성을 모두 잡을 수 있어요.
반응형

'Kafka' 카테고리의 다른 글

[Kafka] Raft 알고리즘  (1) 2025.09.29
Event Driven Architecture란?  (0) 2022.06.30

댓글