CCAAK · 섹션 5
Cluster Configuration
운영 중 가장 자주 손대는 영역이고, 그래서 사고도 가장 많이 납니다.
이 섹션의 절반은 kafka-reassign-partitions.sh 3단계 워크플로이고
나머지 절반은 kafka-configs.sh의 동적 설정입니다.
list order 문항이 가장 많이 나오는 섹션이므로 절차의 순서를 그대로 외우세요.
학습 목표
- 파티션 재할당 3모드를 순서대로 실행하고, 스로틀을 걸고 해제할 수 있습니다.
- 설정의 갱신 모드(
read-only/per-broker/cluster-wide)를 구분할 수 있습니다. - 쿼터를 (user, client-id) · user · client-id 수준으로 설정하고 조회할 수 있습니다.
- 파티션 수를 늘릴 때의 부작용 세 가지를 말할 수 있고, 줄일 수 없다는 것을 압니다.
- 4.3 롤링 업그레이드 절차와 메타데이터 다운그레이드 제약을 설명할 수 있습니다.
이 도메인이 묻는 것
| 질문 형태 | 실제로 확인하는 것 |
|---|---|
| "재할당 단계를 순서대로 배열하시오" (list order) | --generate → --execute → --verify |
| "재할당이 끝났는데 정상 복제가 느리다" | 스로틀 해제를 잊은 상태 |
| "이 설정을 재시작 없이 바꿀 수 있는가?" | 갱신 모드 3종 구분 |
| "특정 사용자만 대역을 제한하려면?" | 쿼터 엔티티 계층과 --entity-default |
| "파티션을 40에서 20으로 줄이는 명령은?" | 줄일 수 없다는 사실 |
| "복제 계수를 3으로 올리는 방법은?" | kafka-topics.sh가 아니라 재할당 JSON |
핵심 개념 요약 — 운영 관점
파티션 재할당 3단계 — 이 섹션의 핵심
kafka-reassign-partitions.sh는 상호 배타적인 세 모드로 동작합니다.
도구는 데이터 분포를 스스로 분석하지 않으므로, 무엇을 옮길지는 운영자가 정합니다.
| 순서 | 모드 | 하는 일 | 놓치면 생기는 일 |
|---|---|---|---|
| 1 | --generate |
대상 토픽 목록과 브로커 목록을 받아 후보 계획을 만듭니다. 현재 배치와 제안 배치를 함께 출력합니다. | 계획 없이 손으로 JSON을 쓰다 실수합니다. 현재 배치는 롤백용으로 반드시 저장하세요. |
| 2 | --execute |
JSON 계획대로 이동을 시작합니다. 완료를 기다리지 않고 반환합니다. | 스로틀 없이 실행하면 정상 트래픽이 복제에 밀려 지연이 튑니다. |
| 3 | --verify |
진행 상태를 확인하고, 완료되었으면 스로틀을 해제합니다. | 스로틀이 영구히 남아 이후 모든 복제가 조여집니다. |
스로틀은 다섯 개의 설정으로 구현되며, kafka-reassign-partitions.sh가 자동으로 세팅합니다.
직접 손대는 것보다 도구를 통하는 것이 안전합니다.
| 설정 | 범위 | 역할 |
|---|---|---|
leader.replication.throttled.rate | 브로커 (동적) | 리더 측 복제 전송 상한(B/s) |
follower.replication.throttled.rate | 브로커 (동적) | 팔로워 측 복제 수신 상한(B/s) |
replica.alter.log.dirs.io.max.bytes.per.second | 브로커 (동적) | 브로커 내부 디스크 간 이동 상한 |
leader.replication.throttled.replicas | 토픽 | 리더 스로틀이 적용될 레플리카 목록 |
follower.replication.throttled.replicas | 토픽 | 팔로워 스로틀이 적용될 레플리카 목록 |
파티션 수 — 늘릴 수만 있고 공짜가 아닙니다
Kafka는 파티션 수 감소를 지원하지 않습니다. 늘리는 것은 되지만 부작용이 있습니다.
- 키 분배가 바뀝니다. 기본 파티셔너는
hash(key) % 파티션수로 배정하므로, 파티션 수가 바뀌면 같은 키가 다른 파티션으로 갑니다. 기존 데이터는 재분배되지 않습니다. 결과적으로 기존 키의 순서 보장이 깨집니다. auto.offset.reset=latest컨슈머가 메시지를 놓칠 수 있습니다. 새 파티션이 생기고 컨슈머가 그것을 발견하기 전에 들어온 메시지가 건너뛰어집니다.- 메타데이터 전파 지연이 있습니다.
metadata.max.age.ms주기까지는 클라이언트가 새 파티션을 모르므로 분배가 한동안 불균등하고 lag이 튑니다.
복제 계수 변경 — kafka-topics.sh로는 안 됩니다
복제 계수는 --alter로 바꿀 수 없습니다.
재할당 JSON에 레플리카를 추가하고 --execute하는 방식만 있습니다.
예를 들어 브로커 5에만 있던 파티션을 5·6·7 세 곳으로 늘리면 RF가 1에서 3이 됩니다.
동적 설정 — 갱신 모드 3종
브로커 설정은 재시작 없이 바꿀 수 있는 것과 그렇지 않은 것이 나뉩니다. 공식 설정 표의 Dynamic Update Mode 열이 그 구분입니다.
| 모드 | 의미 | 예 |
|---|---|---|
read-only |
브로커 재시작이 필요합니다 | listeners, advertised.listeners, broker.rack, replica.lag.time.max.ms, log.dirs |
per-broker |
브로커별로 동적 변경 가능 | listener.security.protocol.map, ssl.keystore.location, sasl.enabled.mechanisms |
cluster-wide |
클러스터 기본값으로 동적 변경 가능. 테스트용으로 브로커별 지정도 가능 | min.insync.replicas, unclean.leader.election.enable, num.io.threads, message.max.bytes, log.segment.bytes |
쿼터 — 엔티티 계층
기본적으로 클라이언트는 무제한 쿼터를 받습니다.
쿼터는 (user, client-id) · user · client-id 세 수준에 설정할 수 있고,
각 수준에 구체 이름(--entity-name)과
기본값(--entity-default)을 줄 수 있습니다.
| 키 | 제한 대상 | 단위 |
|---|---|---|
producer_byte_rate | 프로듀서 쓰기 대역 | bytes/sec |
consumer_byte_rate | 컨슈머 읽기 대역 | bytes/sec |
request_percentage | 요청 처리 스레드 점유율 | % |
리텐션과 정리 정책
디스크 사용량을 실제로 좌우하는 것은 리텐션 값이 아니라 리텐션과 세그먼트 크기의 조합입니다. 삭제는 세그먼트 단위이고, 활성 세그먼트는 삭제되지 않습니다.
segment.ms/segment.bytes가 실제 보관량을 좌우함
필수 CLI 명령어
# 옮길 토픽 목록을 JSON 으로 작성
cat > topics-to-move.json <<'EOF'
{
"topics": [ { "topic": "orders" }, { "topic": "payments" } ],
"version": 1
}
EOF
# 대상 브로커를 지정해 후보 계획 생성
bin/kafka-reassign-partitions.sh --bootstrap-server localhost:9092 \
--topics-to-move-json-file topics-to-move.json \
--broker-list "5,6" --generate
# 출력의 "Current partition replica assignment" 를 rollback.json 으로 저장하고,
# "Proposed partition reassignment configuration" 을 reassign.json 으로 저장한다
# 브로커 간 50MB/s, 브로커 내부 디스크 간 100MB/s 로 제한
bin/kafka-reassign-partitions.sh --bootstrap-server localhost:9092 \
--reassignment-json-file reassign.json --execute \
--throttle 50000000 --replica-alter-log-dirs-throttle 100000000
# 진행 중에 스로틀을 올리려면 --additional 로 같은 JSON 을 다시 실행
bin/kafka-reassign-partitions.sh --bootstrap-server localhost:9092 \
--additional --execute --reassignment-json-file reassign.json --throttle 700000000
# --execute 에 쓴 것과 "같은" JSON 을 넘겨야 합니다
bin/kafka-reassign-partitions.sh --bootstrap-server localhost:9092 \
--reassignment-json-file reassign.json --verify
# 완료 시 출력에 다음이 보여야 스로틀이 해제된 것입니다
# Clearing broker-level throttles on brokers 1,2,3
# Clearing topic-level throttles on topic orders
# 스로틀이 남아 있는지 직접 확인
bin/kafka-configs.sh --describe --bootstrap-server localhost:9092 --entity-type brokers
bin/kafka-configs.sh --describe --bootstrap-server localhost:9092 --entity-type topics
# 브로커 5 에만 있던 파티션에 6,7 을 추가 → RF 1 에서 3 으로
cat > increase-rf.json <<'EOF'
{"version":1,"partitions":[{"topic":"orders","partition":0,"replicas":[5,6,7]}]}
EOF
bin/kafka-reassign-partitions.sh --bootstrap-server localhost:9092 \
--reassignment-json-file increase-rf.json --execute
bin/kafka-reassign-partitions.sh --bootstrap-server localhost:9092 \
--reassignment-json-file increase-rf.json --verify
# 토픽 설정 추가 / 삭제
bin/kafka-configs.sh --bootstrap-server localhost:9092 --entity-type topics \
--entity-name orders --alter --add-config retention.ms=86400000
bin/kafka-configs.sh --bootstrap-server localhost:9092 --entity-type topics \
--entity-name orders --alter --delete-config retention.ms
# 브로커 개별 설정 (per-broker)
bin/kafka-configs.sh --bootstrap-server localhost:9092 --entity-type brokers \
--entity-name 0 --alter --add-config log.cleaner.threads=2
# 현재 동적 설정 조회
bin/kafka-configs.sh --bootstrap-server localhost:9092 --entity-type brokers \
--entity-name 0 --describe
# 브로커 로거 레벨 변경 (재시작 없이)
bin/kafka-configs.sh --bootstrap-server localhost:9092 --broker-logger 0 \
--alter --add-config kafka.server.ReplicaManager=DEBUG
# 오프라인 브로커의 설정을 다루려면 컨트롤러로
bin/kafka-configs.sh --bootstrap-controller localhost:9093 --entity-type brokers \
--entity-name 1 --describe
# (user, client-id) 조합
bin/kafka-configs.sh --bootstrap-server localhost:9092 --alter \
--add-config 'producer_byte_rate=1024,consumer_byte_rate=2048,request_percentage=200' \
--entity-type users --entity-name user1 --entity-type clients --entity-name clientA
# user 단위
bin/kafka-configs.sh --bootstrap-server localhost:9092 --alter \
--add-config 'producer_byte_rate=1048576' --entity-type users --entity-name user1
# 모든 user 의 기본 쿼터 (--entity-name 대신 --entity-default)
bin/kafka-configs.sh --bootstrap-server localhost:9092 --alter \
--add-config 'producer_byte_rate=1048576' --entity-type users --entity-default
# 조회 — 이름을 생략하면 해당 타입 전체
bin/kafka-configs.sh --bootstrap-server localhost:9092 --describe --entity-type users
# 특정 로그 디렉터리를 신규 배치 대상에서 제외 (cordon)
bin/kafka-configs.sh --bootstrap-server localhost:9092 --alter \
--add-config cordoned.log.dirs=/data/dir1 --entity-type brokers --entity-name 1
# 브로커 전체를 cordon
bin/kafka-configs.sh --bootstrap-server localhost:9092 --alter \
--add-config cordoned.log.dirs="*" --entity-type brokers --entity-name 1
# cordon 해제 (브로커가 오프라인이면 컨트롤러로)
bin/kafka-configs.sh --bootstrap-controller localhost:9093 --alter \
--delete-config cordoned.log.dirs --entity-type brokers --entity-name 1
반드시 외워야 할 설정값
| 설정 | 기본값 | 갱신 모드 | 운영 포인트 |
|---|---|---|---|
min.insync.replicas |
1 | cluster-wide |
장애 중에도 즉시 조정할 수 있습니다. RF=3이면 2가 표준입니다. |
unclean.leader.election.enable |
false | cluster-wide |
최후의 수단. 켠 뒤 반드시 되돌리세요. |
message.max.bytes |
1048588 | cluster-wide |
토픽 레벨 max.message.bytes가 오버라이드합니다. |
log.segment.bytes |
1073741824 | cluster-wide |
토픽 레벨은 segment.bytes입니다. |
num.io.threads |
8 | cluster-wide |
RequestHandlerAvgIdlePercent를 보고 조정합니다. |
num.network.threads |
3 | cluster-wide |
NetworkProcessorAvgIdlePercent를 보고 조정합니다. |
num.replica.fetchers |
1 | cluster-wide |
복제가 못 따라잡을 때 올립니다. |
background.threads |
10 | cluster-wide |
— |
log.cleaner.threads |
1 | cluster-wide |
compaction이 밀리면 올립니다. |
advertised.listeners |
null | read-only |
변경에 롤링 재시작이 필요합니다. |
replica.lag.time.max.ms |
30000 | read-only |
동적으로 못 바꿉니다. 장애 중 회피 수단이 아닙니다. |
log.retention.check.interval.ms |
300000 (5분) |
read-only |
리텐션 정리가 5분 주기로 돕니다. 즉시 줄어들지 않는 이유입니다. |
retention.bytestopic |
-1 (무제한) |
— | 크기 기준 리텐션은 기본적으로 꺼져 있습니다. 디스크 보호에 유용합니다. |
min.cleanable.dirty.ratiotopic |
0.5 | — | compaction 시작 임계값. 낮추면 더 자주 정리하지만 CPU를 씁니다. |
delete.retention.mstopic |
86400000 (1일) |
— | compacted 토픽에서 tombstone을 유지하는 시간입니다. |
group.initial.rebalance.delay.ms |
3000 | read-only |
그룹 최초 형성 시 멤버를 모으려고 일부러 기다리는 시간입니다. |
delete.topic.enable |
true | read-only |
false면 삭제 요청이 무시됩니다. |
장애 시나리오와 대응
시나리오 1 — 재할당이 끝난 뒤부터 URP가 만성적으로 남는다
- 재할당 이력을 확인합니다. 최근에
--execute --throttle을 했다면 1순위 의심 대상입니다. kafka-configs.sh --describe --entity-type brokers로leader.replication.throttled.rate·follower.replication.throttled.rate가 남아 있는지 봅니다.--entity-type topics로*.throttled.replicas도 확인합니다.- 원래 JSON이 있으면
--verify를 실행해 정상 경로로 해제합니다. - JSON을 잃었다면
kafka-configs.sh --alter --delete-config로 다섯 개 설정을 직접 지웁니다.
시나리오 2 — 재할당이 며칠째 still in progress
--verify로 어느 파티션이 남았는지 확인합니다.FetcherLagMetrics의ConsumerLag가 감소 추세인지 봅니다. 줄지 않으면 진행이 멈춘 것입니다.- 브로커의
BytesInPerSec와 스로틀 값을 비교합니다.max(BytesInPerSec) > throttle이면 구조적으로 따라잡을 수 없습니다. --additional --execute --throttle로 상한을 올립니다. 트래픽이 적은 시간대에 올리는 것이 안전합니다.- 대상 브로커의 디스크가 가득 차 가는지도 확인합니다. 목적지 용량 부족도 같은 증상을 냅니다.
시나리오 3 — 설정을 바꿨는데 반영되지 않는다
| 확인 | 원인 | 조치 |
|---|---|---|
그 설정이 read-only인가 |
동적 변경이 불가능한 설정 | 롤링 재시작이 필요합니다 |
| 토픽 레벨 오버라이드가 있는가 | 브로커 값을 바꿔도 토픽 값이 우선 | --entity-type topics --describe로 확인 후 토픽 값을 바꾸거나 지웁니다 |
| 리텐션인데 디스크가 안 줄어드는가 | 정리 주기(log.retention.check.interval.ms 기본 5분)와 세그먼트 단위 삭제 |
기다리거나 segment.ms/segment.bytes를 줄여 세그먼트가 굴러가게 합니다 |
| 클라이언트 쪽 설정인가 | 브로커 설정으로 클라이언트 동작을 바꾸려 함 | 프로듀서·컨슈머 설정을 함께 조정해야 합니다(설정 치트시트) |
시나리오 4 — 한 애플리케이션이 클러스터 전체를 느리게 만든다
RequestHandlerAvgIdlePercent가 0.3 미만으로 떨어졌는지 확인합니다.- 사용자·클라이언트별
kafka.server:type={Produce|Fetch},user=...,client-id=...메트릭으로 어느 주체가 대역을 먹는지 특정합니다. - 해당 주체에
producer_byte_rate또는consumer_byte_rate쿼터를 부여합니다. - 요청 수 자체가 문제(작은 배치를 대량으로)라면
request_percentage로 스레드 점유를 제한합니다. - 재발 방지를 위해
--entity-default로 전체 기본 쿼터를 걸고, 신뢰된 클라이언트만 개별 상향합니다.
시나리오 5 — 4.3으로 롤링 업그레이드
이 절차는 list order 유형의 단골입니다. 순서를 그대로 외우세요.
- 전제 확인: KRaft 모드이고 소프트웨어·메타데이터 버전이 최소 3.3.x여야 합니다. 3.3.x보다 오래된 KRaft 클러스터는 3.9.x를 먼저 경유하는 것이 권장됩니다.
- 브로커를 한 대씩 종료하고, 바이너리를 교체하고, 재시작합니다. 각 노드마다 URP가 0으로 돌아오는지 확인하고 다음 노드로 갑니다.
- 모든 노드가 새 버전으로 올라오면 동작과 성능을 검증합니다. 이 단계에서는 메타데이터 버전이 아직 예전 값이라 되돌릴 수 있습니다.
- 검증이 끝나면
kafka-features.sh --bootstrap-server localhost:9092 upgrade --release-version 4.3으로 업그레이드를 확정(finalize)합니다. - 확정 후에는 메타데이터 다운그레이드가 불가능합니다. 4.3은 메타데이터 변경을 포함하므로, 사이에 메타데이터 변경이 없는 버전 간에만 다운그레이드가 가능합니다.
자주 나오는 함정
관련 케이스 스터디
- 케이스 1 · 배포 후 며칠치 데이터가 사라졌다 — 리텐션 설정 변경의 파급
- 케이스 5 · 파티션을 1000개로 늘렸더니 더 느려졌다 — 파티션 증설의 비용
- 케이스 8 · 상태 토픽 데이터가 조용히 사라졌다 —
cleanup.policy오설정
설정값 전체 목록은 설정 치트시트, 명령 목록은 CLI 치트시트, 스토리지 배경은 7장 스토리지·리텐션·컴팩션에 있습니다. 이 페이지의 조치가 어떤 메트릭으로 확인되는지는 Observability에서 다룹니다.
미니 퀴즈
절차 배열과 갱신 모드 구분이 중심입니다.
공식 문서 출처
- Basic Kafka Operations — 재할당 3모드, 스로틀 5개 설정,
--verify의 스로틀 해제,max(BytesInPerSec) > throttle, 쿼터 설정, 파티션 감소 불가, 파티션 증가 부작용, 복제 계수 증가 - Broker Configs — Dynamic Update Mode 3종,
kafka-configs.sh사용법, 기본값 - Topic Configs —
retention.bytes,min.cleanable.dirty.ratio,delete.retention.ms - Upgrading Apache Kafka — 4.3 롤링 업그레이드 절차, 최소 3.3.x 요건,
kafka-features.sh upgrade --release-version, 메타데이터 다운그레이드 제약, 4.3의--delete-config변경 - Monitoring —
FetcherLagMetrics, 쿼터 메트릭