Kafka가 클러스터 메타데이터를 스스로 관리하는 구조. Kafka Raft의 줄임말이고 KIP-500에서 제안됐다.
먼저 — ZooKeeper가 무슨 일을 했나
브로커가 여럿이면 누군가는 이런 것들을 알고 있어야 한다.
- 어느 브로커가 어느 파티션의 리더인가
- 토픽·파티션 수·복제 설정은 무엇인가
- 지금 살아 있는 브로커는 누구인가
- 컨트롤러(클러스터를 지휘하는 브로커) 는 누구인가
이 메타데이터 관리를 Kafka는 14년 동안 ZooKeeper라는 별도 분산 시스템에 맡겼다. 즉 Kafka를 운영한다는 건 사실상 분산 시스템 두 개를 운영하는 일이었다 — 각각 별도의 클러스터·설정·모니터링·장애 대응이 필요했다.
KRaft의 발상 — 메타데이터도 로그다
Kafka가 제일 잘하는 게 로그다. 그러니 메타데이터 변경도 로그에 쌓으면 된다.
[ZooKeeper 시대]
ZooKeeper 앙상블 ←→ 브로커들 (외부 시스템에 상태 저장)
[KRaft]
컨트롤러 쿼럼(액티브 1 + 팔로워들)
│ 메타데이터 변경을 이벤트로 append
↓
__cluster_metadata (내부 토픽)
│ 브로커가 컨슈머처럼 당겨(pull) 읽는다
↓
각 브로커의 메모리에 반영
몇 대의 컨트롤러가 쿼럼(과반이 동의해야 결정이 확정되는 집단)을 이뤄 Raft 합의로 이 로그를 복제한다. 로그가 무한정 커지지 않도록 주기적으로 스냅샷을 남긴다.
왜 장애 복구가 빨라지나 — 메모리에 이미 있다
[ZooKeeper] 컨트롤러 사망
→ 새 컨트롤러 선출
→ ZooKeeper에서 전체 메타데이터를 다시 읽어 메모리에 구성
→ 파티션이 많을수록 이 로딩이 길어진다
[KRaft] 액티브 컨트롤러 사망
→ 팔로워는 이미 같은 로그를 따라오며 메모리에 상태를 갖고 있다
→ 거의 즉시 이어받는다
이 차이가 파티션 확장 한계로 이어진다. 복구 시간이 파티션 수에 비례하지 않게 되면서 클러스터가 다룰 수 있는 파티션 수가 크게 늘었다.
버전 — 실무에서 반드시 아는 지점
| 시점 | 상태 |
|---|---|
| 3.3 | 신규 클러스터에 대해 프로덕션 준비 완료 |
| 3.9 | ZooKeeper 모드에서 넘어오는 마지막 다리 |
| 4.0 (2025-03) | KRaft 전용. ZooKeeper 모드 제거 |
ZooKeeper 모드에서 4.0으로 직행할 수 없다. 3.x(권장 3.9)에서 먼저 KRaft로 전환한 뒤 4.0으로 올려야 한다. 자바 요구 버전도 갈렸다 — 클라이언트·Streams는 Java 11, 브로커·Connect·도구는 Java 17 이상이다.
면접 함정
- ❌ "Kafka는 ZooKeeper가 필요하다" → 4.0부터 아니다. 이 한 줄로 공부 시점이 드러난다.
- ❌ "ZooKeeper는 이제 안 쓰인다" → Kafka에서 빠졌을 뿐, 분산 락·서비스 디스커버리 용도로는 여전히 쓰인다(etcd와 같은 자리).
설정이 어떻게 바뀌나
# ZooKeeper 시대
zookeeper.connect=zk1:2181,zk2:2181,zk3:2181
# KRaft — 외부 주소가 사라지고 역할과 쿼럼 구성이 들어온다
process.roles=broker,controller # broker / controller / 둘 다
node.id=1
controller.quorum.voters=1@kafka1:9093,2@kafka2:9093,3@kafka3:9093
process.roles를 분리하면 컨트롤러 전용 노드를 따로 둘 수 있다(대규모 클러스터의 권장 구성). 소규모에서는 broker,controller로 겸하는 게 보통이다.
# 클러스터를 처음 만들 때 저장소를 포맷하는 단계가 새로 생겼다
kafka-storage.sh format -t $(kafka-storage.sh random-uuid) -c server.properties