이 도메인의 학습 목표

이 도메인이 묻는 것

출제 형태는 대체로 "이 증상일 때 무엇을 먼저 보는가"입니다. 메트릭 이름을 정확히 아는지 묻는 matching 문항도 흔합니다. 브로커 쪽 메트릭도 일부 나오지만, 개발자가 자기 애플리케이션을 진단하기 위해 보는 범위에 한정됩니다. 참고 챕터는 11장 운영 기초의 모니터링 절과 5장 Consumer 심화입니다.

핵심 개념 (1) 클라이언트 메트릭

Kafka 클라이언트는 JMX로 메트릭을 노출합니다. MBean 이름은 kafka.producer:type=producer-metrics,client-id={id} 형태이고, 같은 값을 KafkaProducer.metrics() / KafkaConsumer.metrics()로 코드에서도 읽을 수 있습니다. client.id를 지정하지 않으면 자동 생성된 이름이 붙어 대시보드에서 구분이 안 됩니다. 서비스명 기반으로 반드시 지정하세요.

프로듀서 핵심 메트릭 (kafka.producer:type=producer-metrics)
메트릭의미이상 신호
record-send-rate초당 전송 레코드 수갑자기 0 → 프로듀서 정지 또는 버퍼 고갈
record-error-rate초당 전송 실패 레코드 수0이 아니면 즉시 조사. 알림 1순위
record-retry-rate초당 재시도 수상승 → 리더 이동, ISR 부족, 네트워크 불안정
record-queue-time-avg배치가 accumulator에서 대기한 평균 시간linger.ms보다 크게 상승 → 전송 병목
request-latency-avg브로커 응답 평균 지연상승 → 브로커 부하 또는 acks=all + 느린 팔로워
batch-size-avg평균 배치 크기batch.size에 한참 못 미치면 linger.ms 조정 여지
records-per-request-avg요청당 평균 레코드 수1에 가까우면 배치가 전혀 안 되는 상태
compression-rate-avg평균 압축률압축 효과 확인용
컨슈머 핵심 메트릭 — MBean type이 셋으로 나뉩니다
MBean type메트릭의미
consumer-fetch-manager-metrics records-lag-max 할당된 파티션 중 최대 lag. lag 알림의 기본 지표
records-lag-avg평균 lag
records-consumed-rate초당 소비 레코드 수
bytes-consumed-rate초당 소비 바이트
fetch-latency-avgfetch 요청 평균 지연
consumer-metrics time-between-poll-avg · time-between-poll-max poll 간격. max.poll.interval.ms에 가까워지면 위험
last-poll-seconds-ago마지막 poll 이후 경과 초. 멈춘 컨슈머 감지
poll-idle-ratio-avgpoll에서 대기한 시간 비율. 낮으면 처리에 시간을 다 쓰고 있음
consumer-coordinator-metrics assigned-partitions 현재 할당된 파티션 수. 0이면 아무것도 안 읽고 있음
rebalance-rate-per-hour시간당 리밸런스 횟수
failed-rebalance-rate-per-hour실패한 리밸런스. 0이 아니면 조사
rebalance-latency-avg리밸런스 평균 소요 시간
last-rebalance-seconds-ago마지막 리밸런스 이후 경과 초
commit-rate · commit-latency-avg커밋 빈도와 지연

핵심 개념 (2) consumer lag 측정 3가지 방법

lag = high watermark − committed offset입니다. LEO에서 빼는 것이 아닙니다(LEO와 high watermark 사이는 아직 복제가 끝나지 않아 컨슈머가 읽을 수 없는 구간입니다). 재는 방법이 세 가지이고, 각각 다른 것을 보여 줍니다.

lag 측정 방법 비교
방법 얻는 값 장점 함정
kafka-consumer-groups
--describe
CURRENT-OFFSET / LOG-END-OFFSET / LAG 클라이언트 수정 없이 즉시 확인. 파티션별로 볼 수 있음 커밋된 오프셋 기준이라 자동 커밋 주기만큼 늦음. 컨슈머가 죽으면 CONSUMER-ID가 비고 lag이 멈춘 값으로 남음
② 클라이언트 JMX
records-lag-max
컨슈머가 fetch 응답에서 계산한 실시간 lag 커밋과 무관하게 실제 처리 지연을 반영. 초 단위 관측 컨슈머가 살아 있어야만 값이 나옵니다. 죽으면 메트릭이 사라져 lag이 "0"처럼 보입니다 (가장 위험한 함정)
③ 외부 lag 익스포터
(Burrow 계열)
__consumer_offsets를 읽어 그룹별 lag을 계산 컨슈머가 죽어도 관측 가능. 소비 속도 추세까지 판단 별도 컴포넌트 운영 부담. 커밋 기준이므로 ①과 같은 지연이 있음
① 방법 — 실제 출력 형태
$ bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
    --describe --group billing-worker

TOPIC      PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG      CONSUMER-ID       HOST         CLIENT-ID
payments   0          854144          855809          1665     consumer1-3fc8…   /10.0.1.21   consumer1
payments   1          460537          803290          342753   consumer1-3fc8…   /10.0.1.21   consumer1
payments   2          243655          398812          155157   -                 -            -

핵심 개념 (3) 개발자가 알아야 할 브로커 메트릭

브로커 운영은 CCAAK 범위이지만, "내 애플리케이션이 느린 게 아니라 클러스터가 아픈 것"을 구분하려면 아래 정도는 알아야 합니다.

필수 브로커 JMX 메트릭
MBean정상 값이상 시 의미
kafka.server:type=ReplicaManager,
name=UnderReplicatedPartitions
0 ISR이 부족한 파티션. 0이 아니면 복제가 밀리고 있음
kafka.server:type=ReplicaManager,
name=UnderMinIsrPartitionCount
0 min.insync.replicas 미달. acks=all 쓰기가 실패하는 상태
kafka.server:type=ReplicaManager,
name=AtMinIsrPartitionCount
0 딱 최소치에 걸려 있음. 한 대만 더 빠지면 쓰기 실패
kafka.controller:type=KafkaController,
name=OfflinePartitionsCount
0 리더가 없는 파티션. 읽기·쓰기 모두 불가
kafka.controller:type=KafkaController,
name=ActiveControllerCount
클러스터 전체 합 1 0이면 컨트롤러 없음, 2 이상이면 split-brain 의심
kafka.server:type=ReplicaManager,
name=IsrShrinksPerSec
낮게 유지 반복 증가 → 팔로워가 계속 따라잡지 못함
kafka.server:type=
KafkaRequestHandlerPool,
name=RequestHandlerAvgIdlePercent
0.3 이상 낮으면 요청 처리 스레드 포화 → num.io.threads 검토
kafka.network:type=SocketServer,
name=NetworkProcessorAvgIdlePercent
0.3 이상 낮으면 네트워크 스레드 포화 → num.network.threads 검토
kafka.server:type=ReplicaManager,
name=IsrShrinksPerSec
평시 0 잦으면 브로커 불안정. 클라이언트에는 NotLeaderOrFollowerException으로 보임. LeaderElectionRateAndTimeMsKRaft에서 제거된 ZooKeeper 전용 지표입니다
kafka.server:type=BrokerTopicMetrics,
name=BytesInPerSec
· BytesOutPerSec
용량 추세 판단의 기준선

핵심 개념 (4) Connect 관측 — 로그를 어디서 찾는가

이 절이 이 도메인에서 가장 자주 틀리는 지점입니다. Connect 워커는 각 노드의 로컬 파일에 로그를 남깁니다. REST API는 클러스터 전체를 보여 주지만, 로그는 그렇지 않습니다.

  1. GET /connectors/{name}/status로 실패한 태스크와 trace를 확인합니다. 응답의 각 태스크에는 worker_id(호스트:포트)가 들어 있습니다.
  2. worker_id가 가리키는 노드의 로그를 봅니다. 다른 워커의 로그에는 그 태스크의 스택트레이스가 없습니다. 워커가 완전히 죽었다면 죽은 노드의 파일을 직접 열어야 합니다.
  3. 필요하면 런타임에 로그 레벨을 올립니다. 재시작 없이 PUT /admin/loggers/{logger}로 변경할 수 있습니다.
  4. 중앙 집계를 미리 준비합니다. 워커 로그를 중앙 로그 시스템으로 보내는 log appender를 추가하고, 커넥터별로 로그를 분리해 두면 사후 조사가 가능해집니다.
실패 조사 순서
# 1) 어느 태스크가 어느 워커에서 죽었는지
curl -s http://connect-1:8083/connectors/orders-sink/status

# 2) 런타임 로그 레벨 상향 (재시작 불필요)
curl -s -X PUT -H "Content-Type: application/json" \
  --data '{"level":"DEBUG"}' \
  http://connect-2:8083/admin/loggers/org.apache.kafka.connect.runtime.WorkerSinkTask

# 3) 현재 설정된 로거 확인
curl -s http://connect-2:8083/admin/loggers

# 4) 조사 후 원래 레벨로 되돌린다
curl -s -X PUT -H "Content-Type: application/json" \
  --data '{"level":"INFO"}' \
  http://connect-2:8083/admin/loggers/org.apache.kafka.connect.runtime.WorkerSinkTask
Connect 핵심 메트릭 (kafka.connect:type=…)
MBean type메트릭의미
connect-worker-metrics connector-total-task-count커넥터의 전체 태스크 수
connector-running-task-count실행 중 태스크 수
connector-failed-task-count실패 태스크 수. 알림 1순위
connect-worker-
rebalance-metrics
rebalancing · rebalance-avg-time-ms 리밸런스 중인지, 얼마나 걸리는지. 409의 원인 추적
source-task-metrics source-record-poll-rate · source-record-write-rate 읽는 속도와 Kafka에 쓰는 속도. 차이가 크면 변환·전송 병목
sink-task-metrics sink-record-read-rate · sink-record-send-rate Kafka에서 읽는 속도와 대상에 쓰는 속도
sink-record-lag-maxsink 커넥터의 lag
offset-commit-success-percentage낮아지면 오프셋 커밋 실패 → 재처리 위험
task-error-metrics total-record-errors · total-records-skipped · total-retries · deadletterqueue-produce-requests errors.tolerance=all일 때 조용히 버려지는 레코드를 잡는 지표

핵심 개념 (5) Streams 애플리케이션 관측

Streams MBean type과 용도
MBean type무엇을 보는가
kafka.streams:type=stream-metrics애플리케이션 전체 상태(state). REBALANCING이 길면 문제
kafka.streams:type=stream-thread-metrics스레드별 처리율, commit·poll·process 지연
kafka.streams:type=stream-task-metrics태스크별 처리율과 지연. 특정 파티션의 편중을 찾음
kafka.streams:type=stream-processor-node-metrics토폴로지 노드별 처리율. 어느 연산이 느린지
kafka.streams:type=stream-state-metrics상태 저장소(RocksDB) 지표. 복구 시간 추적
kafka.streams:type=stream-record-cache-metrics레코드 캐시 히트율. statestore.cache.max.bytes 튜닝 근거

Streams는 내부적으로 컨슈머·프로듀서를 쓰므로 위의 클라이언트 메트릭도 함께 노출됩니다. Streams의 lag은 결국 내부 컨슈머의 records-lag-max이며, kafka-consumer-groups --group {application.id}로도 볼 수 있습니다.

핵심 개념 (6) 수집 파이프라인과 알림 임계값

