Ch 05. 프로그램 내에서 코드로 카프카 관리하기
p. 126
카프카의 AdminClient는 Future 객체를 Result 객체 안에 감싸는데, Result 객체는 작업이 끝날 때까지 대기하거나 작업 결과에 대해 일반적으로 뒤이어 쓰이는 작업을 수행하는 헬퍼 메서드를 가지고 있다.
Q. 실제 클래스 구조는 어떻게 생겼는가?
p. 137
// ① 커밋된 consumer group offset 조회
Map<TopicPartition, OffsetAndMetadata> offsets = admin
.listConsumerGroupOffsets(CONSUMER_GROUP)
.partitionsToOffsetAndMetadata().get();
// ② 다시 최신 offset 조회
Map<TopicPartition, OffsetSpec> requestLatestOffsets = new HashMap<>();
for (TopicPartition tp : offsets.keySet()) {
requestLatestOffsets.put(tp, OffsetSpec.latest());
}
Map<TopicPartition, ListOffsetsResult.ListOffsetsResultInfo> latestOffsets =
admin.listOffsets(requestLatestOffsets).all().get();
Q. ①에서 이미 offset이 있는 것 같은데, 왜 굳이 ②에서 최신 offset을 따로 조회하는가?

두 값은 Kafka에서 lag (지연) 분석, 누락 처리, backlog 확인, 지표 수집 등에서 핵심적으로 사용된다.
Ch 06. 카프카 내부 메커니즘
p. 150
Q. Ephemeral 노드란?
A. Ephemeral 노드는 ZooKeeper에서 클라이언트 세션이 살아있는 동안만 유지되는 임시 노드입니다.
세션이 종료되면 자동으로 삭제됩니다.
p. 150
그렇기 때문에 만약 특정한 브로커가 완전히 유실되어 동일한 ID를 가진 새로운 브로커를 투입할 경우, 곧바로 클러스터에서 유실된 브로커의 자리를 대신해서 이전 브로커의 토픽과 파티션들을 할당받는다.
Q. 동일한 ID를 갖게 만든다는 것이 의도적이냐?
A. 맞다. Kafka에서 브로커 ID는 브로커의 정체성을 나타내는 핵심 식별자이며, 이전 브로커가 사라졌을 때 그 자리를 대체하려면 일부러 같은 ID로 재투입해야 한다.
만약 다른 ID로 띄우면 Kafka는 이 새 브로커를 전혀 다른 새 브로커로 인식한다.
- 메타데이터에는 새로운 /brokers/ids/N 으로 등록
- 컨트롤러는 기존 파티션을 절대 이 브로커에 자동으로 매핑하지 않음
- 명시적으로 파티션 reassignment를 해야 반영 가능
p. 152
컨트롤러가 주키퍼에 메타데이터를 쓰는 작업은 동기적으로 이루어지지만, 브로커 메시지를 보내는 작업은 비동기적으로 이루어진다. 또한, 주키퍼로부터 업데이트를 받는 과정 역시 비동기적으로 이루어진다. 그렇기 때문에 브로커, 컨트롤러, 주키퍼 간에 메타데이터 불일치가 발생할 수 있으며, 잡아내기도 어렵다.
Q. 비동기에서는 어느 시점에 항상 불일치가 발생할 수 있을 것 같은데, 이것이 Zookeeper 기반 메타데이터 구조이기 때문이라고 말할 수 있는가?
A. 위 내용은 다음 구조적 특징들을 포함한다:
- 컨트롤러 → ZooKeeper: 동기 쓰기
- 컨트롤러 → 브로커들(리더 변경 알림): 비동기 메시지 전송
- 브로커 → ZooKeeper(메타데이터 watch): 비동기 업데이트 감지
결국, 동기/비동기 혼합 구조로 인해 모든 컴포넌트가 동일한 시점에 동일한 정보를 가지고 있지 않을 수 있다.
Q. KRaft 모드로 전환하면 메타데이터 불일치 문제가 해결되나?
A. Kafka 2.8+부터 ZooKeeper를 제거한 **KRaft 모드(KIP-500)**가 도입되며:
- 컨트롤러가 Raft consensus로 직접 메타데이터를 저장
- 브로커는 controller로부터 push 기반으로 메타데이터를 받음
- ZooKeeper의 watch + 비동기 구조에서 생기던 불일치가 줄어듦
즉, 불일치 문제는 ZooKeeper 탓이 아니라 구조적 설계의 한계였고, Kafka는 이를 해결하기 위해 아예 ZooKeeper를 없애는 구조(KRaft)로 진화 중이다.
KRaft 에서는 ZooKeeper를 제거하고, Kafka 내부에서 메타데이터를 일관되게 관리한다.
따라서 기존 ZooKeeper 기반 Kafka보다는 메타데이터 불일치 확률이 획기적으로 줄어든다.
하지만 모든 시점에서 절대적 일치가 항상 즉시 보장되지는 않는다. 다만, Raft 알고리즘 특성상 eventually consistent가 아닌 strictly consistent로 수렴한다.
Q. Raft Quorum, Raft consensus, Raft 알고리즘은 무엇인가?
1. 큰 그림 : 왜 Raft가 필요한가?
Kafka 같은 분산 시스템에서는 메타데이터(토픽, 파티션, 리더 등) 를 여러 노드가 같이 알고 있어야 한다.
하지만 아래와 같은 문제가 발생할 수 있다:
- 리더가 죽으면?
- 네트워크가 일시적으로 분리되면?
- 여러 노드가 서로 다른 상태를 알고 있다면?
이럴 때도 시스템이 안전하게 일관된 결정을 내릴 수 있어야 한다.
이를 해결하기 위해 Kafka는 Raft consensus 알고리즘을 사용한다.
2. Raft Consensus 란?
여러 대의 서버가 서로 다른 네트워크 환경에서도, "하나의 결정"에 다수의 동의를 얻어 일관된 상태를 유지하도록 만드는 알고리즘
3. Raft의 핵심 구성 요소
| Leader | 유일한 쓰기 권한 보유자. 모든 변경은 리더를 통해서만 가능 |
| Followers | 리더의 명령을 따라가는 노드들 |
| Candidate | 리더 선출을 위해 임시로 나선 노드 |
| Term | 리더 선출 주기 (선거 라운드 같은 개념) |
| Log | 모든 노드가 공유해야 할 기록 (예: 메타데이터 변경, 설정 변경 등) |
4. Raft Quorum 이란?
Quorum은 과반수 이상 참여자들의 동의를 의미한다
5. Raft는 어떤 방식으로 일관성을 보장할까?
1️⃣ 리더 선출
- 모든 노드는 일정 시간 동안 리더 응답이 없으면 후보(candidate) 가 됨
- 투표를 요청하고, 과반수 이상이 승인하면 리더가 됨
2️⃣ 로그 복제
- 클라이언트가 어떤 변경 요청(예: 토픽 생성)을 하면
- 리더는 자신의 로그에 먼저 추가하고, follower들에게 복제를 요청함
- Quorum(과반수) 노드가 이 로그를 받았다고 응답하면 commit 확정
3️⃣ 일관성 보장
- follower는 리더가 승인한 로그만 최종 반영
- 로그는 모든 노드에서 같은 순서, 같은 내용으로 유지됨
p. 156
브로커는 추후 시동 시간을 줄이기 위해 메타데이터를 디스크에 저장한다.
Q. 그러면 ZooKeeper 사용할 떄는 브로커가 메타데이터를 디스크에 저장하지 않아?
A. 맞다. ZooKeeper 기반 Kafka에서는 브로커가 메타데이터를 디스크에 직접 저장하지 않는다.
대신, 브로커는 시동 시마다 ZooKeeper에서 메타데이터를 fetch해서 메모리에 적재한다.
즉, 브로커 디스크에는 파티션 데이터만 저장되고, 메타데이터는 저장되지 않는다.
Q. 왜 ZooKeeper 사용 때는 디스크에 저장을 안했을까?
A. Kafka 2.x 구조에선:
- 메타데이터는 전적으로 ZooKeeper가 authoritative source
- 브로커는 그때그때 ZooKeeper에서 읽어서 메모리에만 저장
- 메타데이터가 자주 변경될 수 있고, ZooKeeper와의 정합성 유지가 중요해서 디스크 캐시 유지하지 않음
➡ 디스크 저장의 유익보다 메타데이터 신선도와 consistency 확보가 더 중요했던 구조
'독서' 카테고리의 다른 글
| [카프카 핵심 가이드] Ch 11, Ch 12 (1) | 2025.08.31 |
|---|---|
| [카프카 핵심 가이드] Ch 09, Ch 10 (1) | 2025.08.24 |
| [카프카 핵심 가이드] Ch 04 (3) | 2025.08.03 |
| [카프카 핵심 가이드] Ch 03 (2) | 2025.07.27 |
| [카프카 핵심 가이드] ch 01, 02 (0) | 2025.07.20 |