백엔드 데이터 인프라 89편. Kafka Replication — leader-follower 비대칭 모델, ISR(in-sync replicas)·committed message·자동 failover, Quorum 접근 vs ISR 접근의 trade-off, Unclean Leader Election 의 위험과 선택까지 풀어쓴 학습 노트. Part 5-2 Design 의 마지막 글.
이 글은 백엔드 데이터 인프라 시리즈 130편 중 89편이에요. Part 5-2 Design 의 마지막 글. 88편 에서 delivery semantics 보장의 기반 인 replication 이 어떻게 동작하는지가 이번 89편의 주제.
Kafka Replication이 어렵게 느껴지는 이유
분산 시스템에서 가장 깊은 영역이에요. 분산 합의 알고리즘, leader election, split-brain (네트워크 분리로 양쪽이 서로 leader 라고 우김) 같은 무거운 주제가 한꺼번에 엮입니다.
첫째, ISR (In-Sync Replicas) 개념이 새롭다는 점. 전통적 분산 시스템의 Quorum (다수결 투표 합의) 와 완전 다른 접근이라, ISR 이 뭐고 왜 Kafka 가 이걸 선택했는지부터 봐야 합니다.
둘째, Committed 의 정확한 정의. 언제 메시지가 안전한지가 ACK 설정과 ISR 의 조합으로 결정돼요.
셋째, Unclean Leader Election 의 위험. 데이터 손실과 가용성의 trade-off 인데, 잘못 선택하면 조용히 데이터가 사라집니다.
이 글에서 ISR 모델, committed 정의, Quorum vs ISR, Unclean Leader Election 까지 짚어요.
Replication 기본 모델
Topic "payments", Partition 0, Replication Factor 3
┌─────────────────────────┐
│ Broker 1 │
│ ★ Leader │ ← 모든 write/read 여기로
└─────────────────────────┘
│
│ (자동 복제)
┌──────────┼──────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│Broker 2 │ │Broker 3 │ │Broker 4 │
│Follower │ │Follower │ │Follower │
└─────────┘ └─────────┘ └─────────┘
각 partition 은 leader 1대와 follower N대로 구성돼요. Replication Factor 는 leader 와 follower 를 합한 값(보통 3)입니다.
동작
쓰기는 항상 leader 로 가고, leader 가 follower 에게 복제 stream 을 보냅니다. 읽기도 기본은 leader 지만, KIP-392 (Kafka Improvement Proposal 392, follower 읽기 기능) 같은 새 옵션을 켜면 follower 에서도 읽을 수 있어요. follower 는 일반 Kafka consumer 처럼 leader 에서 pull 로 가져갑니다.
Replica 위치
Kafka 가 broker 분산과 균등 leader 배치를 자동으로 잡아줘요.
Topic "payments" (12 partitions, RF=3)
Broker 1: P0-leader, P3-follower, P6-follower, P9-leader
Broker 2: P0-follower, P4-leader, P7-follower, P10-leader
Broker 3: P1-leader, P4-follower, P7-leader, P10-follower
Broker 4: P1-follower, P5-leader, P8-leader, P11-follower
partition leader 가 broker 들에 균등 분포되면서 자연스럽게 부하 분산이 됩니다.
ISR (In-Sync Replicas) — Kafka 의 핵심 모델
정의
ISR = leader 와 충분히 sync 된 replica 집합 (leader 포함).
조건은 두 가지예요. 하나는 controller 와의 active session (KRaft (Kafka Raft, ZooKeeper 를 대체한 합의 모듈) heartbeat 으로 살아 있다는 신호), 다른 하나는 leader 를 너무 뒤처지지 않게 따라가는 것(replica.lag.time.max.ms).
Leader 가 ISR 관리
Leader 가 각 follower 의 진행 상태를 추적해요. 일정 시간(replica.lag.time.max.ms, 기본 30초) 동안 leader 를 따라잡지 못한 replica 는 ISR 에서 빠집니다.
Out-of-Sync 가 되는 경우
follower 가 느려서 못 따라가거나, follower 와 leader 사이 네트워크가 막혔거나, follower 가 디스크 I/O 부담을 겪거나, follower 자체가 죽은 경우예요.
동적 변경
초기 ISR = {Broker 1, Broker 2, Broker 3, Broker 4} (RF=3 + leader)
Broker 4 가 5초 동안 응답 X
→ ISR = {Broker 1, Broker 2, Broker 3}
Broker 4 복구 + 따라잡음
→ ISR = {Broker 1, Broker 2, Broker 3, Broker 4}
ISR 변경은 cluster 메타데이터에 영속화돼서 모든 broker 가 알게 됩니다.
Committed 의 정확한 정의
"A message is considered committed when all replicas in the ISR for that partition have applied it to their log."
핵심은 ISR 모두가 받으면 committed.
- ISR = {A, B, C} 면 3개 모두 write 완료 → committed
- ISR 이 {A, B} 로 줄어들면 2개 모두 면 committed (B 는 ISR 이라 안전)
- ISR = {A} 만 남으면 A 만 write 해도 committed (위험!)
min.insync.replicas 와의 조합
이게 진짜 안전성을 결정합니다. 86편에서 본 producer 설정과 묶어서 봐요.
replication.factor=3
min.insync.replicas=2
acks=all
의미를 풀면, ISR 이 최소 2개일 때만 producer write 를 받아요. ISR 이 2 미만으로 떨어지면 NotEnoughReplicas 에러가 나면서 producer 쓰기가 실패합니다. broker 한 대가 죽어도 ISR 이 2라 여전히 쓸 수 있지만, 두 대가 죽으면 안전을 위해 쓰기를 막아요.
Consumer 측 — Committed 만
Consumer 는 committed 메시지만 봅니다. replica 까지 복제가 안 끝난 메시지는 consumer 에게 보이지 않으니, consumer 가 본 메시지는 안전하다고 보면 돼요.
Quorum vs ISR — 두 가지 접근
Quorum (전통 분산 시스템 — Raft·Paxos·Zab)
Majority vote — 2f+1 replica 중 f+1 이 동의해야 commit. f 개 fail 허용.
Raft (이해하기 쉬운 합의 알고리즘), Paxos (원조 분산 합의), Zab (ZooKeeper Atomic Broadcast) 가 대표적이에요.
장점은 두 가지. 가장 빠른 follower 만 따라와도 commit 되니 latency 가 짧고, 대중적이고 검증된 알고리즘이라 자료도 많습니다.
단점도 분명해요. f 개 fail 을 허용하려면 2f+1 replica 가 필요해서, 1 fail 허용에 3 copy, 2 fail 허용에 5 copy 가 들어가요. 데이터를 5배로 복제하는 건 대용량 영역에서 비실용적입니다.
Kafka 의 ISR 접근
모든 ISR 동의해야 commit. f+1 replica 로 f 개 fail 허용.
장점은 데이터 copy 가 절반이라는 점. 1 fail 을 허용하려면 2 copy 만 있으면 되니, Quorum 의 3 copy 와 비교해 대용량 환경에 잘 맞아요.
단점은 가장 느린 ISR follower 까지 기다려야 한다는 점. 다만 acks=1 로 두면 client 가 원할 때 빨리 응답받을 수 있게 열어둡니다.
왜 Kafka 가 ISR?
대용량 영역(TB·PB 데이터) 에 5× 데이터 비용은 비현실적이에요. Kafka 는 대용량 우선 설계라 ISR 을 골랐고, ZooKeeper 나 etcd 같은 작은 메타데이터 시스템은 데이터 양 자체가 적으니 Quorum (Raft) 이 자연스럽습니다.
한 줄 정리 — Kafka ISR = 대용량 친화 (데이터 절반) + 가장 느린 ISR 까지 기다림 trade-off.
Crashed Node 의 복구
여기서 시험 함정이 하나 있어요. Kafka 는 crashed node 가 모든 데이터를 보존한다고 가정하지 않습니다.
Broker A 가 죽음 (디스크 데이터 일부 손실 가능)
→ 다른 ISR 이 새 leader
→ Broker A 가 복구되면 *완전 re-sync* 후 ISR 재진입
이유는 운영 환경의 현실이에요. 디스크 에러가 가장 흔한 문제고, 매 write 마다 fsync (디스크에 강제 flush 하는 시스템 콜) 를 부르면 성능이 100~1000배 떨어져서 비실용적입니다. 그래서 fsync 없이도 안전하게 동작하는 protocol 로 설계됐어요.
unflushed data 가 있어도 복구 후 다시 동기화하므로 일관성은 유지됩니다.
Unclean Leader Election — 가장 위험한 결정
문제
ISR 의 모든 broker 가 동시에 죽으면 어떻게 될까요? ISR = {} 상태에서 누가 새 leader 가 돼야 할지가 문제입니다.
두 가지 옵션
(A) Clean Leader Election (안전)
unclean.leader.election.enable=false (기본)
ISR 이 부활할 때까지 partition 이 정지(read/write 둘 다 막힘) 합니다. 데이터 손실은 없지만 가용성도 없어요. ISR 이 전부 죽으면 partition 이 영원히 멈추니, operator 가 수동으로 개입해야 합니다.
(B) Unclean Leader Election (위험)
unclean.leader.election.enable=true
ISR 이 아니어도 살아 있는 replica 가 leader 가 됩니다. 가용성은 살아나지만, 그 replica 가 committed 메시지를 누락했을 수 있어서 과거 데이터 손실이 가능해요.
선택 가이드
금융이나 법적 데이터는 false 로 안전을 우선합니다. 로그·메트릭처럼 약간의 손실을 감내할 수 있는 데이터는 true 로 가용성을 우선해요. 기본값은 false 입니다.
여기서 정말 중요한 자리예요. unclean.leader.election.enable=true 는 데이터 손실 위험을 명시적으로 수용하는 설정이라, 대부분 환경에서는 false 를 권장합니다.
Network Partition
Kafka 는 네트워크가 분리된 환경에서는 가용성을 보장하지 않아요. broker 가 살아 있어도 network partition 으로 controller 와 통신이 끊기면 SDOWN (Subjectively Down, 외부에서 죽은 것으로 판단) 처리됩니다. minority partition 쪽 broker 는 죽은 것처럼 다뤄지고, majority partition 쪽만 계속 동작해요.
CAP theorem (분산 시스템에서 Consistency·Availability·Partition tolerance 중 둘만 보장 가능) 관점에서 Kafka 는 CP 시스템입니다. Consistency 와 Partition tolerance 를 잡고 Availability 를 희생합니다.
실무 운영 권장 설정
# Topic 레벨
replication.factor=3
min.insync.replicas=2
unclean.leader.election.enable=false
# Broker 레벨
replica.lag.time.max.ms=30000 # ISR 판정 (기본)
default.replication.factor=3
# Producer
acks=all
enable.idempotence=true
RF=3, min.insync=2, acks=all = Kafka 안전성의 황금 조합.
모니터링
UnderReplicatedPartitions 메트릭:
$ kafka-topics.sh --describe --under-replicated-partitions
ISR 이 replication factor 보다 적은 partition 을 알려줘요. 이 값이 지속적으로 0 이 아니면 문제입니다.
UnderMinIsrPartitions 는 ISR 이 min.insync.replicas 보다 적은 partition. 쓰기 실패 위험이 임박했다는 신호예요.
시험 직전 한 번 더 — Kafka Replication 함정 압축 노트
- Replication = partition 단위, replication factor (보통 3)
- 각 partition = 1 leader + N follower
- Write/Read 기본 = leader, follower 는 Kafka consumer 처럼 pull
- Partition leader = broker 들에 균등 분포
- ISR (In-Sync Replicas) = leader + 충분히 sync 된 follower
- ISR 조건 = (1) active session (2)
replica.lag.time.max.ms안에 leader 따라잡음 - ISR 동적 변경 = cluster 메타데이터에 영속화
- Committed = ISR 모두 받음 → 메시지 안전
- Consumer = committed 메시지만 본다
min.insync.replicas= ISR 최소 수, 안 되면NotEnoughReplicas에러- 운영 황금 조합 =
RF=3+min.insync.replicas=2+acks=all - 1 broker 죽어도 OK, 2 broker 죽으면 쓰기 실패 (안전 우선)
- Quorum 접근 (전통) = 2f+1 → f+1 동의 — 데이터 ×3·5
- Kafka ISR 접근 = f+1 → 모든 ISR 동의 — 데이터 ×2·3 (대용량 친화)
- ISR 단점 = 가장 느린 ISR 까지 기다림 (latency)
- Crashed node = 복구 시 완전 re-sync 후 ISR 재진입
- fsync every write 강제 X (성능 100~1000배 하강 방지)
- Unclean Leader Election = ISR 모두 죽었을 때 정책
false(기본) = partition 정지, 데이터 손실 X, 가용성 Xtrue= 살아 있는 아무 replica leader, 데이터 손실 가능, 가용성 ◯- 금융·법적 = false / 로그·메트릭 = true 가능
- Network Partition = Kafka 는 CP 시스템 (가용성 희생)
- 모니터링 메트릭 =
UnderReplicatedPartitions·UnderMinIsrPartitions - Under replicated 지속 = 문제 신호
- Under min ISR = 쓰기 실패 위험 임박
공식 문서: Kafka Design — Replication 에서 자세한 사양을 확인할 수 있어요.
시리즈 다른 편 (앞뒤 글 모음)
이전 글:
- 84편 — Kafka Design: Persistence (디스크가 빠르다)
- 85편 — Kafka Design: Efficiency (Zero-Copy · Batch 압축)
- 86편 — Kafka Design: Producer (Partition 선택·ACK·Idempotent)
- 87편 — Kafka Design: Consumer (Pull · Consumer Group · Offset)
- 88편 — Kafka Message Delivery Semantics (at-most·at-least·exactly-once)
다음 글: