내구성과 복제 메커니즘
왜 복제인가
Kafka 브로커 한 대는 언젠가 죽는다. 디스크가 고장 나거나, OS가 재부팅되거나, 네트워크가 끊길 수 있다. 복제(replication) 는 동일한 파티션 데이터를 여러 브로커에 두어 단일 장애점을 없애는 핵심 메커니즘이다.
각 파티션은 하나의 리더와 0개 이상의 팔로워로 구성된다. 프로듀서와 컨슈머는 항상 리더와 통신하고, 팔로워는 리더로부터 데이터를 fetch 해 복제한다.
ISR(In-Sync Replicas)
복제 그룹 내 모든 팔로워가 항상 리더와 동기화된 것은 아니다. ISR은 리더와 충분히 동기화되어 있는 레플리카(리더 포함)의 집합이다.
팔로워는 리더에게 Fetch 요청을 반복해 새 레코드를 가져온다. replica.lag.time.max.ms(기본 30초) 안에 Fetch 요청을 보내지 않거나 충분히 따라잡지 못하면 ISR에서 제거된다(ISR 축소). 나중에 따라잡으면 다시 ISR에 합류한다(ISR 확장).
ISR 축소·확장 비율은 kafka.server:type=ReplicaManager,name=IsrShrinksPerSec / IsrExpandsPerSec 메트릭으로 관찰할 수 있다. 이 값이 높으면 네트워크 불안정·GC 지연·리소스 부족을 의심한다.
내구성의 두 축: acks와 min.insync.replicas
내구성은 프로듀서 acks와 브로커 min.insync.replicas가 함께 결정한다.
| 설정 | 역할 |
|---|---|
acks=all (producer) | "ISR 전원이 복제할 때까지 ACK를 기다려라" |
min.insync.replicas (broker/topic) | "ISR 수가 이 값 미만이면 쓰기를 거부한다" |
replication.factor=3, min.insync.replicas=2로 설정하면:
- 브로커 1대가 죽어도(ISR=2) 쓰기가 가능하다.
- 브로커 2대가 죽으면(ISR=1 < 2)
NotEnoughReplicasException이 발생하고 쓰기가 거부된다.
acks=all만 단독으로 설정하면 ISR이 1(리더만)인 상황에서도 쓰기가 성공하므로 내구성 보장이 없다. 반드시 min.insync.replicas와 함께 사용해야 한다.
리더 장애와 리더 선출
리더 브로커가 죽으면 클러스터 컨트롤러가 ISR 중에서 새 리더를 선출한다. ISR 멤버는 리더의 모든 커밋된 메시지를 보유하므로 데이터 손실 없이 리더를 교체할 수 있다.
Unclean Leader Election
ISR의 모든 레플리카가 장애 나면 클러스터는 쓰기 불가 상태가 된다. unclean.leader.election.enable=true로 설정하면 ISR 밖의 레플리카(뒤처진 팔로워)도 리더가 될 수 있다. 가용성은 살아나지만, 그 레플리카에 없는 커밋 메시지는 영구 유실된다.
기본값은 false. 데이터 유실이 치명적인 시스템(결제, 감사 로그)에서는 반드시 false로 유지한다.
KRaft 모드에서의 컨트롤러
Kafka 4.0부터 ZooKeeper가 완전히 제거됐다. KRaft(Kafka Raft) 모드에서는 컨트롤러 역할을 맡은 브로커들이 자체 Raft 쿼럼으로 클러스터 메타데이터(__cluster_metadata 토픽)를 관리한다. 리더 장애 감지와 선출도 ZooKeeper 없이 컨트롤러 쿼럼 내부에서 처리된다. 운영 복잡도가 크게 낮아진 변화다.
High Watermark와 데이터 가시성
리더는 HW(High Watermark) 까지만 컨슈머에게 노출한다. HW는 현재 ISR 모두가 복제 완료한 오프셋이다. LEO(Log End Offset, 리더 기준 마지막 쓰기 오프셋)와 HW 사이의 레코드는 아직 복제 중인 "미확정" 데이터이므로 읽을 수 없다.
리더 장애 후 새 리더가 선출되면 팔로워들은 자신의 LEO를 HW로 트런케이트(truncate) 해 일관성을 맞춘다. 이 과정에서 HW 이후 레코드는 유실되지만, 컨슈머에게는 이미 노출된 적이 없으므로 안전하다.
복제 설정 권고
| 설정 | 권고값 | 이유 |
|---|---|---|
replication.factor | 3 | 브로커 1대 장애 허용 |
min.insync.replicas | 2 | replication.factor - 1; 브로커 1대 장애 시에도 쓰기 가능 |
acks (producer) | all | ISR 전원 복제 후 ACK |
unclean.leader.election.enable | false | 데이터 유실 방지 |
replica.lag.time.max.ms | 30000 (기본) | ISR 판단 기준; 환경에 따라 조정 |
Open Questions
- KRaft 모드의 컨트롤러 쿼럼 크기(3 vs 5) 선택 기준과 조합 브로커(브로커+컨트롤러 겸용) vs 분리 모드의 운영 차이는 별도 정리 필요.
References
- https://kafka.apache.org/documentation/#replication
- https://www.conduktor.io/glossary/kafka-replication-and-high-availability
- https://thelinuxcode.com/understanding-in-sync-replicas-isr-in-apache-kafka-a-practical-operators-guide/
- https://oneuptime.com/blog/post/2026-01-21-kafka-replication-isr/view
- https://oneuptime.com/blog/post/2026-01-25-kafka-replication-fault-tolerance/view
- https://jack-vanlightly.com/blog/2023/8/17/kafka-kip-966-fixing-the-last-replica-standing-issue
- https://dev.to/devopsfundamentals/kafka-fundamentals-kafka-leader-election-3l2i
- https://axonops.com/docs/data-platforms/kafka/architecture/replication/