기본개념 · 2장
아키텍처와 핵심 개념
1장에서 토픽·파티션·오프셋이라는 어휘를 잡았습니다. 이 장은 그 어휘가 실제로 어떤 프로세스와 어떤 파일 위에 구현되어 있는지를 봅니다. 특히 오프셋 4종과 ISR은 CCDAK Fundamentals 도메인에서 가장 자주 출제되는 지점이면서, 실무에서 데이터 유실 사고의 원인이 거의 항상 이 둘 중 하나에 걸립니다. 나머지 아홉 개 장이 모두 이 장의 용어를 전제로 쓰였습니다.
학습 목표
- 브로커·컨트롤러·클라이언트가 각각 무엇을 책임지는지 KRaft 기준으로 구분할 수 있습니다.
- 토픽 → 파티션 → 세그먼트 → 레코드 배치 → 레코드의 4단 계층을 디스크 파일 이름까지 설명할 수 있습니다.
log-start-offset· committed offset · high watermark · LEO의 차이를 구분하고, 컨슈머가 왜 high watermark를 넘어 읽지 못하는지 설명할 수 있습니다.- ISR이 축소·확대되는 조건과
min.insync.replicas가acks=all과만 상호작용하는 이유를 설명할 수 있습니다. unclean.leader.election.enable이 가용성과 일관성 중 무엇을 포기하는지 판단할 수 있습니다.- 레코드가 key · value · timestamp · headers로 구성되고, 배치 단위로 저장·압축된다는 점을 설명할 수 있습니다.
클러스터 구성 요소 — 브로커, 컨트롤러, 클라이언트
Kafka 클러스터는 서버 프로세스 여러 개와 그것에 붙는
클라이언트로 이루어집니다. Kafka 4.x에서 서버 프로세스가 맡는 역할은
process.roles 설정으로 정해지고 값은 broker,
controller, 또는 둘 다인 broker,controller입니다.
역할 조합과 쿼럼 구성은 3장 KRaft와 클러스터 메타데이터에서
따로 다루고, 여기서는 "누가 무엇을 책임지는가"만 정리합니다.
브로커 — 데이터 평면
브로커는 파티션의 로그를 디스크에 보관하고, 클라이언트의 읽기·쓰기 요청을 처리합니다.
저장 위치는 log.dirs로 지정하며 기본값은 /tmp/kafka-logs입니다.
기본값이 /tmp 아래라는 점은 실무에서 반드시 고쳐야 하는 부분입니다.
브로커는 자신이 리더인 파티션에 대해서만 쓰기를 받습니다. 팔로워 레플리카를 가진 파티션에 대해서는 리더로부터 데이터를 fetch해 복제합니다. 하나의 브로커는 어떤 파티션의 리더이면서 동시에 다른 파티션의 팔로워일 수 있고, 실제 운영 클러스터에서는 항상 그렇습니다.
컨트롤러 — 메타데이터 평면
컨트롤러는 클러스터 메타데이터를 관리합니다. 토픽·파티션 구성, 레플리카 배치, ISR 집합, 리더가 누구인지, 브로커가 살아 있는지가 모두 메타데이터입니다. 컨트롤러들은 쿼럼(quorum)을 이루고 그중 한 대가 활성(active) 컨트롤러, 나머지는 핫 스탠바이로 대기합니다. 공식 문서는 컨트롤러를 보통 3대 또는 5대 선택한다고 안내하며, 가용성을 유지하려면 과반이 살아 있어야 합니다.
브로커는 컨트롤러에게 주기적으로 하트비트를 보냅니다
(broker.heartbeat.interval.ms 기본 2000ms). 컨트롤러가
broker.session.timeout.ms(기본 9000ms) 안에 하트비트를 받지 못하면
그 브로커를 fenced 상태로 처리하고, 그 브로커가 리더였던 파티션의 리더를 다른 레플리카로 옮깁니다.
클라이언트 — Producer, Consumer, Admin
| 클라이언트 | 하는 일 | 접속 대상 |
|---|---|---|
| Producer | 레코드를 파티션 리더에게 보냄 | 브로커 (bootstrap.servers) |
| Consumer | 파티션에서 레코드를 읽고 오프셋을 커밋 | 브로커 (bootstrap.servers) |
| Admin | 토픽 생성·삭제, 설정 변경, 그룹 조회 | 브로커, 필요 시 컨트롤러 (--bootstrap-controller) |
클라이언트는 메타데이터로 리더를 찾습니다
클라이언트가 처음 접속할 때 쓰는 것은 bootstrap.servers입니다.
여기 적힌 브로커 중 하나에 붙어 메타데이터 응답을 받고,
그 응답에 담긴 "어느 파티션의 리더가 어느 브로커인가"를 근거로 이후 요청을 직접 리더에게 보냅니다.
메타데이터는 metadata.max.age.ms(기본 300000ms) 주기로 갱신되고,
리더가 바뀌었다는 에러를 받으면 즉시 다시 가져옵니다.
토픽 → 파티션 → 세그먼트 → 레코드
Kafka의 저장 구조는 4단 중첩입니다. 위에서부터 토픽은 논리적 이름, 파티션은 순서가 보장되는 append-only 로그, 세그먼트는 그 로그를 잘라 놓은 실제 파일, 레코드 배치는 세그먼트 안에 기록되는 물리 단위이고 그 안에 레코드가 하나 이상 들어갑니다.
.log · .index · .timeindex 파일까지
파티션은 디렉터리 하나입니다
공식 문서는 파티션이 두 개인 my-topic 토픽이
my-topic-0과 my-topic-1 두 디렉터리로 존재한다고 설명합니다.
디렉터리 이름은 {토픽명}-{파티션번호}입니다.
각 디렉터리 안에는 세그먼트 파일들이 들어 있습니다.
log.dirs 아래의 실제 구조 (파티션 24개 토픽의 일부)/var/lib/kafka/data/
├── orders-0/
│ ├── 00000000000000000000.log ← 세그먼트 데이터 (봉인됨)
│ ├── 00000000000000000000.index ← 오프셋 → 파일 위치 인덱스
│ ├── 00000000000000000000.timeindex ← 타임스탬프 → 오프셋 인덱스
│ ├── 00000000000000538211.log ← 활성 세그먼트 (지금 append 중)
│ ├── 00000000000000538211.index
│ ├── 00000000000000538211.timeindex
│ └── leader-epoch-checkpoint
├── orders-1/
│ └── ...
└── meta.properties ← cluster.id, node.id, directory.id
세그먼트 파일 이름은 시작 오프셋입니다
공식 문서는 각 로그 파일이 그 파일이 담고 있는 첫 메시지의 오프셋으로
이름 붙는다고 명시합니다. 그래서 처음 만들어지는 파일이
00000000000000000000.log입니다. 오프셋으로 읽기 요청이 오면 브로커는
(1) 파일 이름을 이진 탐색해 해당 세그먼트를 찾고, (2) .index로 파일 안 위치를 좁힌 뒤
(3) 그 위치부터 읽습니다.
세그먼트가 롤링되는 기준은 크기와 시간입니다. log.segment.bytes의 기본값은
1073741824(1GiB)입니다. 세그먼트 단위 구조가 리텐션·컴팩션의 동작을
어떻게 좌우하는지는 7장 스토리지·리텐션·컴팩션에서 자세히 다룹니다.
활성 세그먼트는 하나뿐입니다
파티션마다 지금 append되고 있는 세그먼트가 정확히 하나 있고 이를 활성 세그먼트(active segment)라고 부릅니다. 나머지는 봉인된 상태입니다. 활성 세그먼트는 삭제와 컴팩션의 대상이 아닙니다. "리텐션을 1시간으로 줄였는데 디스크가 안 비워진다"는 신고의 대부분이 이 사실 때문입니다.
오프셋 4종 — 이 장에서 가장 중요한 절
"오프셋"이라는 한 단어가 실제로는 최소 네 가지 서로 다른 지점을 가리킵니다. 시험에서도, 장애 대응에서도 이 네 개를 섞어 쓰면 결론이 완전히 달라집니다. 아래 다이어그램의 슬라이더로 복제 진행과 컨슈머 커밋을 움직여 보면 네 지점이 어떻게 벌어지는지 보입니다.
log-start-offset · committed offset · high watermark · LEO를
하나의 파티션 로그 위에 배치하고, high watermark가 ISR 내 최소 LEO로 결정되는 과정을 표시
LEO와 high watermark
LEO(log end offset)는 로그에 다음에 쓰일 오프셋, 즉 마지막 레코드의 오프셋 + 1입니다. 리더의 LEO는 프로듀서가 쓴 순간 올라갑니다. 팔로워도 각자 자신의 LEO를 가지며, 복제가 뒤처지면 리더보다 작습니다.
high watermark는 컨슈머가 읽을 수 있는 상한입니다. 공식 문서는 "committed된 메시지만 컨슈머에게 주어진다"고 명시합니다. 즉 ISR 안의 모든 레플리카가 복제를 마친 지점까지만 노출됩니다. 리더가 방금 기록했지만 아직 복제되지 않은 구간(high watermark ~ LEO)은 컨슈머에게 존재하지 않는 것과 같습니다.
이 설계 덕분에 리더가 죽어도 컨슈머가 이미 읽은 데이터가 사라지는 일이 없습니다. 컨슈머가 읽은 데이터는 정의상 ISR 전체에 복제된 데이터이기 때문입니다.
committed offset — 컨슈머 쪽 진행 상황
committed offset은 컨슈머 그룹이 "여기까지 처리했다"고 기록해 둔 위치입니다.
브로커의 내부 토픽 __consumer_offsets에 저장되며,
로그 자체와는 아무 관계가 없습니다. 같은 파티션에 대해 컨슈머 그룹이 세 개면
committed offset도 세 개입니다.
여기서 용어 충돌이 하나 있습니다. Kafka는 복제 관점의 "committed message" (ISR 전체가 복제를 마친 메시지)와 컨슈머 관점의 "committed offset"을 같은 단어로 부릅니다. 공식 문서도 컨슈머 쪽을 "혼란스럽게도 committed offset이라 불린다"고 표현합니다. 문장에서 주체가 브로커인지 컨슈머 그룹인지를 먼저 확인하세요.
log-start-offset — 남아 있는 가장 오래된 지점
log-start-offset은 파티션에 아직 남아 있는 가장 작은 오프셋입니다.
리텐션으로 세그먼트가 삭제되면 이 값이 올라갑니다. 항상 0이 아니라는 점이 중요합니다.
컨슈머의 committed offset이 log-start-offset보다 작아지면 —
예를 들어 컨슈머가 3일 멈춰 있었고 리텐션이 1일이라면 — 그 오프셋은 더 이상 존재하지 않습니다.
이때 auto.offset.reset이 발동하고, 기본값이 latest이므로
중간 데이터를 조용히 건너뜁니다. 이 사고의 전말은
케이스 1에 있습니다.
4종 비교표
| 지표 | 의미 | 소유 주체 | 언제 움직이는가 | 조회 방법 |
|---|---|---|---|---|
log-start-offset |
남아 있는 가장 작은 오프셋 | 파티션 (브로커) | 리텐션으로 세그먼트가 삭제될 때 | kafka-get-offsets.sh --time -2 / JMX kafka.log:type=Log,name=LogStartOffset |
| committed offset | 컨슈머 그룹이 커밋한 위치 | 컨슈머 그룹 | 그룹이 커밋할 때 (그룹마다 별개) | kafka-consumer-groups.sh --describe |
| high watermark | 컨슈머가 읽을 수 있는 상한 | 파티션 리더 | ISR 전체가 복제를 마칠 때 | kafka-get-offsets.sh --time -1 (클라이언트 요청은 read_uncommitted 기준으로 high watermark를 반환) |
| LEO (log end offset) | 다음에 쓰일 오프셋 | 각 레플리카 (리더·팔로워 각각) | 레코드가 append될 때 | JMX kafka.log:type=Log,name=LogEndOffset — 클라이언트 API로는 노출되지 않습니다 |
복제와 ISR
복제 단위는 토픽이 아니라 토픽-파티션입니다. 파티션마다 리더 하나와 팔로워 0개 이상이 있고, 모든 읽기·쓰기는 리더가 처리합니다. 팔로워는 리더에게 fetch 요청을 보내 데이터를 따라옵니다 — 즉 복제도 결국 하나의 컨슈머 동작입니다.
replica.lag.time.max.ms를
넘겨 뒤처졌을 때 ISR이 축소되고 min.insync.replicas에 미달하는 과정
리더와 팔로워, 그리고 replicas 순서
kafka-topics.sh --describe는 파티션마다
Leader, Replicas, Isr 세 값을 보여줍니다.
$ bin/kafka-topics.sh --bootstrap-server localhost:9092 --topic foo --describe
Topic:foo PartitionCount:1 ReplicationFactor:3 Configs:
Topic: foo Partition: 0 Leader: 5 Replicas: 5,6,7 Isr: 5,6,7
Replicas는 할당된 레플리카 전체이고
Isr은 그중 현재 따라오고 있는 것들입니다.
Replicas의 첫 번째 항목이 preferred replica이며,
정상 상황에서는 그것이 리더가 됩니다(아래 preferred 리더 선출).
ISR에서 빠지는 조건
공식 문서는 in-sync 판정을 두 조건으로 설명합니다. 하나는 세션이 살아 있는가,
다른 하나는 리더의 로그 끝을 제한 시간 안에 따라잡는가입니다.
후자를 제어하는 것이 replica.lag.time.max.ms이고 기본값은
30000(30초)입니다. 이 시간 안에 리더의 로그 끝까지 따라오지 못한 레플리카는
리더가 ISR에서 제거합니다.
ISR이 바뀌면 그 사실이 클러스터 메타데이터에 기록됩니다.
그래서 ISR 축소·확대가 잦으면 메타데이터 쓰기가 늘고, 컨트롤러 부하로 이어집니다.
운영에서 IsrShrinksPerSec를 감시하는 이유입니다
(메트릭 치트시트).
Kafka는 과반 투표(majority vote) 방식을 쓰지 않고 ISR 방식을 씁니다. 공식 문서의 표현대로, 쓰기는 ISR 안의 모든 레플리카가 받아야 committed로 간주되고, ISR 멤버만 리더가 될 수 있습니다. 레플리카가 f+1개면 f대 장애까지 committed 메시지를 잃지 않습니다.
min.insync.replicas는 acks=all과만 맞물립니다
min.insync.replicas는 프로듀서가 acks=all(또는 -1)로
보냈을 때 쓰기가 성공하기 위해 필요한 최소 ISR 크기입니다(리더 포함).
기본값은 1이며 브로커 설정과 토픽 설정 양쪽에 존재합니다.
조건을 만족하지 못하면 프로듀서는 NotEnoughReplicasException 또는
NotEnoughReplicasAfterAppendException을 받습니다. 프로토콜 에러 코드로는
NOT_ENOUGH_REPLICAS(19)와 NOT_ENOUGH_REPLICAS_AFTER_APPEND(20)이며
둘 다 재시도 가능한 에러입니다.
공식 문서가 권장하는 조합은 RF 3 · min.insync.replicas 2 ·
acks=all입니다. 이 조합에서 각 값이 무엇을 보장하는지 정리하면 이렇습니다.
| 현재 ISR | min.insync.replicas=1 |
min.insync.replicas=2 |
min.insync.replicas=3 |
|---|---|---|---|
| 3 (정상) | 성공 — 3대 모두 ack | 성공 — 3대 모두 ack | 성공 — 3대 모두 ack |
| 2 (1대 이탈) | 성공 — 2대 ack | 성공 — 2대 ack | 거부 — NotEnoughReplicas |
| 1 (리더만 남음) | 성공 — 유실 위험 노출 | 거부 — NotEnoughReplicas |
거부 — NotEnoughReplicas |
unclean leader election — 가용성과 일관성의 교환
ISR에 있던 모든 레플리카가 죽으면 선택지는 둘뿐입니다.
- 기다린다 — ISR에 있던 레플리카가 돌아올 때까지 파티션은 사용 불가.
- 뒤처진 레플리카를 리더로 올린다 — 파티션은 살아나지만 그 레플리카에 없던 committed 메시지는 영구 손실.
이 선택을 정하는 것이 unclean.leader.election.enable이고
기본값은 false입니다. 즉 Kafka는 기본적으로 1번,
일관성을 선택합니다. 브로커 설정과 토픽 설정 모두에 존재합니다.
Eligible Leader Replicas (KIP-966)
Kafka 4.0부터 ELR(Eligible Leader Replicas)가 도입되었습니다.
"strict min ISR" 규칙 덕분에 ISR 크기가 min.insync.replicas보다 작으면
high watermark가 전진하지 못합니다. 그렇다면 ISR에서 빠졌지만
데이터 유실 없이 리더가 될 수 있는 레플리카가 존재하게 되고,
KRaft 컨트롤러가 그것들을 PartitionRecord의 ELR 필드에 기록해 둡니다.
공식 문서가 밝히는 리더 선출 순서입니다.
- ISR이 비어 있지 않으면 그중에서 고른다.
- ELR이 비어 있지 않으면 fenced되지 않은 것을 고른다.
- 마지막으로 알려진 리더가 unfenced면 그것을 고른다.
파티션 할당과 리더 선출
레플리카 배치와 랙 인식
토픽을 만들면 컨트롤러가 각 파티션의 레플리카를 브로커에 배분합니다. 목표는 두 가지입니다 — 레플리카가 고르게 퍼지는 것과 리더 수가 브로커별로 균등한 것입니다.
브로커에 broker.rack을 지정하면 랙 인식(rack awareness)이 켜집니다.
공식 문서는 이때 하나의 파티션이 min(랙 수, 복제 계수)개의 서로 다른 랙에
걸치도록 배치된다고 명시합니다. AWS 가용 영역 같은 그룹에도 그대로 적용됩니다.
preferred 리더와 자동 리밸런싱
브로커가 죽으면 그 브로커가 리더였던 파티션들의 리더십이 다른 레플리카로 넘어갑니다. 그 브로커가 돌아와도 자동으로 리더를 되찾지는 않습니다 — 모든 파티션에서 팔로워로 복귀하므로 클라이언트 트래픽을 전혀 받지 않습니다. 클러스터가 불균형해집니다.
그래서 Kafka는 preferred replica 개념을 씁니다.
레플리카 목록이 5,6,7이면 목록의 앞에 있는 5번이 preferred 리더입니다.
auto.leader.rebalance.enable의 기본값이 true이므로
클러스터가 주기적으로 리더십을 preferred 레플리카로 되돌립니다.
점검 주기는 leader.imbalance.check.interval.seconds(기본 300초)입니다.
수동으로 즉시 실행할 수도 있습니다.
$ bin/kafka-leader-election.sh --bootstrap-server localhost:9092 \
--election-type preferred --all-topic-partitions
controlled shutdown
브로커를 정상 종료하면 (1) 로그를 디스크에 동기화하고
(2) 자신이 리더인 파티션의 리더십을 다른 레플리카로 넘긴 뒤 종료합니다.
controlled.shutdown.enable이 이 동작을 켭니다.
단 공식 문서는 해당 브로커의 모든 파티션에 살아 있는 다른 레플리카가 있을 때만
성공한다고 명시합니다. RF 1 토픽이 하나라도 있으면 controlled shutdown이 실패합니다.
레코드 구조 — key, value, timestamp, headers
레코드(record)는 Kafka가 저장하는 최소 단위이지만, 디스크에 혼자 저장되는 일은 없습니다. 공식 문서의 표현대로 "메시지는 항상 배치로 기록"되고, 그 배치를 레코드 배치(record batch)라고 부릅니다. 레코드가 하나뿐인 배치도 배치입니다.
baseOffset · producerId ·
baseSequence 등)와 그 안의 개별 레코드(key · value · timestamp delta · headers)
레코드가 담는 것
| 필드 | null 허용 | 역할 |
|---|---|---|
| key | 가능 | 파티션 결정의 근거이자 컴팩션의 기준. 없으면 순서 보장과 컴팩션의 근거가 사라집니다 |
| value | 가능 | 실제 페이로드. null value는 컴팩션 토픽에서 tombstone(삭제 표시)입니다 |
| timestamp | 불가 | 배치의 baseTimestamp에 대한 delta로 저장. 리텐션과 시간 기반 조회의 기준 |
| headers | value만 가능 | 키·값 쌍의 목록. 헤더 키는 non-null 보장, 헤더 값은 null 가능. 순서가 보존됩니다 |
| offset | 불가 | 브로커가 배치의 baseOffset + offsetDelta로 부여 |
배치 헤더가 왜 중요한가
레코드 배치 헤더에는 baseOffset, partitionLeaderEpoch,
magic(현재 값 2), crc, attributes,
producerId, producerEpoch, baseSequence 등이 들어갑니다.
여기서 세 가지가 다른 장과 직접 연결됩니다.
producerId·producerEpoch·baseSequence— 멱등성 프로듀서의 중복 판별 근거입니다. 브로커는 들어온 배치의 첫·마지막 시퀀스 번호를 그 프로듀서의 마지막 값과 비교해 중복을 판정합니다 (4장 멱등성 프로듀서).attributes의 압축 비트 — 압축은 레코드 단위가 아니라 배치 단위로 적용됩니다. 그래서 배치가 클수록 압축률이 좋아집니다 (4장 압축).isTransactional·isControlBatch비트 — 트랜잭션과read_committed의 동작 근거입니다. 컨트롤 배치는 abort/commit 마커를 담고 애플리케이션에 노출되지 않습니다 (6장 전달 보장과 트랜잭션).
이 장에서 나온 설정
기본값은 Apache Kafka 4.3 공식 문서(Broker Configs / Topic Configs) 기준입니다. 전체 목록은 설정 치트시트를 보세요.
| 설정 | 범위 | 기본값 | 설명 | 튜닝 포인트 |
|---|---|---|---|---|
default.replication.factor |
브로커 | 1 | 자동 생성 토픽의 복제 계수 | 1이면 브로커 한 대 손실로 데이터가 사라집니다. 프로덕션 토픽은 명시 생성하고 3을 쓰세요 |
num.partitions |
브로커 | 1 | 자동 생성 토픽의 파티션 수 | 소비 병렬성의 상한. 나중에 늘릴 수는 있지만 줄일 수 없고, 늘리면 키 분배가 바뀝니다 |
min.insync.replicas |
브로커 · 토픽 | 1 | acks=all 쓰기가 성공하기 위한 최소 ISR 크기(리더 포함) |
RF 3이면 2를 권장. RF와 같게 두면 브로커 1대 재시작에 쓰기가 멈춥니다 |
unclean.leader.election.enable |
브로커 · 토픽 | false | ISR 밖 레플리카를 리더로 올릴지 여부 | true는 가용성을 얻고 유실을 허용하는 선택입니다. 기본값 유지가 안전합니다 |
replica.lag.time.max.ms |
브로커 | 30000 | 이 시간 안에 리더의 로그 끝을 따라오지 못한 레플리카를 ISR에서 제거 | 너무 짧으면 ISR이 자주 흔들려 메타데이터 쓰기가 늘고, 너무 길면 뒤처진 레플리카가 ISR에 남습니다 |
num.replica.fetchers |
브로커 | 1 | 팔로워가 복제에 쓰는 fetcher 스레드 수 | 파티션이 많거나 처리량이 높아 복제가 뒤처질 때 늘립니다 |
replica.fetch.max.bytes |
브로커 | 1048576 | 파티션당 복제 fetch 응답 최대 크기 | 절대 상한이 아닙니다 — 첫 배치가 이 값보다 커도 그대로 반환해 복제 진행을 보장합니다(This is not an absolute maximum). 값이 작으면 배치가 잘게 쪼개져 복제 처리량이 떨어질 뿐 멈추지는 않습니다 |
offsets.topic.replication.factor |
브로커 | 3 | __consumer_offsets의 복제 계수 |
브로커가 3대 미만인 개발 클러스터에서는 낮춰야 토픽이 생성됩니다 |
auto.leader.rebalance.enable |
브로커 | true | 리더십을 preferred 레플리카로 자동 복구 | 끄면 브로커 재시작 후 리더가 편중된 상태가 유지됩니다 |
broker.rack |
브로커 | null | 브로커가 속한 랙/가용 영역 식별자 | 지정하면 레플리카가 min(랙 수, RF)개 랙에 분산됩니다. 랙별 브로커 수를 같게 두세요 |
log.segment.bytes |
브로커 | 1073741824 | 세그먼트 파일 롤링 크기 | 삭제는 세그먼트 단위이므로 이 값이 실제 보관량을 좌우합니다(7장) |
message.max.bytes |
브로커 | 1048588 | 허용되는 최대 레코드 배치 크기 (압축 후) | 토픽의 max.message.bytes(기본 동일)가 오버라이드합니다 |
broker.session.timeout.ms |
브로커 | 9000 | 컨트롤러가 브로커를 죽었다고 판정하는 시간 | broker.heartbeat.interval.ms(기본 2000)와 함께 조정합니다 |
내구성을 최우선으로 할 때의 표준 조합은 다음과 같습니다. 프로듀서 쪽 설정의 의미는 4장에서 다룹니다.
# 토픽 (kafka-topics.sh --create --replication-factor 3 ... 로 생성)
min.insync.replicas=2
unclean.leader.election.enable=false
# 프로듀서 — 아래 세 개는 4.x 기본값이므로 명시하지 않아도 동일합니다
acks=all
enable.idempotence=true
max.in.flight.requests.per.connection=5
흔한 오해
시험 포인트 정리
확인 문제
단일 선택 · 복수 선택 · 연결형 · 순서 배열이 섞여 있습니다. 키보드만으로 완주할 수 있습니다 — 숫자키로 선택, Enter로 제출입니다.
이어서 볼 곳
-
3장 · KRaft와 클러스터 메타데이터
컨트롤러 쿼럼,
__cluster_metadata, 노드 롤과 부트스트랩 절차. -
4장 · Producer 심화
acks와min.insync.replicas의 결합, 멱등성, 배치와 순서 보장. - 7장 · 스토리지·리텐션·컴팩션 세그먼트 단위 삭제, 컴팩션과 tombstone, 디스크 산정.
- 케이스 6 · minISR을 RF와 같게 두었을 때 롤링 재시작이 곧 장애가 되는 조합. 즉시 조치와 근본 해결.
- 케이스 3 · 파티션이 offline로 남았다 unclean leader election과 ISR 소실. 복구 판단 기준.
- 설정 치트시트 브로커·토픽·프로듀서·컨슈머 설정 전체 표와 3가지 프리셋.
공식 문서 출처
이 장의 설정 기본값과 동작 서술은 모두 아래에서 확인했습니다 (Apache Kafka 4.3 문서 기준).
- Broker Configs —
min.insync.replicas,unclean.leader.election.enable,replica.lag.time.max.ms,num.replica.fetchers,replica.fetch.max.bytes,auto.leader.rebalance.enable,broker.session.timeout.ms,log.dirs,log.segment.bytes - Topic Configs — 토픽 레벨
min.insync.replicas,max.message.bytes,unclean.leader.election.enable - Design — Replication — committed message 정의, ISR 방식과 과반 투표의 비교, high watermark 노출 조건
- Design — Unclean Leader Election — 가용성과 일관성의 교환, 기본 동작
- Eligible Leader Replicas (KIP-966) — ELR 리더 선출 순서,
eligible.leader.replicas.version, 4.1부터 신규 클러스터 기본 활성 - KIP-966 Part 1 — ELR 설계 (4.3 문서가 인용하는 링크)
- Implementation — Log — 파티션 디렉터리와 세그먼트 파일 이름 규칙, 오프셋 기반 조회, 세그먼트 단위 삭제
- Implementation — Record Batch / Record — 배치 헤더 필드, 압축 비트, 컨트롤 배치, 헤더 null 규칙과 순서 보존
- Operations — Balancing Leadership — preferred replica,
kafka-leader-election.sh - Operations — Balancing Replicas Across Racks —
broker.rack,min(랙 수, RF)배치 - Operations — Hardware and OS —
vm.max_map_count와 세그먼트당 맵 영역 2개 - KRaft — Process Roles / Controllers — 컨트롤러 3대 또는 5대 권장, 과반 생존 요건
- Upgrading Apache Kafka — 4.x에서 제거된 설정 목록,
--bootstrap-server형식 제한 - Protocol — Error Codes —
NOT_ENOUGH_REPLICAS(19),NOT_ENOUGH_REPLICAS_AFTER_APPEND(20) - Monitoring —
UnderReplicatedPartitions,UnderMinIsrPartitionCount,OfflinePartitionsCount