백엔드 데이터 인프라 105편. Kafka KRaft — Kafka 4.0+ 의 기본 모드. Zookeeper 제거의 의미·Raft 합의 알고리즘·controller quorum·Combined vs Dedicated mode·migration·운영 가이드까지 풀어쓴 학습 노트.
이 글은 백엔드 데이터 인프라 시리즈 130편 중 105편이에요. Part 5-5 Operations 까지로 일상 운영 을 잡았다면, 이번 105편부터는 Part 5-6 — 고급 인프라 3편이 이어져요. 첫 글은 KRaft (Kafka Raft, Kafka 내장 합의 프로토콜) — Kafka 4.0 부터 기본 모드로 자리잡았고, Zookeeper 의 후속이에요.
KRaft가 어렵게 느껴지는 이유
Kafka 의 가장 큰 아키텍처 변화 라고 할 만해요. 2010년 이래 Zookeeper (분산 코디네이션 서비스) 의존이 기본이었는데, 이게 완전히 대체되는 흐름이거든요.
첫째, 왜 Zookeeper 를 버렸나 가 잘 안 잡혀요. Zookeeper 는 안정적이고 검증된 시스템인데 왜 별도 protocol 까지 만들었을까요?
둘째, Raft 알고리즘 자체가 새로워요. 분산 합의 알고리즘에 Kafka 의 metadata log 가 결합된 형태죠.
셋째, Migration 의 복잡성 이에요. 기존 Zookeeper 모드에서 KRaft 모드로 전환하는 게 운영의 큰 주제로 떠올랐어요.
이 글에서 KRaft 의 의의·구조·운영·migration 패턴까지 짚어봐요.
왜 Zookeeper 를 버렸나
Zookeeper 의존이 만든 문제 4가지.
1. 두 시스템 운영 부담
Kafka cluster 와 Zookeeper ensemble 을 따로 운영해야 하니까 별도 cluster 가 두 개가 돼요. 각각 모니터링·튜닝·security·upgrade 까지 다 따로 챙겨야 하고요. 운영 복잡도가 ×2 가 되는 거죠.
2. Zookeeper Scalability 한계
Zookeeper 는 수백~수천 partition 까지의 메타데이터에 적합해요. 수십만 partition 환경이 되면 bottleneck 으로 작동해요.
3. Cluster 시작 시간
대규모 cluster 가 startup 할 때 Zookeeper 와 수많은 metadata 를 sync 해요. 수십 분에서 수 시간 까지도 걸려요.
4. Failover 속도
Controller failover 가 Zookeeper 의 watcher 등록과 notification 을 거쳐요. 결국 수 초에서 수십 초가 걸리죠.
KRaft 의 해법
Before: After (KRaft):
┌────────────┐ ┌─────────────────┐
│ Zookeeper │ │ Kafka Cluster │
│ ensemble │ │ (built-in │
└────────────┘ │ metadata mgmt) │
│ └─────────────────┘
│ metadata
↓
┌────────────┐
│ Kafka │
│ Cluster │
└────────────┘
Kafka 가 자체적으로 metadata 를 관리 하면서 Raft 합의 알고리즘이 내장된 형태에요.
장점:
- 단일 시스템 — 운영 부담 절반
- 확장성 — 수십만 partition 가능
- 빠른 startup — 수초~수분
- 빠른 failover — < 1초
Process Roles — 3가지
KRaft 에서 각 Kafka 서버가 가질 수 있는 역할이에요.
broker
process.roles=broker
데이터 저장과 producer/consumer 처리만 맡아요. 기존 broker 역할과 동일 해요.
controller
process.roles=controller
Metadata 관리만 맡고, Quorum (정족수 합의 그룹) 멤버로 동작해요.
broker,controller (Combined)
process.roles=broker,controller
둘 다 맡는 모드에요. 소규모 환경 (개발·테스트) 에 적합해요.
Combined vs Dedicated Mode
Combined Mode
[Server 1: broker+controller]
[Server 2: broker+controller]
[Server 3: broker+controller]
- 단순하고 비용이 적어요
- Controller 가 broker 부하의 영향을 받아요
- Rolling restart 시 controller 와 broker 가 함께 영향을 받아요
- 운영 환경 비권장 (공식 문서)
Dedicated Mode (운영 권장)
[Controller 1] [Controller 2] [Controller 3] ← metadata 전용 quorum
↓
[Broker 1] [Broker 2] [Broker 3] ... [Broker N] ← 데이터 처리
- Controller 와 broker 가 분리돼서 각자 독립적으로 운영 돼요
- Controller 는 작은 인스턴스 로도 OK (CPU·디스크 부담이 적어요)
- 운영 환경 표준
시험 함정이 하나 있어요 — Combined Mode 운영 환경 비권장. 작은 cluster (3 broker 이하) + 학습·테스트에만 쓰는 게 맞아요.
Controller Quorum
3 또는 5 controller
권장은 3 controller (소~중규모) 또는 5 controller (대규모) 에요.
| Controller 수 | Tolerate Failure |
|---|---|
| 1 | 0 (단일 장애 시 cluster 정지) |
| 3 | 1 |
| 5 | 2 |
| 7 | 3 |
홀수 로 두는 건 정족수(majority) 를 형성하기 위해서에요.
Quorum 동작
3 controller (id=1, 2, 3):
→ Raft 알고리즘으로 한 명이 leader (active controller)
→ 나머지 2명 = hot standby
→ Active 죽으면 quorum 동의로 새 leader 선출
→ 1초 이내 failover
Zookeeper 보다 훨씬 빠른 failover 가 나오고, 대량 metadata 도 처리해요.
설정
Controller 노드 (controller-1.example.com)
process.roles=controller
node.id=1
controller.listener.names=CONTROLLER
listeners=CONTROLLER://controller-1.example.com:9093
controller.quorum.bootstrap.servers=\
controller-1.example.com:9093,\
controller-2.example.com:9093,\
controller-3.example.com:9093
Broker 노드 (broker-1.example.com)
process.roles=broker
node.id=10
controller.listener.names=CONTROLLER
listeners=PLAINTEXT://broker-1.example.com:9092
controller.quorum.bootstrap.servers=\
controller-1.example.com:9093,\
controller-2.example.com:9093,\
controller-3.example.com:9093
핵심:
controller.quorum.bootstrap.servers= 모든 controller 명시node.id= controller 와 broker 모두 uniquecontroller.listener.names= controller 통신 listener 이름
Provisioning — kafka-storage.sh
KRaft 를 처음 시작할 때는 Provisioning (초기 클러스터 준비) 단계로 Cluster UUID 를 만들고 각 server 를 format 해야 해요.
Step 1: Cluster UUID 생성 (한 번만)
$ KAFKA_CLUSTER_ID=$(bin/kafka-storage.sh random-uuid)
$ echo $KAFKA_CLUSTER_ID
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Step 2: 각 server format
모든 controller 와 broker 에서:
$ bin/kafka-storage.sh format \
--cluster-id $KAFKA_CLUSTER_ID \
--config /etc/kafka/server.properties
같은 cluster-id 로 모든 노드를 format 해야 해요. 안 하면 cluster 가 합쳐지지 않아요.
Step 3: Start
# 각 controller 와 broker 에서
$ bin/kafka-server-start.sh -daemon /etc/kafka/server.properties
이전 (Zookeeper 모드) 와 다른 점:
- 수동 format 필수
- Cluster ID 가 모든 노드에 일치
- 미리 format 안 한 노드는 시작에 실패
Migration — Zookeeper → KRaft
기존 Zookeeper 모드 cluster 를 KRaft 로 전환하는 과정 (Migration) 이에요. 단계적으로 진행해요.
Step 1: Controller 노드 추가 (KRaft mode)
새 controller 들을 launching 해요. process.roles=controller 에 migration.enabled=true 를 같이 두고요.
process.roles=controller
node.id=3000
zookeeper.connect=zk1:2181,zk2:2181,zk3:2181
zookeeper.metadata.migration.enable=true
Controller 들이 Zookeeper 에서 metadata 를 읽어 자체 quorum 으로 동기화해요.
Step 2: Broker 들 KRaft mode 로 재시작 (rolling)
process.roles=broker
node.id=10
controller.quorum.bootstrap.servers=ctrl1:9093,ctrl2:9093,ctrl3:9093
zookeeper.metadata.migration.enable=true
각 broker 를 하나씩 rolling restart 해요. 이 시점이 dual write (Zookeeper 와 KRaft 둘 다에 metadata 를 쓰는 단계) 에요.
Step 3: Migration 완료 후
Zookeeper 의존을 제거해요.
# zookeeper.connect 제거
# zookeeper.metadata.migration.enable 제거
또 한 번 rolling restart 를 거쳐서 Zookeeper 를 완전히 분리해요.
Step 4: Zookeeper ensemble 종료
이제 안전하게 ZK 클러스터를 shut down 할 수 있어요.
전체 migration 은 수 일에서 수 주 정도 걸려요 (안전을 우선해야 하니까요). 미리 스테이징 환경에서 시뮬레이션해보는 게 필수에요.
Kafka 버전별 상태
- Kafka 2.8 — KRaft 도입 (early access)
- Kafka 3.3 — KRaft production ready
- Kafka 3.5 — KRaft Migration 정식 지원
- Kafka 4.0 — Zookeeper 모드 deprecated, KRaft 만 권장
- Kafka 4.1+ — Zookeeper 제거 (deprecated 후 완전 제거 예정)
신규 도입은 무조건 KRaft 로 가요. 기존 Zookeeper cluster 는 Kafka 3.5+ 으로 migration 하면 돼요.
운영 차이점 — Zookeeper vs KRaft
| 항목 | Zookeeper | KRaft |
|---|---|---|
| 별도 cluster | ◯ (ZK 따로) | X |
| Format 필수 | X (auto) | ◯ (kafka-storage.sh format) |
| Cluster ID | 자동 생성 | 명시적 (kafka-storage.sh random-uuid) |
| Controller failover | 수 초 | < 1초 |
| 대규모 partition (>200K) | 어려움 | OK |
| 운영 부담 | 두 cluster | 한 cluster |
| Tools 호환 | 대부분 호환 | KRaft 전용 도구 (kafka-metadata-quorum.sh) |
모니터링
Controller 상태
$ kafka-metadata-quorum.sh --bootstrap-server localhost:9092 describe --status
ClusterId: ...
LeaderId: 2
LeaderEpoch: 5
HighWatermark: 12345
MaxFollowerLag: 10
MaxFollowerLagTimeMs: 500
CurrentVoters: [1, 2, 3]
CurrentObservers: []
핵심:
LeaderId= 현재 active controllerMaxFollowerLag= follower 들의 metadata lagCurrentVoters= quorum 멤버
Replication
$ kafka-metadata-quorum.sh --bootstrap-server localhost:9092 describe --replication
각 voter 의 log end offset, lag 같은 정보가 나와요.
한계·실무 함정
1. Combined Mode 운영 강행
위에서 강조한 부분이에요. 작은 cluster + 학습용 에만 써요.
2. Format 누락
신규 노드를 format 없이 시작하면 Kafka 시작이 실패해요. 반드시 kafka-storage.sh format 을 거쳐야 해요.
3. Cluster ID 불일치
각 노드를 다른 cluster id 로 format 하면 cluster 가 합쳐지지 않아요. 모든 노드가 같은 ID 여야 해요.
4. Controller 수 부족
1 controller 는 단일 장애가 곧 cluster 정지로 이어져요. 최소 3 으로 가요.
5. Migration 중 dual-write 부담
migration 기간에는 Zookeeper 와 KRaft 둘 다에 metadata 를 쓰는 (dual-write) 구간이에요. 부담이 크니까 운영 모니터링도 강화해야 해요.
6. Old client 호환성
Kafka 4.0+ KRaft 는 일부 old client (< 2.5) 와 호환이 안 돼요. client 버전 upgrade 도 함께 진행해요.
시험 직전 한 번 더 — KRaft 함정 압축 노트
- KRaft = Kafka Raft, Zookeeper 의 후속
- Kafka 4.0+ = 기본 모드, Zookeeper deprecated
- 해결한 문제 = 운영 부담 (두 시스템)·확장성·startup 시간·failover 속도
- Process Roles =
broker/controller/broker,controller(combined) - Combined Mode = 단순·소규모 (운영 환경 비권장)
- Dedicated Mode = 운영 표준 (controller 와 broker 분리)
- Controller Quorum = 3 (소~중) 또는 5 (대규모), 홀수
- 3 controller = 1 failure tolerance, 5 = 2
- Active controller 1 + standby 들 (Raft 합의)
- Failover < 1초
- 핵심 설정 =
process.roles·node.id·controller.listener.names·controller.quorum.bootstrap.servers - Provisioning =
kafka-storage.sh random-uuid(cluster ID) +format --cluster-id - 모든 노드 같은 cluster ID 필수
- Migration = Zookeeper → KRaft 단계적 (수 일~수 주)
- Migration step = Controller 추가 (
migration.enable=true) → Broker rolling restart → Zookeeper 제거 → ZK shutdown - 신규 = 무조건 KRaft
- Kafka 3.5+ = Migration 정식 지원
- Kafka 4.0+ = ZK deprecated
- 모니터링 =
kafka-metadata-quorum.sh describe --status / --replication - 핵심 메트릭 =
LeaderId·MaxFollowerLag·CurrentVoters - 함정 — Combined 운영 강행
- 함정 — Format 누락 = 시작 실패
- 함정 — Cluster ID 불일치 = cluster 안 합쳐짐
- 함정 — 1 controller = 단일 장애
- 함정 — Migration 중 dual-write 부담
- 함정 — Old client 호환성 (< 2.5)
공식 문서: Kafka KRaft 에서 자세한 사양·migration 가이드를 확인할 수 있어요.
시리즈 다른 편 (앞뒤 글 모음)
이전 글:
- 100편 — Kafka Monitoring (JMX 메트릭 30가지)
- 101편 — Kafka Multi-tenancy (Quota · Naming · ACL 분리)
- 102편 — Kafka 다중 데이터센터 (Stretched · Local + Mirror)
- 103편 — Kafka Geo-Replication (MirrorMaker 2)
- 104편 — Kafka Hardware · OS (CPU·메모리·디스크·튜닝)
다음 글: