반응형
카프카에서 “컨슈머 그룹(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) “중복”은 두 종류가 있다 (헷갈리기 쉬운 포인트)
- 동시 중복: 같은 레코드를 동시에 두 컨슈머가 소비
- 같은 그룹에서는 발생하지 않도록 Coordinator가 파티션 독점 할당
- 시간차 중복(재처리): 처리 후 커밋 전에 장애가 나 재처리되는 경우
- 이는 커밋 타이밍/트랜잭션/멱등성으로 다루는 전달 시맨틱 이슈
- 기본은 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 |
댓글