主题
Kafka 监控指标与告警
一句话:Kafka 的告警要同时看数据面和控制面——副本与 ISR 决定还能不能安全写,Controller/KRaft quorum 决定还能不能选主和改元数据,lag 只是最后表现出来的结果。
适用版本:Kafka 3.9 / 4.0。4.0 起仅支持 KRaft,3.9 是最后一个支持 ZooKeeper 的版本。指标名、MBean 路径和默认值以官方文档为准;阈值只是起点,按自己的基线校准。
相关:01-架构与ISR、03-消费者与可靠性、05-Kafka生产踩坑。
为什么需要
Kafka 的故障常常是「静默」的:生产者开着重试,消费者 lag 在涨,业务侧只看到延迟变大;等应用开始报错,副本冗余可能早就没了。acks=all 在 ISR 不足时拒写,这是保护,不是故障本身;如果等到拒写才告警,已经失去了提前处置的窗口。
监控要回答三个问题:副本还够不够、控制面还有没有人负责、消费还能不能追上。三个问题的指标不同,处置顺序也不同。
核心机制
四个观察面
| 观察面 | 主要指标(JMX/MBean) | 反映什么 | 恶化后的先兆 |
|---|---|---|---|
| 副本 | kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions、UnderMinIsrPartitionCount、IsrShrinksPerSec/IsrExpandsPerSec | 副本是否齐全、ISR 是否在收缩 | 冗余丢失,下一步就是拒写 |
| 分区可用性 | kafka.controller:type=KafkaController,name=OfflinePartitionsCount | 有没有分区没有 Leader | 生产、消费直接失败 |
| 控制面 | KafkaController,name=ActiveControllerCount、ControllerStats,name=LeaderElectionRateAndTimeMs、KRaft quorum | 元数据操作是否有人负责 | 建 Topic、选主、重分配卡住 |
| 请求处理 | KafkaRequestHandlerPool,name=RequestHandlerAvgIdlePercent、SocketServer,name=NetworkProcessorAvgIdlePercent、DelayedOperationPurgatory,name=PurgatorySize | 请求线程和延迟操作是否排队 | 延迟上升、超时、重试放大 |
| 流量 | BrokerTopicMetrics,name=MessagesInPerSec、BytesInPerSec、BytesOutPerSec | 负载大小与方向 | 只作背景,不单独判故障 |
| 消费 | kafka-consumer-groups.sh 的 LAG;客户端 records-lag-max | 消费是否跟上 | 积压、下游变慢 |
| 存储 | kafka.log:type=LogManager,name=OfflineLogDirectoryCount(部分面板叫 LogDirOffline)、LogFlushRateAndTimeMs、磁盘使用率 | 日志目录和刷盘 | 分区 offline |
| 资源 | JVM GC、fd、网络 | 进程是否停顿 | 心跳和副本复制超时 |
消费 lag 不是 Broker 的 JMX 指标,要从消费组命令、客户端指标或 kafka_exporter 取。把 lag 当成「Broker 健康度」会误判:Broker 一切正常,消费者也可能因为下游慢而积压。
链一:ISR 收缩 → 低于 minISR → 拒写 → 重试放大
Follower 的复制进度落后超过 replica.lag.time.max.ms=30000(3.x 默认)就被移出 ISR,UnderReplicatedPartitions 大于 0。此时如果 ISR 数量仍然大于等于 min.insync.replicas,分区还能写,只是冗余在减少。
一旦 ISR 小于 min.insync.replicas(Broker 默认 1,RF=3 时通常设 2),acks=all 的生产者会收到 NotEnoughReplicas / NotEnoughReplicasAfterAppend。这是 Kafka 用「拒写」换「不丢数据」:宁可不可写,也不让少数派副本接管。
生产者的重试会继续打同一批分区,RequestHandlerAvgIdlePercent 下降、PurgatorySize 上升;请求排队又让 Follower 的 fetch 更慢,ISR 继续收缩。这条链的入口不是 lag,而是副本复制速度和请求处理余量。
链二:磁盘满 → 分区离线 → 消费停滞
日志目录写满后,Broker 无法追加,分区可能变成 offline,OfflinePartitionsCount 大于 0。生产端失败、消费端取不到数据,上游重试继续放大压力。
保留策略按整段删除,retention.ms 调小后不会立刻释放空间;活跃 Segment 也不会被删。所以磁盘告警必须在 70% 左右就开始处理,不能等到写失败。OfflineLogDirectoryCount 大于 0 说明目录已经不可用,通常伴随挂载、权限或磁盘故障。
链三:rebalance 风暴 → lag 暴涨
消费者在 rebalance 期间停止消费。单次 poll() 处理超过 max.poll.interval.ms=300000、session.timeout.ms=45000 内没心跳、滚动发布没配 group.instance.id、eager 与 cooperative 分配策略混用,都会让成员反复进出。
成员一变,分区重新分配,lag 在短时间内跳升;上游看到积压又加消费者,继续触发 rebalance。重启消费者不是恢复手段,它本身就是一次成员变更。
链四:控制面失联
各 Broker 的 ActiveControllerCount 之和应为 1。为 0 表示没有 Controller,建 Topic、分区重分配、Leader 选举都卡住;大于 1 在 ZooKeeper 模式下意味着脑裂风险。数据面可能还在跑,但故障一旦发生就无法自动恢复。
KRaft 下用 kafka-metadata-quorum.sh describe --status 看 LeaderId、HighWatermark、CurrentVoters;LeaderId=-1 或 HighWatermark 不再推进,说明元数据提交停了。describe --replication 能看到每个 voter 的 Lag。
指标怎么串起来读
先看 OfflinePartitionsCount 和 UnderMinIsrPartitionCount:大于 0 是「已经不可用/不可写」。再看 UnderReplicatedPartitions:大于 0 但未低于 minISR,是「还能写,冗余在丢」。最后看请求侧 RequestHandlerAvgIdlePercent、NetworkProcessorAvgIdlePercent 和 PurgatorySize,判断是容量问题还是副本问题。MessagesInPerSec 只用来解释负载背景。
最小上手
bash
bin/kafka-topics.sh --bootstrap-server localhost:9092 \
--describe --topic orders
bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
--describe --group order-worker
bin/kafka-log-dirs.sh --bootstrap-server localhost:9092 \
--describe --topic-list orders
# KRaft 控制面(地址按 controller listener 替换)
bin/kafka-metadata-quorum.sh --bootstrap-controller localhost:9093 \
describe --statusJMX 用 jmx_prometheus_javaagent 暴露到 9404:
bash
export KAFKA_OPTS="-javaagent:/opt/jmx_exporter/jmx_prometheus_javaagent.jar=9404:/opt/jmx_exporter/kafka.yml"
bin/kafka-server-start.sh -daemon config/kraft/server.propertiesyaml
lowercaseOutputName: true
lowercaseOutputLabelNames: true
rules:
- pattern: 'kafka\.(server|controller|network|log)<type=([^,>]+), name=([^,>]+)><>(\w+)'
name: 'kafka_$1_$2_$3_$4'
type: GAUGE规则决定最终指标名;带 delayedOperation=... 的 Purgatory 等复合 MBean 要另加带 labels 的规则。先用 curl -s localhost:9404/metrics | grep -E 'underreplicated|offlinepartitions|activecontroller' 确认,再写告警。
常见 PromQL(前三行按上面的规则生成,最后一行来自 kafka_exporter,按实际调整):
promql
sum(kafka_server_replicamanager_underreplicatedpartitions_value) > 0
sum(kafka_controller_kafkacontroller_offlinepartitionscount_value) != 0
sum(kafka_controller_kafkacontroller_activecontrollercount_value) != 1
sum by (consumergroup) (kafka_consumergroup_lag) > 10000生产动作
以下阈值均为起点,按基线校准;告警要带 Topic、分区和 Broker 维度。
| 告警 | 条件 | 级别 | 先做什么 |
|---|---|---|---|
| 副本不足 | UnderReplicatedPartitions > 0 持续 5m | P1 | 查掉队 Broker 的磁盘、GC、网络 |
| 低于 minISR | UnderMinIsrPartitionCount > 0 | P0 | 写入已被拒;恢复副本或切流,别先调大 lag 阈值 |
| 分区离线 | OfflinePartitionsCount > 0 | P0 | 查 Leader 存活、磁盘、日志目录 |
| 控制面异常 | ActiveControllerCount 之和不为 1;KRaft LeaderId=-1 或 HW 不推进 | P0 | 先恢复 quorum,不要重启全部 Controller |
| ISR 抖动 | IsrShrinksPerSec 持续大于 IsrExpandsPerSec;Leader 选举速率高于基线 | P1 | 查网络、GC、单条大消息和 replica.fetch.max.bytes |
| 请求饱和 | RequestHandlerAvgIdlePercent < 20% 或 NetworkProcessorAvgIdlePercent < 30% 持续 5m | P1 | 限流非核心生产或扩 Broker,不要先重启 |
| Purgatory 堆积 | Produce/Fetch 的 PurgatorySize 持续增长 | P1 | 对应 ISR 或 fetch 问题,先查副本 |
| 消费 lag | LAG 超过阈值且 10m 斜率向上 | P1/P2 | 扩消费者到分区数,再查 key 倾斜和下游 |
| 磁盘/目录 | 使用率大于 70% 预警、大于 85% 严重;OfflineLogDirectoryCount > 0 | P1/P0 | 降 retention、扩盘或迁分区;不删活跃 Segment |
| GC | Full GC 停顿大于 1s 或老年代大于 80% | P1 | 调堆、查泄漏;GC 会触发心跳和副本超时 |
先扩容/限流还是先重启:
- ISR 仍大于等于 minISR、
OfflinePartitionsCount=0:还能写,优先限流非核心生产、查掉队 Broker,不要重启 Leader。 - ISR 小于 minISR 或已有分区 offline:已经不可写/不可用,先恢复副本、磁盘或切流;重启 Broker 不会让磁盘变空,也不会让副本自动追上。
- 请求线程饱和但副本健康:限流或扩 Broker/分区;重启只会让请求重新排队。
- 消费 lag:先把消费者扩到分区数(不能超过),再优化单条处理;不要用
--reset-offsets --to-latest消 lag。 - rebalance 风暴:修
max.poll.interval.ms、静态成员和分配策略;不要靠反复重启消费者恢复。
边界
Kafka 的优势是分区顺序写、PageCache + sendfile 零拷贝、高吞吐和可回放日志;Connect、Streams、Flink 生态让它适合做事件总线和流式管道。副本机制在 RF=3、minISR=2、acks=all 下能给出明确的持久性边界。
代价是并行度被分区数锁死:分区只能增不能减,扩分区会改变 key 落点;rebalance 有停顿成本;副本复制、Controller/KRaft quorum 和磁盘保留都需要专门运维。它不擅长低延迟点对点调用,也不擅长复杂路由、任意延迟消息和业务级事务;这些场景 RocketMQ 更合适。
扬长避短的做法:用 Kafka 承载高吞吐、可回放、按 key 有序的流;分区数按目标消费者数和吞吐一次规划;核心 Topic 固定 RF=3、minISR=2;监控按分区和 Broker 展开,不要只看集群总量;业务消息的延迟、事务、过滤需求交给 RocketMQ 或应用层实现。
讲清楚
Kafka 的告警先分数据面和控制面。数据面看副本:URP 持续大于零说明冗余在丢,ISR 缩到
min.insync.replicas以下时acks=all会拒写,这是保护;上游一重试,请求队列和 Purgatory 就堆,延迟更高又逼更多 Follower 掉队。消费面看 lag 斜率和 rebalance,重启消费者只会再触发一次重平衡。控制面看ActiveControllerCount是否恰好为一、KRaft quorum 有没有 Leader、HighWatermark 是否推进;控制面挂了,建 Topic 和选主都会停。磁盘满会让分区 offline,先降 retention、扩盘或迁分区,别删活跃 Segment。Kafka 强在分区顺序写和高吞吐,代价是并行度受分区数约束、rebalance 和副本运维成本高;延迟消息、事务和复杂路由交给 RocketMQ。