CCAAK · 섹션 1
Kafka Fundamentals
나머지 6개 섹션이 모두 이 섹션 위에 서 있습니다. 개발자 시험에서는 "이 코드가 무엇을 반환하는가"를 묻지만,
운영자 시험에서는 같은 개념을 "이 출력이 무엇을 뜻하는가"로 묻습니다.
kafka-topics.sh --describe의 한 줄을 보고 리더·ISR·복제 상태를 즉시 읽어 내는 것이 이 섹션의 목표입니다.
학습 목표
- KRaft 클러스터의 구성 요소(브로커·컨트롤러 쿼럼·
__cluster_metadata)와 역할을 설명할 수 있습니다. - ISR이 축소·확장되는 조건과
replica.lag.time.max.ms의 역할을 말할 수 있습니다. - high watermark가 어떻게 결정되는지, 왜 컨슈머가 그 위를 못 읽는지 설명할 수 있습니다.
- 리더 선출 종류(정상·preferred·unclean)를 구분하고 각각의 대가를 말할 수 있습니다.
--describe계열 CLI 출력에서 이상 신호를 찾아낼 수 있습니다.
이 도메인이 묻는 것
이 섹션의 문항은 크게 세 갈래입니다. 첫째, 구조 판독 — CLI 출력이나 설정 조각을 주고 무엇이 잘못되었는지 찾게 합니다. 둘째, 보장의 경계 — 어떤 조건에서 데이터가 남고 어떤 조건에서 사라지는지 묻습니다. 셋째, 용어의 정확성 — leader와 preferred leader, replica와 ISR, LEO와 high watermark를 구분하는지 matching 유형으로 확인합니다.
| 질문 형태 | 실제로 확인하는 것 |
|---|---|
"이 토픽의 Isr가 Replicas보다 짧다. 무슨 일인가?" |
ISR 축소 조건과 그것이 쓰기 가능성에 미치는 영향 |
| "브로커 1대를 잃었을 때 데이터가 유실되는 조건은?" | RF · min.insync.replicas · acks 3자 관계 |
| "파티션 수를 줄이려면?" | 줄일 수 없다는 사실과 그 이유 |
| "컨트롤러가 하는 일을 고르시오" | KRaft에서 메타데이터가 어디에 저장되고 누가 관리하는가 |
| "각 오프셋 지표를 의미와 연결하시오" (matching) | log-start-offset · committed offset · high watermark · LEO 구분 |
핵심 개념 요약 — 운영자 관점
클러스터의 모양
Kafka 4.x의 모든 서버는 process.roles로 역할을 선언합니다.
broker는 데이터를 저장·서빙하고, controller는 메타데이터 쿼럼에 참여합니다.
컨트롤러 중 하나가 active controller이고 나머지는 hot standby입니다.
클러스터 메타데이터는 __cluster_metadata 파티션에 Raft로 복제됩니다.
복제와 ISR — 이 섹션의 중심
복제 단위는 토픽이 아니라 토픽-파티션입니다. 각 파티션에는 리더 하나와 팔로워가 있고, 쓰기와 읽기는 모두 리더로 갑니다. 팔로워는 리더에서 fetch해 따라잡습니다. ISR(in-sync replicas)은 "충분히 따라잡은" 레플리카 집합입니다.
팔로워가 replica.lag.time.max.ms(기본 30000ms) 안에
리더의 로그 끝까지 따라잡은 fetch 요청을 보내지 못하면 ISR에서 빠집니다.
다시 따라잡으면 되돌아옵니다. 이 축소·확장은 각각
IsrShrinksPerSec · IsrExpandsPerSec 메트릭으로 관측됩니다.
min.insync.replicas를 밑도는 과정
리더 선출 3종
| 종류 | 언제 일어나는가 | 대가 |
|---|---|---|
| 정상 선출 | 리더 브로커가 내려가면 컨트롤러가 ISR 안에서 새 리더를 뽑습니다. | 없음. 커밋된 데이터는 보존됩니다. |
| preferred 선출 | 레플리카 목록의 첫 번째 브로커로 리더를 되돌립니다.
auto.leader.rebalance.enable=true(기본)이면 자동, 아니면
kafka-leader-election.sh --election-type preferred로 수동. |
선출 순간 짧은 리더 없음 구간. 대신 리더가 한쪽 브로커에 쏠리는 것을 막습니다. |
| unclean 선출 | ISR이 비어 있을 때 ISR 밖 레플리카를 리더로 올립니다.
unclean.leader.election.enable=true일 때만 가능합니다(기본 false). |
데이터 유실. 가용성을 데이터로 사는 거래입니다.
UncleanLeaderElectionsPerSec는 항상 0이어야 합니다. |
로그의 물리 구조
각 파티션은 log.dirs 아래 {토픽명}-{파티션번호} 디렉터리 하나에 들어갑니다.
여러 디렉터리를 지정하면 파티션이 라운드로빈으로 배치되며, 하나의 파티션이 두 디렉터리에 걸치지는 않습니다.
디렉터리 안은 세그먼트 파일들로 나뉘고, 삭제는 세그먼트 단위로만 일어납니다.
그래서 retention.ms를 짧게 줘도 segment.bytes/segment.ms가 크면
실제 보관량이 예상보다 오래 남습니다. 자세한 계산은 7장에 있습니다.
필수 CLI 명령어
이 섹션에서 손에 익어야 하는 명령은 많지 않습니다. 대신 출력 판독이 중요합니다. 전체 목록은 CLI 치트시트에 있습니다.
# 토픽 생성 (프로덕션에서는 항상 명시 생성)
bin/kafka-topics.sh --bootstrap-server localhost:9092 --create --topic orders \
--partitions 20 --replication-factor 3 --config min.insync.replicas=2
# 리더 · 레플리카 · ISR 판독
bin/kafka-topics.sh --bootstrap-server localhost:9092 --describe --topic orders
# 목록
bin/kafka-topics.sh --bootstrap-server localhost:9092 --list
# 파티션 증설 (감소는 불가)
bin/kafka-topics.sh --bootstrap-server localhost:9092 --alter --topic orders --partitions 40
# 쿼럼 요약: 리더, 에폭, high watermark, voter/observer 목록
bin/kafka-metadata-quorum.sh --bootstrap-server localhost:9092 describe --status
# 각 노드의 복제 진행 (Lag, LastFetchTimestamp, LastCaughtUpTimestamp)
bin/kafka-metadata-quorum.sh --bootstrap-server localhost:9092 describe --replication
# 브로커가 다 죽어 있으면 컨트롤러로 직접
bin/kafka-metadata-quorum.sh --bootstrap-controller localhost:9093 describe --status
# preferred 레플리카로 리더 되돌리기 (전체 파티션)
bin/kafka-leader-election.sh --bootstrap-server localhost:9092 \
--election-type preferred --all-topic-partitions
# CURRENT-OFFSET / LOG-END-OFFSET / LAG
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group my-group
# 그룹 상태 요약 (코디네이터, 할당 전략, 멤버 수)
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group my-group --state
# 그룹 종류를 한눈에 (Consumer / Share / Streams)
bin/kafka-groups.sh --bootstrap-server localhost:9092 --list
반드시 외워야 할 설정값
| 설정 | 기본값 | 의미 | 운영 포인트 |
|---|---|---|---|
replica.lag.time.max.msbroker |
30000 | 이 시간 안에 리더를 따라잡지 못한 팔로워를 ISR에서 제거 | 낮추면 ISR이 예민해져 잦은 축소·확장이 생깁니다. read-only라 재시작이 필요합니다. |
min.insync.replicasbroker topic |
1 | acks=all 쓰기가 성공하기 위해 필요한 최소 ISR 크기 |
기본값 1이면 acks=all도 사실상 리더 1대만 확인합니다. RF=3이면 2가 표준입니다. |
unclean.leader.election.enablebroker topic |
false | ISR 밖 레플리카를 리더로 올릴지 여부 | true로 바꾸면 유실을 허용합니다. 켜야 한다면 해당 토픽만 한시적으로 켜세요. |
default.replication.factorbroker |
1 | 자동 생성 토픽의 복제 계수 | auto.create.topics.enable=true(기본)와 겹치면 오타 토픽이 RF=1로 조용히 생깁니다. |
num.partitionsbroker |
1 | 자동 생성 토픽의 파티션 수 | 소비 병렬성 상한이 1이 됩니다. |
auto.create.topics.enablebroker |
true | 없는 토픽에 요청이 오면 자동 생성 | 운영 클러스터에서는 끄는 편이 안전합니다. |
controlled.shutdown.enablebroker |
true | 종료 전에 리더십을 다른 레플리카로 넘김 | RF>1이고 살아 있는 레플리카가 있어야 성공합니다. RF=1 토픽이 있으면 실패합니다. |
auto.leader.rebalance.enablebroker |
true | preferred 레플리카로 리더를 자동 복구 | 점검 주기는 leader.imbalance.check.interval.seconds(기본 300)입니다. |
offsets.topic.replication.factorbroker |
3 | __consumer_offsets의 복제 계수 |
브로커가 3대 미만인 환경에서는 토픽 생성 자체가 실패합니다. 실습 클러스터에서 자주 걸립니다. |
transaction.state.log.replication.factorbroker |
3 | __transaction_state의 복제 계수 |
짝인 transaction.state.log.min.isr 기본값은 2입니다. |
log.segment.bytesbroker |
1073741824 (1 GiB) |
세그먼트 롤 크기 | 삭제는 세그먼트 단위라, 이 값이 크면 리텐션이 실제보다 늦게 듣습니다. |
segment.mstopic |
604800000 (7일) |
시간 기준 세그먼트 롤 | 저트래픽 토픽은 크기 기준으로는 안 굴러가므로 이 값이 실질 기준이 됩니다. |
retention.mstopic |
604800000 (7일) |
delete 정책의 보관 시간 |
장애 복구에 며칠이 필요한 파이프라인이면 먼저 확인할 값입니다. |
message.max.bytesbroker |
1048588 | 허용되는 최대 레코드 배치 크기(압축 후) | "1MB"가 아니라 정확히 1048588입니다. 토픽 레벨 max.message.bytes가 오버라이드합니다. |
장애 시나리오와 대응
시나리오 1 — ISR이 계속 줄었다 늘어난다
| 단계 | 할 일 |
|---|---|
| 증상 | IsrShrinksPerSec와 IsrExpandsPerSec가 0이 아닌 값으로 반복. UnderReplicatedPartitions가 0과 양수를 왕복. |
| 1 | 어느 브로커가 빠지는지 특정합니다. kafka-topics.sh --describe에서 Isr에 빠진 노드 ID를 확인합니다. |
| 2 | 그 브로커의 디스크 I/O와 네트워크를 봅니다. 팔로워 fetch가 느려지는 원인은 대개 디스크 포화입니다. |
| 3 | RequestHandlerAvgIdlePercent가 낮으면 요청 처리 스레드가 부족합니다. num.io.threads(기본 8)를 검토합니다. |
| 4 | 진행 중인 파티션 재할당이 있는지 확인합니다. 스로틀이 해제되지 않은 상태면 정상 복제까지 조여집니다. |
| 5 | 근본 원인을 못 찾았는데 급하다면 num.replica.fetchers(기본 1)를 늘려 팔로워 fetch 병렬성을 올립니다. |
시나리오 2 — 브로커를 정상 종료했는데 종료가 끝나지 않는다
controlled.shutdown.enable=true(기본)이면 브로커는 종료 전에 자기 리더 파티션을 넘기려 시도합니다.- 그런데 RF=1 파티션이 하나라도 있으면 넘길 대상이 없어 controlled shutdown이 실패합니다.
- 먼저
kafka-topics.sh --describe로 RF=1 토픽을 찾습니다. 자동 생성으로 만들어진 토픽이 흔한 원인입니다. - 재할당 JSON에 레플리카를 추가해
kafka-reassign-partitions.sh --execute로 복제 계수를 올립니다(절차는 클러스터 구성). - 근본 대책은
auto.create.topics.enable=false와default.replication.factor상향입니다.
시나리오 3 — 롤링 재시작 뒤 특정 브로커만 CPU가 높다
- 재시작된 브로커는 팔로워로만 복귀합니다. 리더가 남은 브로커들에 쏠려 부하가 불균형해집니다.
PreferredReplicaImbalanceCount가 0보다 큰지 확인합니다. 이 값이 곧 "리더가 preferred가 아닌 파티션 수"입니다.auto.leader.rebalance.enable=true라면leader.imbalance.check.interval.seconds(기본 300) 주기로 자동 복구됩니다. 기다립니다.- 급하면
kafka-leader-election.sh --election-type preferred --all-topic-partitions로 수동 복구합니다. - 롤링 절차 자체에 각 노드 재시작 후 URP가 0으로 돌아올 때까지 대기를 넣어 두면 이 상황이 크게 줄어듭니다.
시나리오 4 — "파티션이 너무 많으니 줄여 달라"는 요청
Kafka는 파티션 수 감소를 지원하지 않습니다. 유일한 방법은
새 토픽을 원하는 파티션 수로 만들어 데이터를 옮기고 클라이언트를 전환하는 것입니다.
늘리는 것도 공짜가 아닙니다. 키 해시 분배가 바뀌어 기존 키의 순서 보장이 깨지고,
auto.offset.reset=latest인 컨슈머는 새 파티션의 초기 메시지를 놓칠 수 있습니다.
자주 나오는 함정
관련 케이스 스터디
- 케이스 3 · 브로커 장애 후 메시지가 유실됐다 —
acks와 ISR의 결합 - 케이스 5 · 파티션을 1000개로 늘렸더니 더 느려졌다 — 파티션 수의 비용
- 케이스 6 · RF=3인데 브로커 1대 죽자 유실됐다 —
min.insync.replicas기본값 함정 - 케이스 1 · 배포 후 며칠치 데이터가 사라졌다 — 리텐션과 세그먼트
배경 개념이 더 필요하면 2장 아키텍처와 핵심 개념과 3장 KRaft와 클러스터 메타데이터를 먼저 보세요.
미니 퀴즈
matching과 list order 유형이 섞여 있습니다. 실제 CCAAK에서도 이 두 유형이 출제되므로 함께 연습하세요.
공식 문서 출처
- Broker Configs —
replica.lag.time.max.ms,min.insync.replicas,unclean.leader.election.enable,controlled.shutdown.enable,auto.leader.rebalance.enable,offsets.topic.replication.factor - Topic Configs —
retention.ms,segment.ms,max.message.bytes - Basic Kafka Operations — 토픽 생성·수정, 파티션 감소 불가, 내부 토픽 파티션 증설 금지, graceful shutdown, preferred 리더 선출
- KRaft —
process.roles, 컨트롤러 쿼럼,kafka-metadata-quorum.sh - Monitoring —
UnderReplicatedPartitions,UnderMinIsrPartitionCount,IsrShrinksPerSec,UncleanLeaderElectionsPerSec,PreferredReplicaImbalanceCount - Hardware and OS — 파일 디스크립터 100000 권고,
vm.max_map_count와 세그먼트당 map area 2개