JMX는 로컬 프로세스의 메트릭을 노출하는 인터페이스일 뿐입니다. 대시보드를 만들려면 세 단계가 필요합니다.

  1. 노출 — JVM에 JMX_PORT(또는 JMX Exporter 에이전트)를 붙여 메트릭을 꺼낼 수 있게 합니다.
  2. 수집 — 익스포터가 JMX를 주기적으로 읽어 시계열 형식으로 변환하고, 수집기가 스크레이프합니다.
  3. 시각화·알림 — 대시보드에 올리고 규칙으로 알림을 만듭니다.

Kafka 자체에는 metric.reporters 설정이 있어 커스텀 리포터를 붙일 수도 있습니다. 중요한 것은 클라이언트 메트릭은 클라이언트 프로세스에서 꺼내야 한다는 점입니다 — 브로커를 감시해도 컨슈머의 poll 간격은 볼 수 없습니다.

알림 임계값 출발점 — 서비스 특성에 맞게 조정하세요
지표경고긴급
record-error-rate (프로듀서)> 0 이 5분 지속> 0 이 1분 지속
records-lag-max (컨슈머)SLA 처리 시간의 2배 분량SLA 처리 시간의 10배 분량
assigned-partitions= 0 이 5분 지속
time-between-poll-max> max.poll.interval.ms × 0.5> max.poll.interval.ms × 0.8
failed-rebalance-rate-per-hour> 0지속 증가
connector-failed-task-count> 0
total-records-skipped (Connect)> 0급증
UnderReplicatedPartitions> 0 이 5분 지속지속 증가
UnderMinIsrPartitionCount> 0
OfflinePartitionsCount> 0

반드시 외워야 할 설정값

관측과 직접 관련된 설정 (Apache Kafka 4.3)
설정소속 · 기본값시험 포인트
client.id클라이언트 · ""지정하지 않으면 메트릭·브로커 로그에서 구분 불가
group.instance.id컨슈머 · nullstatic membership. 지정 시 재시작 리밸런스 회피
max.poll.interval.ms컨슈머 · 300000time-between-poll-max와 비교하는 기준선
max.poll.records컨슈머 · 500poll 간격 초과의 1차 처방
session.timeout.ms컨슈머 · 45000last-heartbeat-seconds-ago와 비교
metadata.max.age.ms클라이언트 · 300000메타데이터 갱신 주기. 리더 변경 반영 지연의 원인
offset.flush.interval.msConnect 워커 · 60000source 오프셋 커밋 주기 (lag 관측 지연의 원인)
errors.log.enableConnect · false기본값이 false에러가 로그에 안 남습니다
errors.log.include.messagesConnect · false켜면 디버깅은 쉬워지지만 민감정보가 로그에 남습니다
replica.lag.time.max.ms브로커 · 30000ISR 판정 기준. URP 해석의 근거

자주 나오는 함정

함정 1 — 클라이언트 JMX lag vs 커밋 기반 lag

같은 "lag"인데 죽은 컨슈머에서 답이 갈립니다
관점records-lag-max (JMX)kafka-consumer-groups
무엇인가컨슈머가 fetch 응답으로 계산한 실시간 lag커밋 오프셋과 로그 끝의 차
어디서 나오는가컨슈머 프로세스브로커 (__consumer_offsets)
언제 발동fetch마다 갱신명령 실행 시점
컨슈머 사망 시메트릭이 사라짐 — lag이 0처럼 보임마지막 커밋 기준으로 lag이 계속 커짐
출제 형태"lag 알림이 안 울렸는데 데이터가 밀렸다" → JMX만 보고 있었던 경우

함정 2 — 하트비트 지표 vs poll 지표

어느 축이 터졌는지 구분하는 메트릭이 다릅니다
관점하트비트 축poll 축
관련 설정session.timeout.ms(45000)max.poll.interval.ms(300000)
볼 메트릭last-heartbeat-seconds-ago, heartbeat-ratetime-between-poll-max, last-poll-seconds-ago, poll-idle-ratio-avg
터지는 원인프로세스 정지, 장기 GC, 네트워크레코드 처리가 느림
혼동 시 결과처리 지연 문제에 session.timeout.ms를 올려 장애 감지만 늦어집니다
출제 형태"리밸런스가 반복된다. 원인을 좁히려면 어느 메트릭을 보는가"

