
분산 시스템을 공부하다 보면 반드시 마주치게 되는 개념이 있습니다. 바로 Quorum(쿼럼)입니다. 오늘은 Quorum이 무엇인지, 왜 필요한지, 그리고 Apache Kafka에서 어떻게 활용되는지 깊이 있게 알아보겠습니다.
Quorum(쿼럼)은 우리말로 "정족수"를 의미합니다. 표준국어대사전에서는 정족수를 "합의체가 의사를 진행하고 결정하는 데에 필요한 최소한의 출석 인원"이라고 정의합니다.
분산 시스템에서 Quorum은 특정 작업을 수행하기 위해 필요한 최소한의 노드 합의 수를 의미합니다. 즉, 의사결정에 필요한 최소 참여자 수라고 할 수 있습니다.
분산 시스템에서 가장 많이 사용되는 것은 Majority Quorum(과반수 쿼럼)입니다. n개의 노드가 있을 때, n/2보다 큰 수의 노드가 동의해야 작업이 진행되는 방식입니다.
예를 들어:
분산 시스템에서 가장 치명적인 문제 중 하나가 Split Brain(스플릿 브레인) 현상입니다. 이는 클러스터에서 둘 이상의 노드 그룹이 서로 독립적으로 I/O 요청을 처리하면서 충돌이 발생하는 상황을 말합니다.
하나의 Primary 노드에 4대의 Secondary 노드가 연결되어 있다고 가정해봅시다:
Primary
├── Secondary 1
├── Secondary 2
├── Secondary 3 (네트워크 단절)
└── Secondary 4 (네트워크 단절)
네트워크 문제로 Secondary 3, 4가 고립되면:
"데이터를 쓰기 위해서는 최소 3대의 노드로부터 동의를 얻어야 한다"는 Quorum 규칙을 적용하면:
Quorum 모델은 두 가지 핵심 규칙을 통해 데이터 일관성을 보장합니다:
Vr (Read Set) + Vw (Write Set) > V (Total Nodes)
Read를 수행하는 노드들과 Write를 수행하는 노드들이 반드시 최소 하나 이상 겹쳐야 합니다.
이를 통해:
예시 (총 3개 노드):
Vw > V/2
Write 작업에 참여하는 노드 수가 전체 노드의 과반수를 초과해야 합니다.
이를 통해:
예시 (총 5개 노드):
Apache Kafka는 3.0 버전부터 KRaft(Kafka Raft) 모드를 도입하여 ZooKeeper 의존성을 제거하고 자체 Quorum 메커니즘을 구현했습니다.
1. 성능 문제
2. 운영 복잡도
3. 모니터링 부담
1. 아키텍처 단순화
2. 성능 향상
3. 운영 효율성
KRaft 모드에서는 Controller Quorum이라는 컨트롤러 노드 그룹이 존재합니다:
Controller Quorum (3대 권장)
├── Controller 1 (Active Leader) ✅
├── Controller 2 (Standby)
└── Controller 3 (Standby)
구성 원칙:
KRaft는 Raft 합의 알고리즘을 기반으로 동작합니다:
핵심 RPC:
리더 선출 과정:
1. 컨트롤러 1 장애 발생
↓
2. 컨트롤러 2, 3이 타임아웃 감지
↓
3. 후보자(Candidate)가 자신에게 투표
↓
4. 다른 컨트롤러들에게 투표 요청
↓
5. 과반수 표를 얻은 후보자가 새로운 리더로 선출
↓
6. BeginQuorumEpoch로 새 리더 알림
KRaft 모드에서는 @metadata라는 특별한 내부 토픽을 사용하여 클러스터 메타데이터를 관리합니다:
__cluster_metadata-0/
├── 00000000000000000000.log (메타데이터 로그)
└── quorum-state (쿼럼 상태)
메타데이터 복제 방식:
Quorum 유지 중:
3대 구성에서 1대 장애
→ 2/3 과반수 유지
→ 정상 작동 계속
Quorum 상실:
3대 구성에서 2대 장애
→ 1/3 과반수 미달
→ 메시지 읽기/쓰기는 일정 시간 가능
→ 메타데이터 변경 작업 차단
(새 토픽 생성, 파티션 추가 등 불가)
# 노드 역할 (broker, controller, 또는 둘 다)
process.roles=broker,controller
# 노드 고유 ID
node.id=0
# Controller Quorum 투표자 목록
controller.quorum.voters=0@172.17.20.57:9093,1@172.17.20.58:9093,2@172.17.20.59:9093
# 리스너 설정 (브로커와 컨트롤러 포트 분리)
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
# 외부 통지 주소
advertised.listeners=PLAINTEXT://172.17.20.57:9092
# 로그 디렉터리
log.dirs=/tmp/kraft-combined-logs
# 1. Cluster ID 생성
$ bin/kafka-storage.sh random-uuid
KLvENWRBTF6-9Mb33Y8zhA
# 2. Storage 초기화
$ bin/kafka-storage.sh format \
-t KLvENWRBTF6-9Mb33Y8zhA \
-c config/kraft/server.properties
# 3. KRaft 모드 시작
$ bin/kafka-server-start.sh config/kraft/server.properties
Confluent의 벤치마크 결과에 따르면:
| 메트릭 | ZooKeeper 모드 | KRaft 모드 | 개선율 |
|---|---|---|---|
| 컨트롤러 장애 복구 시간 | ~수십 초 | ~수 초 | 10배 이상 |
| 지원 파티션 수 | ~200,000개 | 수백만 개 | 10배 이상 |
| 메타데이터 동기화 | 비동기 (불일치 가능) | 동기 (일관성 보장) | - |
성능 향상 이유:
1. 메모리 내 메타데이터 캐시
2. ZooKeeper 통신 오버헤드 제거
3. 효율적인 메타데이터 복제 메커니즘
4. 빠른 리더 선출 프로세스
기존 3대 Controller Quorum을 5대로 확장하는 과정:
# 1. 기존 컨트롤러 설정 파일 수정
controller.quorum.voters=0@host1:9093,1@host2:9093,2@host3:9093,3@host4:9093,4@host5:9093
# 2. 각 노드 순차적으로 재시작
# - 설정 파일 수정
# - quorum-state 파일 삭제
# - 노드 재시작
# 3. 새 노드 추가
# - process.roles에 controller 추가
# - listeners에 컨트롤러 프로토콜 추가
# - controller.quorum.voters 업데이트
# 메타데이터 셸 사용
$ bin/kafka-metadata-shell.sh \
--snapshot /tmp/kraft-combined-logs/__cluster_metadata-0/00000000000000000000.log
>> ls /
brokers local metadataQuorum topicIds topics
>> cd brokers/
>> ls
0 1 2
>> cat 0/registration
RegisterBrokerRecord(brokerId=0, ...)
권장 구성:
프로덕션 환경:
- Controller Quorum: 3대 (작은 규모) ~ 5대 (대규모)
- 전용 Controller 노드 또는 Combined 모드 선택
개발/테스트 환경:
- Controller Quorum: 1대 또는 3대
- Combined 모드 (broker + controller)
주의사항:
필수 메트릭:
kafka.controller:type=KafkaController,name=ActiveControllerCountkafka.controller:type=ControllerStats,name=LeaderElectionRateAndTimeMskafka.server:type=KafkaServer,name=BrokerStatekafka.log.remote:type=RemoteLogMetricsManager,name=RemoteLogMetadataCount분산 시스템의 핵심 개념인 Quorum과 Kafka의 진화한 아키텍처 KRaft에 대해 알아보았습니다. 이 개념들은 현대 데이터 인프라의 신뢰성과 확장성을 보장하는 핵심 기술입니다. 여러분의 시스템에도 적용해보시기 바랍니다! 🚀