Kafka 핵심 아키텍처 — 토픽, 파티션, 브로커, 리플리케이션
90+ Kafka Interview Questions and Answers (blog) kafka_ref_01(Q1-9,14-21), kafk_ref_02(Q1-4,7-8,32), kafka_ref_05(Q1-5) 종합. kafk_ref_02의 Kafka vs RabbitMQ 처리량 비교 수치(Q32, '10만 vs 2만 msg/sec')는 출처 불명확한 벤치마크 주장이라 판단해 구체 수치 대신 구조적 차이 설명으로 완화.
이 모듈을 다 읽으면
- 토픽/파티션/브로커/프로듀서/컨슈머의 역할과 상호작용을 설명할 수 있다
- 파티션이 왜 Kafka의 확장성과 병렬 처리의 핵심 단위인지 설명할 수 있다
- 리플리케이션, 리더/팔로워, ISR이 어떻게 장애 허용성을 제공하는지 설명할 수 있다
- Kafka와 전통적 메시지 큐(RabbitMQ 등)의 근본적 차이를 구분할 수 있다
Kafka의 기본 구성요소(토픽, 파티션, 브로커, 프로듀서, 컨슈머)가 어떻게 맞물려 분산 커밋 로그를 이루는지, 그리고 리플리케이션과 ISR이 어떻게 장애 상황에서도 가용성과 내구성을 지키는지 정리한다.
Kafka는 무엇이고 왜 전통적 메시지 큐와 다른가
Kafka는 pub/sub 이벤트 스트리밍 플랫폼으로, 메시지를 소비 여부와 무관하게 설정된 보존 기간 동안 불변 로그(append-only log)에 남겨둔다는 점이 전통적 메시지 큐(RabbitMQ, 전통적 JMS 브로커 등)와 가장 다르다. 전통적 큐는 컨슈머가 메시지를 처리하면 큐에서 제거하는 것을 전제로 설계되어 신뢰성 있는 1회성 전달과 다양한 라우팅 패턴에 강점이 있는 반면, Kafka는 컨슈머가 각자 자신의 오프셋(위치)만 관리하며 원하는 시점부터 다시 읽을 수 있는 재생(replay) 가능한 로그를 지향한다.
이 차이 때문에 처리량 수치 하나로 두 계열의 우열을 단정하는 것은 부정확하다 — 벤치마크 환경(메시지 크기, 배치 설정, 하드웨어)에 따라 격차가 크게 달라지므로, "Kafka가 초당 10만 건, RabbitMQ가 초당 2만 건" 같은 특정 수치를 암기하기보다, Kafka는 파티션과 배치·압축·zero-copy를 통해 수평 확장형 고처리량에 최적화되어 있고 RabbitMQ류는 다양한 라우팅(exchange, routing key)과 낮은 지연의 요청-응답/작업 큐 패턴에 강점이 있다는 구조적 차이로 이해하는 것이 맞다.
핵심 포인트
- Kafka는 소비 여부와 무관하게 보존 기간 동안 메시지를 남기는 불변 로그다
- 전통 메시지 큐는 처리 완료 후 메시지를 제거하는 것을 전제로 설계된다
- 처리량 수치는 벤치마크 조건에 따라 크게 달라지므로 특정 수치를 절대값으로 암기하지 않는다
토픽과 파티션 — 확장성의 단위
토픽은 레코드가 발행되는 논리적 채널이며, 내부적으로 하나 이상의 파티션으로 나뉜다. 각 파티션은 순서가 보장되는 append-only 로그이고, 파티션 내 각 레코드에는 순차적인 오프셋이 부여된다. 파티션은 서로 다른 브로커에 분산 배치될 수 있어 여러 프로듀서가 동시에 서로 다른 파티션에 쓰고, 컨슈머 그룹 내 여러 컨슈머가 동시에 서로 다른 파티션을 읽는 것이 가능해진다 — 이것이 Kafka의 수평 확장 메커니즘이다.
다만 순서 보장은 파티션 내부에서만 성립하고 토픽 전체(파티션 간)로는 보장되지 않는다. 같은 키를 가진 메시지는 기본 파티셔너(murmur2 해시 기반)에 의해 항상 같은 파티션으로 라우팅되므로, 엔티티 단위(예: 주문 ID, 고객 ID)로 순서가 필요하면 그 값을 키로 사용하는 것이 일반적인 해법이다. 파티션 수는 이후 늘릴 수는 있지만(kafka-topics.sh --alter --partitions) 줄일 수는 없고, 파티션을 늘리면 키-파티션 해시 매핑이 바뀌어 기존 키의 순서 보장이 깨지므로 처음부터 신중히 설계해야 한다.
핵심 포인트
- 파티션은 순서가 보장되는 append-only 로그이며 오프셋으로 각 레코드를 식별한다
- 순서 보장은 파티션 내부에서만 성립하고 토픽 전체로는 보장되지 않는다
- 같은 키는 항상 같은 파티션으로 라우팅되어 엔티티 단위 순서를 보장하는 수단이 된다
- 파티션 수는 늘릴 수만 있고 줄일 수 없으며, 늘리면 기존 키의 파티션 매핑이 바뀐다
리플리케이션, 리더/팔로워, ISR로 장애를 견디는 방법
각 파티션은 replication factor만큼 복제되며, 그중 하나가 리더로서 모든 읽기/쓰기를 처리하고 나머지 팔로워는 리더로부터 수동적으로 복제한다. 리더가 속한 브로커가 죽으면, 클러스터 컨트롤러(현재는 KRaft 컨트롤러 쿼럼)가 ISR(In-Sync Replicas, 리더와 충분히 동기화된 복제본 집합) 중에서 새 리더를 선출한다.
replication factor는 파티션의 총 복제본 수일 뿐이고, ISR은 그중 실제로 리더를 따라잡고 있는 부분집합이라는 점이 중요하다 — replication factor가 3이어도 두 팔로워가 지연되어 있으면 ISR은 1(리더만)일 수 있다. min.insync.replicas는 acks=all일 때 쓰기가 성공하려면 ISR에 최소 몇 개가 있어야 하는지를 정의하며, 부족하면 프로듀서는 예외(NotEnoughReplicasException)를 받는다. unclean.leader.election.enable=false(기본값이자 권장값)는 ISR이 전부 사라졌을 때 오래된(동기화되지 않은) 복제본을 억지로 리더로 승격시키지 않고 해당 파티션을 일시적으로 오프라인 상태로 두어, 가용성보다 일관성(데이터 유실 방지)을 우선한다. 랙 인식 배치(broker.rack)를 쓰면 같은 파티션의 복제본들이 같은 랙/가용영역에 몰리지 않게 분산시켜, 랙 단위 장애에도 데이터가 보호된다.
핵심 포인트
- replication factor는 총 복제본 수, ISR은 그중 리더와 동기화된 부분집합이라는 점이 다르다
- min.insync.replicas는 acks=all 쓰기가 성공하기 위한 ISR 최소 개수를 정의한다
- unclean.leader.election.enable=false는 데이터 유실을 막기 위해 가용성보다 일관성을 우선하는 안전한 기본값이다
- broker.rack 기반 랙 인식 배치는 랙/AZ 단위 장애로부터 복제본을 보호한다