빠른참조 · 트러블슈팅
트러블슈팅 치트시트
지금 장애 대응 중이라면 30초 트리아지부터 실행하세요. 예외 메시지를 이미 갖고 있다면 예외 사전에서 클래스명으로 검색하세요. 이 페이지의 모든 예외 클래스명은 Apache Kafka 4.3 소스에서 확인했습니다.
이 페이지 구조
- 30초 트리아지 — 무엇이 부러졌는지 먼저 가릅니다.
- 컨슈머가 안 읽는다 / 프로듀서 전송 실패 / 브로커 성능 저하 — 의사결정 트리 3개.
- 증상 → 확인 명령 → 원인 → 조치 표.
- 예외 사전 46개 — 클래스명으로 검색.
30초 트리아지
신고를 받으면 클러스터가 문제인지 클라이언트가 문제인지부터 가릅니다. 순서를 지키세요. 아래 네 명령이 전부 깨끗하면 클러스터는 정상이고 문제는 클라이언트 쪽입니다.
export BS=localhost:9092
# 1) 읽기·쓰기가 아예 불가한 파티션 (최고 심각도)
bin/kafka-topics.sh --bootstrap-server $BS --describe --unavailable-partitions
# 2) acks=all 쓰기가 막힌 파티션
bin/kafka-topics.sh --bootstrap-server $BS --describe --under-min-isr-partitions
# 3) 복제가 뒤처진 파티션 (아직 쓰기는 됨)
bin/kafka-topics.sh --bootstrap-server $BS --describe --under-replicated-partitions
# 4) 컨트롤러 쿼럼이 살아 있는가
bin/kafka-metadata-quorum.sh --bootstrap-server $BS describe --status
| 결과 | 판정 | 다음 단계 |
|---|---|---|
| 1번에 출력이 있다 | 클러스터 장애 · 최고 심각도. 해당 파티션은 읽기·쓰기 모두 불가 | 브로커 트리로. 모든 레플리카가 오프라인인지 확인 |
| 2번에만 출력이 있다 | acks=all 프로듀서가 NotEnoughReplicasException을 받고 있습니다 |
죽은 브로커 복구가 정답. 급하면 min.insync.replicas 한시 하향(내구성 포기) |
| 3번에만 출력이 있다 | 복제 지연. 여유가 줄었지만 서비스는 정상 | 롤링 재시작 중이면 정상입니다. 15분 이상 지속되면 조사 |
| 4번에서 리더가 없다 | 컨트롤러 쿼럼 문제. 메타데이터 변경이 전부 실패합니다 | 컨트롤러 노드의 디스크·네트워크·ActiveControllerCount 확인 |
| 전부 깨끗하다 | 클러스터는 정상. 클라이언트 또는 설정 문제입니다 | 컨슈머 트리 또는 프로듀서 트리로 |
의사결정 트리 ① 컨슈머가 안 읽는다
read_committed 문제인가
| # | 확인 | 명령 | 그렇다면 |
|---|---|---|---|
| 1 | 그룹에 멤버가 있는가 | --describe --group G --members |
없으면 애플리케이션이 죽었거나 group.id가 다릅니다. 배포 로그부터 확인 |
| 2 | 그룹 상태가 Stable인가 |
--describe --group G --state |
PreparingRebalance가 계속되면 리밸런스 루프입니다 → 4번으로 |
| 3 | 파티션 수 ≥ 컨슈머 수인가 | --describe --group G --members --verbose |
컨슈머가 더 많으면 남는 컨슈머는 아무 파티션도 받지 못합니다(정상 동작). 파티션을 늘리거나 Share Group을 검토 |
| 4 | 처리 시간이 max.poll.interval.ms를 넘는가 |
애플리케이션 로그에서 CommitFailedException 검색 |
넘으면 브로커가 멤버를 제외합니다 → max.poll.records를 줄이거나 max.poll.interval.ms를 늘리세요 (케이스 2) |
| 5 | lag이 있는데 진행이 없는가 | --describe --group G를 10초 간격으로 두 번 |
CURRENT-OFFSET이 그대로면 poll 루프가 블록되어 있습니다. 스레드 덤프를 뜨세요 |
| 6 | LAG이 -로 나오는가 |
--describe --group G |
커밋된 오프셋이 없습니다. 새 그룹이거나 offsets.retention.minutes 만료 → auto.offset.reset이 결정합니다 (케이스 1) |
| 7 | 데이터가 애초에 있는가 | kafka-get-offsets.sh --topic T --time latest |
모든 파티션이 0이면 프로듀서 문제입니다 → 프로듀서 트리로 |
| 8 | 토픽에 데이터가 있는데 컨슈머만 못 읽는가 | kafka-console-consumer.sh --topic T --from-beginning --max-messages 1 |
콘솔로는 읽힌다면 애플리케이션의 역직렬화·권한·isolation.level 문제입니다 |
| 9 | read_committed를 쓰는가 |
컨슈머 설정 확인 | 그렇다면 멈춘 트랜잭션이 LSO를 막고 있을 수 있습니다 → kafka-transactions.sh find-hanging --topic T |
| 10 | 권한이 있는가 | kafka-acls.sh --list --topic T |
컨슈머는 토픽 READ + 그룹 READ가 모두 필요합니다. 하나만 있으면 GroupAuthorizationException |
| 11 | 쿼터에 걸려 있는가 | JMX fetch-throttle-time-avg |
0이 아니면 consumer_byte_rate 쿼터입니다. "브로커는 한가한데 컨슈머만 느림"의 흔한 원인 |
# 상태를 5초 간격으로 6번 — Stable 이 아닌 상태가 반복되면 리밸런스 루프
for i in $(seq 6); do
bin/kafka-consumer-groups.sh --bootstrap-server $BS --describe --group my-group --state \
| awk 'NR>1 { print strftime("%H:%M:%S"), $0 }'
sleep 5
done
의사결정 트리 ② 프로듀서 전송 실패
| # | 증상 · 예외 | 확인 | 조치 |
|---|---|---|---|
| 1 | TimeoutException: Topic not present in metadata |
토픽이 존재하는가, bootstrap.servers가 맞는가 |
토픽 생성 또는 오타 수정. auto.create.topics.enable=false면 자동 생성되지 않습니다 |
| 2 | TimeoutException: Expiring N record(s) |
delivery.timeout.ms가 만료됐습니다. 브로커가 응답을 못 주고 있습니다 |
URP·ISR 확인. 네트워크·advertised.listeners 확인. 버퍼가 가득 찼는지도 확인 |
| 3 | RecordTooLargeException |
네 곳의 크기 설정 | 프로듀서 max.request.size, 토픽 max.message.bytes, 브로커 message.max.bytes, 브로커 replica.fetch.max.bytes를 전부 맞춰야 합니다 (케이스 10) |
| 4 | NotEnoughReplicasException |
--describe --under-min-isr-partitions |
ISR이 min.insync.replicas 미달입니다. 브로커 복구가 정답 (케이스 6) |
| 5 | TopicAuthorizationException |
kafka-acls.sh --list --topic T |
프로듀서는 WRITE + DESCRIBE가 필요합니다. --producer 편의 옵션이 함께 부여합니다 |
| 6 | ClusterAuthorizationException (멱등 프로듀서) |
멱등성이 켜져 있는가 (4.x 기본 true) |
구버전 브로커에서는 Cluster IdempotentWrite 권한이 필요합니다. --idempotent 옵션 참고 |
| 7 | ConfigException (기동 시) |
설정 조합이 모순되지 않는가 | 대표적으로 enable.idempotence=true + max.in.flight.requests.per.connection > 5, 또는 acks가 all이 아닌 조합 |
| 8 | OutOfOrderSequenceException |
멱등 프로듀서의 시퀀스가 끊겼습니다 | 브로커가 이전 상태를 잃은 경우(로그 절단, PID 만료)입니다. 프로듀서를 재생성해야 합니다. producer.id.expiration.ms도 확인 |
| 9 | ProducerFencedException |
같은 transactional.id로 새 프로듀서가 등록됐습니다 |
정상 동작입니다(좀비 펜싱). 이 인스턴스는 종료해야 합니다. 두 인스턴스가 같은 transactional.id를 쓰고 있지 않은지 확인 |
| 10 | InvalidTxnStateException |
트랜잭션 API 호출 순서 | initTransactions() → beginTransaction() → send() → commitTransaction() 순서를 지켜야 합니다 |
| 11 | send()가 블록되어 돌아오지 않는다 |
버퍼 고갈 또는 메타데이터 조회 실패 | max.block.ms만큼 블록한 뒤 예외가 납니다. buffer.memory와 브로커 도달성 확인 |
| 12 | 예외는 없는데 데이터가 안 들어간다 | 콜백에서 예외를 삼키고 있지 않은가 | send()는 비동기입니다. 콜백 또는 Future.get()으로 결과를 확인하지 않으면 실패가 조용히 사라집니다. JMX record-error-rate 확인 |
실패가 조용히 사라집니다. send()는 즉시 반환하고, 브로커가 거부해도 애플리케이션은 알지 못합니다.
producer.send(record);
// 아무것도 확인하지 않음
콜백에서 예외를 반드시 처리합니다. retriable / non-retriable을 구분해 대응합니다.
producer.send(record, (metadata, exception) -> {
if (exception == null) return;
if (exception instanceof RetriableException) {
// 클라이언트가 이미 delivery.timeout.ms 까지 재시도한 결과입니다.
// 여기 도달했다면 재시도로는 해결되지 않았다는 뜻입니다.
log.error("전송 최종 실패 (retriable 소진): {}", record.key(), exception);
} else {
// RecordTooLargeException, TopicAuthorizationException 등
// 재시도해도 절대 성공하지 않습니다. DLQ 또는 알림으로 보냅니다.
log.error("전송 실패 (재시도 불가): {}", record.key(), exception);
}
failureCounter.increment();
});
의사결정 트리 ③ 브로커 성능 저하
TotalTimeMs를 큐·로컬·원격·응답 구간으로 분해해
스레드 부족 / 디스크 / 복제 지연 / 네트워크 중 무엇인지 가르는 흐름
| # | 확인 | 지표 | 그렇다면 |
|---|---|---|---|
| 1 | 어느 구간이 느린가 | RequestMetrics,name=TotalTimeMs를 큐·Local·Remote·Response로 분해 |
지배적인 구간 하나를 고르고 아래로 내려갑니다 (지연 분해 표) |
| 2 | RequestQueueTimeMs가 크다 |
RequestHandlerAvgIdlePercent < 0.3 |
I/O 스레드 포화 → num.io.threads 상향(디스크 수 이상) |
| 3 | ResponseQueueTimeMs가 크다 |
NetworkProcessorAvgIdlePercent < 0.3 |
네트워크 스레드 포화 → num.network.threads 상향. TLS·압축 부하도 여기 걸립니다 |
| 4 | LocalTimeMs가 크다 |
LogFlushRateAndTimeMs, OS의 디스크 대기 |
디스크가 병목입니다. flush.messages를 임의로 낮췄는지 확인 → Kafka는 복제로 내구성을 얻습니다 |
| 5 | RemoteTimeMs가 크다 |
MaxLag,clientId=Replica, URP |
acks=all에서 복제를 기다린 것입니다. 클라이언트가 아니라 복제 쪽을 보세요 |
| 6 | 복제 throttle이 남아 있는가 | kafka-configs.sh --describe --entity-type topics --entity-name T |
*.replication.throttled.replicas가 남아 있으면 재할당 후 --verify를 안 돌린 것입니다 |
| 7 | 리더가 편중됐는가 | LeaderCount가 브로커 간에 크게 다름 |
kafka-leader-election.sh --election-type preferred --all-topic-partitions |
| 8 | 디스크가 차 있는가 | kafka-log-dirs.sh --describe, OfflineLogDirectoryCount |
즉시 조치로 리텐션 하향. 원래 값을 반드시 기록하고 나중에 되돌리세요 |
| 9 | GC가 길어졌는가 | GC 로그, IsrShrinksPerSec/IsrExpandsPerSec가 장애 없이 0이 아님 |
긴 GC는 하트비트를 끊어 ISR 플래핑과 리더 선거를 유발합니다. 힙과 GC 옵션 검토 |
| 10 | 파티션이 너무 많은가 | GlobalPartitionCount, EventQueueTimeMs |
파티션 총수는 컨트롤러 메모리·페일오버 시간·파일 디스크립터에 직접 영향합니다 (케이스 5) |
| 11 | 메시지 포맷 변환이 일어나는가 | {Produce|Fetch}MessageConversionsPerSec, TemporaryMemoryBytes |
0이 아니면 구버전 클라이언트 때문에 브로커가 변환을 하고 있습니다. zero-copy가 깨져 CPU를 씁니다 |
| 12 | 쿼터가 걸려 있는가 | kafka.server:type={Produce|Fetch},…의 throttle-time |
브로커는 한가한데 클라이언트만 느린 전형적 원인입니다 |
증상 → 확인 명령 → 원인 후보 → 조치
| 증상 | 확인 명령 · 지표 | 원인 후보 | 조치 |
|---|---|---|---|
| 배포 후 컨슈머가 과거 데이터를 전부 다시 읽는다 | --describe --group G에서 LAG이 갑자기 거대 |
group.id가 바뀜 / 커밋 만료 / auto.offset.reset=earliest |
group.id를 되돌리거나 오프셋을 원하는 지점으로 리셋 (케이스 1) |
| 배포 후 컨슈머가 최근 데이터를 건너뛴다 | LAG이 - 였다가 0이 됨 |
새 그룹 + auto.offset.reset=latest(기본값) |
--reset-offsets --to-datetime으로 되돌려 재처리 |
| 컨슈머가 계속 리밸런스만 한다 | --describe --group G --state가 PreparingRebalance 반복 |
처리 시간 > max.poll.interval.ms / 컨슈머가 반복 재시작 / 네트워크 불안정 |
max.poll.records 하향, group.instance.id로 정적 멤버십 (케이스 2) |
| lag이 특정 파티션만 큰다 | JMX records-lag (파티션별) |
핫키 편중 / 그 파티션 리더 브로커가 느림 | 키 설계 재검토 (케이스 4). 리더 브로커 지표 확인 |
| 컨슈머를 늘렸는데 처리량이 그대로다 | --describe --group G --members --verbose |
컨슈머 수 > 파티션 수 — 남는 컨슈머는 유휴 | 파티션을 늘리거나 Share Group(4.2+)을 검토 |
| 메시지가 중복 처리된다 | 애플리케이션 로그의 중복 키 | at-least-once의 정상 동작 / 리밸런스 시 커밋 전 재시작 / EOS 경계 밖의 외부 시스템 | 컨슈머 멱등성을 직접 구현하세요. Kafka EOS는 Kafka 안에서만 성립합니다 (케이스 7) |
| 메시지 순서가 뒤바뀐다 | 키가 지정되어 있는가, 파티션 수가 바뀌었는가 | 키 없음 / 파티션 증설로 키→파티션 매핑 변경 / max.in.flight > 1 + 멱등성 off |
키를 지정하세요. 순서 보장 단위는 파티션입니다 (4장) |
| 트랜잭션 컨슈머가 아무것도 못 읽는다 | kafka-transactions.sh find-hanging --topic T |
멈춘 트랜잭션이 LSO를 막고 있음 | 원인 프로듀서를 정리한 뒤 최후 수단으로 kafka-transactions.sh abort |
| 프로듀서가 갑자기 타임아웃한다 | --describe --under-min-isr-partitions |
ISR 부족 / 리더 이동 / 네트워크 | 브로커 복구. 프로듀서 delivery.timeout.ms는 증상이 아니라 시간 상한일 뿐입니다 |
| 특정 크기 이상 메시지만 실패한다 | 브로커 BytesRejectedPerSec |
4개 크기 설정 중 하나가 낮음 | 프로듀서 · 토픽 · 브로커 · replica.fetch 네 곳을 모두 맞추세요 (케이스 10) |
| 디스크가 예상보다 빨리 찬다 | kafka-log-dirs.sh --describe |
세그먼트 크기가 커서 삭제가 지연 / RF 계산 누락 / 압축 미적용 | 디스크 = 처리량 × 보관기간 × RF × (1 − 압축률). segment.bytes·segment.ms도 함께 조정 (7장) |
| 리텐션을 낮췄는데 디스크가 안 줄어든다 | NumLogSegments, log.retention.check.interval.ms |
활성 세그먼트는 삭제되지 않음 / 검사 주기 대기 / cleanup.policy=compact |
segment.ms를 낮춰 롤링을 유도하세요 |
| 컴팩션 토픽에서 오래된 키가 안 사라진다 | min.cleanable.dirty.ratio, log.cleaner.enable |
dirty 비율 미달 / cleaner 스레드 부족 / min.compaction.lag.ms |
컴팩션은 즉시가 아니라 조건 충족 시 일어납니다 (7장) |
cleanup.policy를 바꿨는데 옛 데이터가 그대로다 |
kafka-configs.sh --describe --entity-type topics |
정책 변경은 소급 적용되지 않음 | 변경 시점 이후 세그먼트에만 적용됩니다 (케이스 8) |
| 브로커 재시작 후 리더가 한쪽에 몰린다 | LeaderCount, PreferredReplicaImbalanceCount |
재시작 순서 / auto.leader.rebalance.enable=false |
kafka-leader-election.sh --election-type preferred --all-topic-partitions |
| 재할당이 끝났는데 복제가 계속 느리다 | 토픽 설정에 *.replication.throttled.replicas |
--verify를 실행하지 않아 throttle이 남음 |
--verify 실행 또는 --delete-config로 직접 제거 |
| 클라이언트가 브로커에 붙지 못한다 (Docker · K8s) | 브로커 로그, advertised.listeners |
컨테이너 내부 주소를 광고 중 | 클라이언트가 도달할 수 있는 주소로 광고하세요 (리스너 패턴 5가지) |
| CLI가 이유 없이 타임아웃한다 | --command-config를 넘겼는가 |
SASL·SSL 클러스터에서 인증 설정 누락 | 인증 실패가 명확한 에러가 아니라 타임아웃으로 보이는 경우가 많습니다 |
| 인증은 되는데 특정 작업만 거부된다 | kafka-acls.sh --list |
인가(ACL) 문제 — 인증과 별개 | 필요한 오퍼레이션 조합을 확인하세요 (ACL 표) |
| 컨슈머가 역직렬화에서 죽고 다시 시작해도 같은 곳에서 죽는다 | 애플리케이션 로그의 RecordDeserializationException |
포이즌 메시지. 커밋을 못 하니 같은 레코드를 계속 읽습니다 | 에러 핸들링 역직렬화기로 감싸 DLQ로 보내거나, 해당 오프셋을 seek로 건너뛰세요 |
| Streams 앱이 재시작마다 오래 걸린다 | state.dir가 영속 볼륨인가 |
상태 저장소가 사라져 changelog를 전부 다시 읽음 | state.dir를 영속화하고 num.standby.replicas=1을 두세요 |
| Connect 커넥터가 FAILED로 떨어진다 | GET /connectors/{name}/status |
변환·컨버터 오류 / 대상 시스템 장애 / errors.tolerance=none |
trace를 확인하고 DLQ를 설정하세요 (Connect 치트시트) |
| Connect 재시작 요청이 409로 거부된다 | 워커 로그의 rebalance | 리밸런스 진행 중 | 리밸런스 완료 후 재시도. 리밸런스 자체가 태스크를 재시작하므로 불필요할 수도 있습니다 |
| 메트릭은 정상인데 사용자는 느리다고 한다 | 쿼터 throttle-time, 클라이언트 request-latency-avg |
쿼터 / 클라이언트 GC / 클라이언트 네트워크 | 브로커 TotalTimeMs와 클라이언트 request-latency-avg의 차이가 네트워크 구간입니다 |
예외 사전
클래스명은 Apache Kafka 4.3 소스
(org.apache.kafka.common.errors,
org.apache.kafka.common.protocol.Errors,
org.apache.kafka.clients.consumer)에서 확인했습니다.
재시도 열은 프로토콜 에러 코드의 retriable 플래그입니다 —
true면 클라이언트가 자동으로 재시도하며, 그래도 여기 도달했다면
재시도 예산이 소진된 것입니다.
| 예외 클래스 | 재시도 | 원인 | 조치 | 관련 |
|---|---|---|---|---|
TimeoutException |
— | 메타데이터 조회 실패, delivery.timeout.ms 만료, Admin 요청 타임아웃 등 여러 상황 |
메시지 본문을 반드시 읽으세요. "Topic not present in metadata"와 "Expiring N record(s)"는 원인이 완전히 다릅니다 | 프로듀서 트리 |
RecordTooLargeException |
아니오 | 레코드(배치)가 허용 크기를 초과. 프로토콜 MESSAGE_TOO_LARGE |
프로듀서 max.request.size, 토픽 max.message.bytes, 브로커 message.max.bytes, replica.fetch.max.bytes를 전부 조정 |
케이스 10 |
RecordBatchTooLargeException |
아니오 | 배치가 서버의 세그먼트 크기보다 큼. 프로토콜 RECORD_LIST_TOO_LARGE |
batch.size를 줄이거나 segment.bytes를 검토 |
|
NotEnoughReplicasException |
예 | ISR 수가 min.insync.replicas 미달. acks=all일 때만 발생 |
브로커 복구가 정답. --describe --under-min-isr-partitions로 대상 확인 |
케이스 6 |
NotEnoughReplicasAfterAppendException |
예 | 로그에는 기록됐지만 ISR이 요구치 미달. 재시도하면 중복이 생길 수 있습니다 | 멱등 프로듀서(4.x 기본 true)면 중복이 제거됩니다. 껐다면 이 예외가 중복의 원인이 됩니다 |
6장 |
NotLeaderOrFollowerException |
예 | 요청을 받은 브로커가 그 파티션의 리더가 아님. 리더 이동 중 | 클라이언트가 메타데이터를 갱신해 자동 복구합니다. 지속되면 리더 선거가 반복되는 것 | |
LeaderNotAvailableException |
예 | 리더 선거 중이어서 리더가 없음 | 보통 자동 해소됩니다. 지속되면 --unavailable-partitions 확인 |
|
UnknownTopicOrPartitionException |
예 | 이 브로커가 해당 토픽-파티션을 호스팅하지 않음. 토픽 오타 또는 아직 생성되지 않음 | 토픽 존재 확인. auto.create.topics.enable=false면 자동 생성되지 않습니다 |
|
UnknownTopicIdException |
예 | 토픽 ID가 이 브로커에 없음. 토픽이 삭제·재생성된 경우 | 동명 토픽을 재생성하면 ID가 바뀝니다. 클라이언트 재시작 | |
OutOfOrderSequenceException |
아니오 | 멱등 프로듀서의 시퀀스 번호가 끊김. 브로커가 이전 상태를 잃음 | 프로듀서를 재생성해야 합니다. producer.id.expiration.ms와 리텐션이 너무 짧지 않은지 확인 |
4장 |
DuplicateSequenceException |
아니오 | 브로커가 같은 시퀀스 번호를 다시 받음 | 멱등성이 정상 동작하며 중복을 걸러낸 결과입니다 | |
UnknownProducerIdException |
아니오 | 브로커가 해당 producer ID의 메타데이터를 찾지 못함. 리텐션으로 관련 레코드가 삭제된 경우 등 | 프로듀서 재생성. 리텐션이 프로듀서 유휴 시간보다 짧은 구성인지 확인 | |
ProducerFencedException |
아니오 | 같은 transactionalId로 더 새로운 프로듀서가 등록되어 이 프로듀서가 펜싱됨 |
설계된 동작입니다. 이 인스턴스는 프로듀서를 닫고 종료해야 합니다. 두 인스턴스가 같은 ID를 쓰고 있는지 확인 | 6장 |
InvalidProducerEpochException |
아니오 | 오래된 epoch로 produce를 시도 | 펜싱과 같은 계열입니다. 트랜잭션을 중단하고 프로듀서를 재초기화 | |
InvalidTxnStateException |
아니오 | 잘못된 트랜잭션 상태에서 API를 호출 | initTransactions() → beginTransaction() → send() → commitTransaction() 순서를 지키세요 |
예제 5 |
TransactionAbortedException |
— | 트랜잭션이 중단되어 해당 send()가 완료되지 못함 |
중단의 원인이 되는 다른 예외를 로그에서 찾으세요. 이것은 결과입니다 | |
TransactionAbortableException |
아니오 | 트랜잭션을 중단해야 하는 오류가 발생 | abortTransaction()을 호출하고 다시 시작하세요 |
|
ConcurrentTransactionsException |
예 | 같은 트랜잭션에 대해 다른 작업이 진행 중 | 클라이언트가 재시도합니다. 지속되면 트랜잭션 코디네이터 부하 확인 | |
CommitFailedException |
아니오 | 커밋 시점에 이미 파티션 소유권을 잃음. 대개 poll() 간격이 max.poll.interval.ms를 초과. 예외 메시지가 원인과 조치를 그대로 알려 줍니다 — 아래 전문 |
max.poll.records를 줄이거나 max.poll.interval.ms를 늘리세요. 처리를 별도 스레드로 옮기는 것도 방법 |
케이스 2 |
RebalanceInProgressException |
아니오 | 그룹이 리밸런스 중이라 재조인이 필요 | 다음 poll()에서 자동 처리됩니다. 빈번하면 리밸런스 원인을 찾으세요 |
|
FencedInstanceIdException |
아니오 | 같은 group.instance.id로 다른 member.id가 등록됨 (정적 멤버십) |
두 인스턴스가 같은 group.instance.id를 쓰고 있습니다. 배포 설정 확인 |
|
UnknownMemberIdException |
아니오 | 코디네이터가 이 멤버를 모름. 세션 타임아웃으로 이미 제거된 경우 | 클라이언트가 재조인합니다. 반복되면 session.timeout.ms·GC 확인 |
|
IllegalGenerationException |
아니오 | generation ID가 낡음. 리밸런스가 이미 지나갔음 | 재조인으로 해소됩니다 | |
InvalidGroupIdException |
아니오 | group.id가 없거나 비어 있는데 그룹 기능(커밋·구독)을 사용 |
subscribe()·commitSync()는 group.id가 필요합니다. assign()만 쓸 거면 커밋도 직접 관리하세요 |
5장 |
GroupMaxSizeReachedException |
아니오 | 그룹 멤버 수가 group.max.size를 초과 |
브로커 group.max.size 확인. 컨슈머를 무한정 늘려도 파티션 수 이상은 무의미합니다 |
|
OffsetOutOfRangeException |
아니오 | 요청 오프셋이 파티션의 보관 범위 밖. 리텐션으로 삭제된 구간을 읽으려 함 | auto.offset.reset이 설정되어 있으면 자동 리셋됩니다. none이면 예외가 그대로 올라옵니다. JMX records-lead-min으로 조기 감지 |
케이스 1 |
NoOffsetForPartitionException |
아니오 | 커밋된 오프셋이 없고 auto.offset.reset=none |
의도적인 안전장치입니다. 어디서부터 읽을지 명시적으로 결정하세요 | |
RecordDeserializationException |
아니오 | 레코드 역직렬화 실패. 포이즌 메시지 | 커밋을 못 하므로 같은 레코드에서 무한 반복합니다. 에러 핸들링 역직렬화기로 감싸거나 seek로 건너뛰세요 |
예제 6 |
SerializationException |
아니오 | 직렬화·역직렬화 실패의 상위 예외. 스키마 불일치도 여기로 옵니다 | 스키마 호환성 모드와 배포 순서를 확인하세요 (8장) | 케이스 9 |
IllegalStateException: Subscription to topics, partitions and pattern are mutually exclusive |
아니오 | 한 컨슈머에서 subscribe(토픽 목록), subscribe(패턴), assign(파티션)을 섞어 호출했습니다 |
세 방식은 배타적입니다. 하나만 쓰세요. 전환하려면 먼저 unsubscribe()를 호출해야 합니다 |
5장 |
TopicAuthorizationException |
아니오 | 토픽에 대한 권한 없음 | 프로듀서는 WRITE+DESCRIBE, 컨슈머는 READ+DESCRIBE가 필요합니다 | 보안 치트시트 |
GroupAuthorizationException |
아니오 | 컨슈머 그룹에 대한 권한 없음 | 토픽 권한만 주고 그룹 권한을 빼먹는 것이 가장 흔한 실수입니다. --consumer 편의 옵션이 둘 다 부여합니다 |
보안 치트시트 |
ClusterAuthorizationException |
아니오 | 클러스터 레벨 오퍼레이션 권한 없음 | 토픽 자동 생성(Cluster CREATE), 재할당(Cluster ALTER) 등에서 발생 | |
TransactionalIdAuthorizationException |
아니오 | transactional.id에 대한 권한 없음 |
TransactionalId 리소스에 WRITE·DESCRIBE를 부여하세요 | |
SaslAuthenticationException |
아니오 | SASL 인증 실패. 자격 증명 오류 또는 메커니즘 불일치 | sasl.mechanism 기본값은 GSSAPI입니다 — SCRAM·PLAIN은 명시해야 합니다 |
보안 치트시트 |
SslAuthenticationException |
아니오 | TLS 핸드셰이크 실패. 인증서 만료, 체인 불일치, 호스트명 검증 실패 | -Djavax.net.debug=ssl:handshake로 원인을 좁히세요. ssl.endpoint.identification.algorithm도 확인 |
보안 치트시트 |
UnsupportedSaslMechanismException |
아니오 | 브로커가 요청한 SASL 메커니즘을 지원하지 않음 | 브로커 sasl.enabled.mechanisms에 해당 메커니즘이 있는지 확인 (기본값은 GSSAPI뿐) |
|
ConfigException |
아니오 | 설정 값·조합이 유효하지 않음. 기동 시점에 발생 | 대표 사례: enable.idempotence=true + max.in.flight.requests.per.connection > 5 |
설정 치트시트 |
KafkaStorageException |
예 | 브로커의 디스크 오류. 로그 디렉터리 접근 실패 | OfflineLogDirectoryCount·LogDirectoryOffline 확인. 디스크 교체가 근본 해결 |
메트릭 치트시트 |
CorruptRecordException |
예 | CRC 불일치, 크기 초과, 컴팩션 토픽의 null 키 등 | kafka-dump-log.sh --files … --index-sanity-check로 확인. 컴팩션 토픽에 키 없는 레코드를 보내지 마세요 |
|
UnsupportedVersionException |
아니오 | 브로커가 요청한 API 버전을 지원하지 않음 | kafka-broker-api-versions.sh로 확인. 신 클라이언트 + 구 브로커 조합에서 발생 |
|
InvalidReplicationFactorException |
아니오 | 복제 계수가 1 미만이거나 사용 가능한 브로커 수보다 큼 | 브로커 3대 미만 클러스터에서 offsets.topic.replication.factor=3(기본값)이 실패하는 전형적 사례 |
|
TopicExistsException |
아니오 | 같은 이름의 토픽이 이미 존재 | 멱등한 스크립트를 원하면 --if-not-exists를 쓰세요 |
|
ThrottlingQuotaExceededException |
예 | Admin 요청이 쿼터를 초과 | 토픽을 대량 생성·삭제하는 스크립트에서 자주 봅니다. 배치 크기를 줄이세요 | |
WakeupException |
— | 다른 스레드가 consumer.wakeup()을 호출해 블로킹 호출을 중단시킴 |
오류가 아닙니다. 정상 종료 신호로 쓰는 패턴입니다. finally에서 close()하세요 |
|
InvalidRequiredAcksException |
아니오 | acks 값이 유효하지 않음 |
허용값은 0, 1, all(-1)입니다 |
CommitFailedException 메시지 전문
이 예외는 메시지 자체가 진단서입니다.
인자 없이 생성될 때의 기본 메시지가 아래와 같이 고정되어 있으므로
(Kafka 4.3 CommitFailedException.java 확인),
로그에서 이 문장을 보면 원인을 더 찾을 필요가 없습니다.
Commit cannot be completed since the group has already rebalanced and assigned
the partitions to another member. This means that the time between subsequent
calls to poll() was longer than the configured max.poll.interval.ms, which
typically implies that the poll loop is spending too much time message
processing. You can address this either by increasing max.poll.interval.ms or
by reducing the maximum size of batches returned in poll() with max.poll.records.
브로커 로그 읽기
| 파일 | 무엇이 들어가는가 | 언제 보는가 |
|---|---|---|
server.log | 브로커 전반의 INFO·WARN·ERROR | 기본. 기동 실패·설정 오류·디스크 문제 |
controller.log | 컨트롤러 이벤트, 리더 선거, 메타데이터 처리 | 리더가 이동하거나 오프라인 파티션이 생겼을 때 |
state-change.log | 파티션 상태 전이 (리더/팔로워/오프라인) | 특정 파티션이 왜 오프라인인지 추적할 때 |
log-cleaner.log | 컴팩션 스레드의 동작 | 컴팩션이 진행되지 않을 때 |
kafka-request.log | 요청 상세 (기본 비활성) | 권한·크기 문제를 요청 단위로 봐야 할 때만 임시로 켭니다 |
kafka-authorizer.log | 인가 판정 결과 | ACL 문제를 추적할 때 — 거부 이유가 여기 남습니다 |
# 인가 문제 추적
bin/kafka-configs.sh --bootstrap-server $BS --alter \
--entity-type broker-loggers --entity-name 1 \
--add-config org.apache.kafka.metadata.authorizer.StandardAuthorizer=DEBUG
# 현재 로그 레벨 확인
bin/kafka-configs.sh --bootstrap-server $BS --describe \
--entity-type broker-loggers --entity-name 1
# 되돌리기 — DEBUG 를 켜 둔 채로 두면 디스크가 빠르게 찹니다
bin/kafka-configs.sh --bootstrap-server $BS --alter \
--entity-type broker-loggers --entity-name 1 \
--add-config org.apache.kafka.metadata.authorizer.StandardAuthorizer=INFO
이어서 볼 곳
공식 문서 출처
예외 클래스명과 retriable 여부는 Apache Kafka 4.3 소스
(org.apache.kafka.common.protocol.Errors와
org.apache.kafka.common.errors 패키지)에서 확인했습니다.
확인하지 못한 예외는 표에 넣지 않았습니다.
의사결정 트리의 순서와 조치 판단은 이 가이드의 정리이며 공식 문서의 서술이 아닙니다.
- Kafka Protocol — Error Codes — 에러 코드와 retriable 플래그, 설명 문장
- apache/kafka 4.3 —
common/errors— 예외 클래스 정의 - Monitoring — 트리에서 참조한 메트릭의 정상 범위
- Basic Kafka Operations — 확인·조치 명령
- Authorization and ACLs — 오퍼레이션별 필요 권한
- KRaft — 컨트롤러·메타데이터 진단