함정 3 — UnderReplicatedPartitions vs UnderMinIsrPartitionCount

하나는 경고, 하나는 이미 장애
관점UnderReplicatedUnderMinIsr
의미ISR < 복제 계수ISR < min.insync.replicas
쓰기 가능가능불가 (acks=all)
클라이언트가 보는 예외없음NotEnoughReplicasException
혼동 시 결과URP만 감시하면 이미 쓰기가 실패하는 상태를 못 잡습니다
출제 형태"프로듀서 전송이 실패한다. 어느 브로커 메트릭을 보는가"

함정 4 — Connect 로그 위치 vs 상태 API

REST는 클러스터 전체, 로그는 노드 로컬입니다
관점GET /connectors/{name}/status워커 로그 파일
얻는 것상태, worker_id, 요약 trace전체 스택트레이스와 주변 로그
어디서아무 워커에 요청해도 동일해당 태스크를 실행한 워커 노드
워커가 죽었을 때그 워커의 태스크가 재배치되거나 UNASSIGNED죽은 노드의 파일을 직접 봐야 함
혼동 시 결과리더 워커 로그만 뒤지며 시간을 낭비합니다
출제 형태"실패 원인을 조사하는 순서를 배열하시오" (list order)

함정 5 — 태스크가 살아 있는 것 vs 데이터가 온전한 것

errors.tolerance=all의 대가
관점connector-failed-task-counttotal-records-skipped
보는 것태스크가 죽었는가레코드가 버려졌는가
errors.tolerance=none실패 시 증가0 (죽어 버리므로)
errors.tolerance=all0 유지조용히 증가
혼동 시 결과"커넥터가 초록불인데 데이터가 빠졌다" — 가장 발견이 늦는 유형의 사고
출제 형태"errors.tolerance=all을 켤 때 반드시 함께 할 것은?" → DLQ + skipped 알림

함정 6 — 메트릭의 소속을 바꿔 놓은 선택지

메트릭 이름과 노출 주체를 짝지어 두세요
메트릭노출 주체함정 선택지
records-lag-max컨슈머"브로커가 노출한다"
record-error-rate프로듀서"컨슈머 메트릭이다"
UnderMinIsrPartitionCount브로커"프로듀서에서 볼 수 있다"
sink-record-lag-maxConnect 워커"모든 커넥터에 있다" (source에는 없음)
poll-idle-ratio-avg컨슈머"Streams 전용이다"

진단 읽기 문제 대비

스니펫 1 — 이 지표 조합의 진단

컨슈머 메트릭 스냅샷
assigned-partitions            = 4
records-lag-max                = 1_240_000
poll-idle-ratio-avg            = 0.02
time-between-poll-max          = 268_000   (ms)
rebalance-rate-per-hour        = 0
commit-rate                    = 0.19      (/s)

스니펫 2 — 왜 알림이 울리지 않았는가

사고 타임라인
14:02  컨슈머 파드 3개 중 3개가 OOMKilled
14:03  대시보드의 records-lag-max 패널이 "No data"
14:03  lag 알림 규칙: records-lag-max > 100000 → 발동 안 함
15:40  하위 DB에 데이터가 없다는 제보로 인지

스니펫 3 — 이 status 응답에서 다음 행동

GET /connectors/orders-sink/status
{
  "name": "orders-sink",
  "connector": { "state": "RUNNING", "worker_id": "connect-1:8083" },
  "tasks": [
    { "id": 0, "state": "RUNNING", "worker_id": "connect-1:8083" },
    { "id": 1, "state": "FAILED",  "worker_id": "connect-3:8083",
      "trace": "org.apache.kafka.connect.errors.DataException: Failed to deserialize..." },
    { "id": 2, "state": "RUNNING", "worker_id": "connect-2:8083" }
  ]
}

도메인 미니 퀴즈

공식 문서 출처