Skip to content

监控与容量:十个指标看住集群 ​

Kafka 的 JMX 指标有几百个。真正需要配告警的只有十个,其余用于定位。

场景 ​

集群"感觉慢"时,先区分是写不进、读不出、还是副本不同步。这三类对应完全不同的指标。

一、打开 JMX ​

broker 默认不开 JMX。用环境变量开:

ini
# /etc/systemd/system/kafka.service 的 [Service] 段
Environment="KAFKA_JMX_OPTS=-Dcom.sun.management.jmxremote \
  -Dcom.sun.management.jmxremote.port=9999 \
  -Dcom.sun.management.jmxremote.rmi.port=9999 \
  -Dcom.sun.management.jmxremote.authenticate=false \
  -Dcom.sun.management.jmxremote.ssl=false \
  -Djava.rmi.server.hostname=10.0.0.31"
bash
systemctl daemon-reload && systemctl restart kafka
ss -lntp | grep 9999
text
LISTEN 0  50  0.0.0.0:9999  0.0.0.0:*  users:(("java",pid=28441,fd=112))

9999 端口绝对不能开公网

authenticate=false 时任何能连上的人都能读所有 JMX 指标,也能调用 MBean 上的写操作(比如改配置)。只允许内网 Prometheus 访问。

-Djava.rmi.server.hostname 必须写真实 IP。不写的话 RMI 会返回主机名,客户端解析不了就是 Connection refused。

二、十个必看指标 ​

#指标MBean正常值
1未同步分区数kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions0
2离线分区数kafka.controller:type=KafkaController,name=OfflinePartitionsCount0
3活跃 controller 数kafka.controller:type=KafkaController,name=ActiveControllerCount每台 0 或 1,全集群和为 1
4每秒入流量kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec对比基线
5每秒出流量kafka.server:type=BrokerTopicMetrics,name=BytesOutPerSec对比基线
6每秒消息数kafka.server:type=BrokerTopicMetrics,name=MessagesInPerSec对比基线
7请求处理线程空闲率kafka.server:type=KafkaRequestHandlerPool,name=RequestHandlerAvgIdlePercent> 0.3
8网络线程空闲率kafka.network:type=SocketServer,name=NetworkProcessorAvgIdlePercent> 0.3
9日志刷盘耗时kafka.log:type=LogFlushStats,name=LogFlushRateAndTimeMs99th < 10ms
10消费积压外部采集(见第六节)不持续增长

1. UnderReplicatedPartitions ​

bash
# 用 kafka-exporter 或 JMX 客户端取值,命令行快速版:
/opt/kafka/bin/kafka-topics.sh --bootstrap-server 10.0.0.31:9092 --describe --under-replicated-partitions | wc -l
text
0

持续 > 0 说明有副本跟不上。它不区分"慢"和"死",需要再看 IsrShrinksPerSec:

  • ISR 收缩但很快恢复 → 网络抖动/GC 停顿,可容忍。
  • ISR 持续收缩不恢复 → 副本 broker 有问题。

2. OfflinePartitionsCount ​

0 就是生产事故:这些分区的 leader 全挂了,不可读不可写。

bash
/opt/kafka/bin/kafka-topics.sh --bootstrap-server 10.0.0.31:9092 --describe --unavailable-partitions

3. ActiveControllerCount ​

每台 broker 的这个值要么 0 要么 1,全集群加起来必须恰好是 1。

  • 全 0:没有 controller,元数据变更全部停摆(分区 leader 选举、topic 创建)。
  • 出现两个 1:脑裂。极罕见,通常是 ZK 会话或 KRaft quorum 出问题。

4~6. 流量三兄弟 ​

text
BytesInPerSec   生产者写入(含副本复制)
BytesOutPerSec  消费者读取 + 副本拉取
MessagesInPerSec 消息条数(不含副本复制)

BytesInPerSec 突然掉 0 而业务还在跑,说明生产者连不上。BytesOutPerSec 远大于 BytesInPerSec,通常是消费者在追历史数据。

7~8. 线程池空闲率 ​

text
RequestHandlerAvgIdlePercent < 0.3  → 请求处理不过来,要加 num.io.threads
NetworkProcessorAvgIdlePercent < 0.3 → 网络线程饱和,要加 num.network.threads

这两个值降到 0 就是请求排队,客户端表现为超时。加线程能缓解,但根因往往是磁盘慢或分区过多。

9. LogFlushRateAndTimeMs ​

Kafka 靠操作系统页缓存,log.flush.interval.messages 默认极大(几乎不主动 flush),所以这个值平时很小。它变大说明:

  • 磁盘 IO 饱和。
  • log.flush.interval.messages 被调小了(不建议,flush 交给 OS 更好)。

10. 消费积压 ​

严格说这不是 broker 指标,是消费组指标。只能外部采集。

三、定位用的二级指标 ​

现象指标
leader 频繁切换kafka.controller:type=ControllerStats,name=LeaderElectionRateAndTimeMs
选了落后副本当 leaderkafka.controller:type=ControllerStats,name=UncleanLeaderElectionsPerSec
分区数增长kafka.server:type=ReplicaManager,name=PartitionCount
副本拉取慢kafka.server:type=ReplicaFetcherManager,name=MaxLag,clientId=Replica
生产者被限流kafka.server:type=BrokerTopicMetrics,name=ProduceMessageConversionsPerSec
请求总数kafka.network:type=RequestMetrics,name=RequestsPerSec,request=Produce
请求排队时间kafka.network:type=RequestMetrics,name=RequestQueueTimeMs,request=Produce
磁盘占用见 Topic 与分区 的 kafka-log-dirs.sh

UncleanLeaderElectionsPerSec > 0 是丢数据的确认信号。配合 unclean.leader.election.enable=false 后它应该永远是 0。

四、用 kafka-exporter 采集 ​

bash
docker run -d --name kafka-exporter -p 9308:9308 \
  danielqsj/kafka-exporter:latest \
  --kafka.server=10.0.0.31:9092 --kafka.server=10.0.0.32:9092 --kafka.server=10.0.0.33:9092
bash
curl -s localhost:9308/metrics | grep -E 'kafka_brokers|kafka_topic_partitions|kafka_consumergroup_lag' | head
text
# HELP kafka_brokers Number of Brokers in the Kafka Cluster.
# TYPE kafka_brokers gauge
kafka_brokers 3
# HELP kafka_topic_partitions Number of partitions for this Topic
# TYPE kafka_topic_partitions gauge
kafka_topic_partitions{topic="order"} 6
# HELP kafka_consumergroup_lag Current Approximate Lag per consumergroup and topic
# TYPE kafka_consumergroup_lag gauge
kafka_consumergroup_lag{consumergroup="order-group",topic="order"} 202

Prometheus 采集配置:

yaml
scrape_configs:
  - job_name: kafka
    static_configs:
      - targets: ['10.0.0.31:9308']
    scrape_interval: 30s

告警规则(Prometheus 语法):

yaml
groups:
  - name: kafka
    rules:
      - alert: KafkaUnderReplicated
        expr: sum(kafka_topic_partition_under_replicated_partition) > 0
        for: 5m
        labels: { severity: warning }
        annotations: { summary: "存在未同步分区 {{ $value }} 个" }

      - alert: KafkaConsumerLagHigh
        expr: sum by (consumergroup) (kafka_consumergroup_lag) > 100000
        for: 10m
        labels: { severity: critical }
        annotations: { summary: "消费组 {{ $labels.consumergroup }} 积压 {{ $value }}" }

      - alert: KafkaBrokerDown
        expr: kafka_brokers < 3
        for: 2m
        labels: { severity: critical }

五、不装 exporter 也能巡检 ​

bash
B=10.0.0.31:9092
K=/opt/kafka/bin

echo "brokers: $($K/kafka-broker-api-versions.sh --bootstrap-server $B | grep -c 'id:')"
echo "under-replicated: $($K/kafka-topics.sh --bootstrap-server $B --describe --under-replicated-partitions | grep -c Partition)"
echo "unavailable: $($K/kafka-topics.sh --bootstrap-server $B --describe --unavailable-partitions | grep -c Partition)"
$K/kafka-consumer-groups.sh --bootstrap-server $B --all-groups --describe 2>/dev/null \
  | awk 'NR>1 && $6 ~ /^[0-9]+$/ {s[$1]+=$6} END {for (g in s) printf "lag %-24s %d\n", g, s[g]}' | sort -k3 -rn
text
brokers: 3
under-replicated: 0
unavailable: 0
lag order-group              202
lag report-group             0

写进 cron,每 5 分钟跑一次,输出变化就告警。这比装一整套 Prometheus 快得多。

六、消费 lag 的正确算法 ​

text
LAG = LOG-END-OFFSET - CURRENT-OFFSET

但注意两点:

  1. read_committed 下 LOG-END-OFFSET 用的是 LSO(最后一个稳定 offset),未提交事务的消息不算在内,所以 lag 会显得偏小。
  2. 如果消费者不活跃(CONSUMER-ID 全 -),CURRENT-OFFSET 是上次提交的值,lag 可能已经很大但没人处理。

按时间估算处理进度:

bash
# 消费到 1761549000000 这个时间点需要多少 offset
/opt/kafka/bin/kafka-get-offsets.sh --bootstrap-server $B --topic order --time 1761549000000

七、容量评估 ​

磁盘 ​

text
所需磁盘 = 日增原始量 × 副本数 × 保留天数 ÷ 压缩比 × 1.3

实例:日增 80GB、RF=3、保留 7 天、zstd 压缩比 3.5、预留 30%:

text
80 × 3 × 7 ÷ 3.5 × 1.3 ≈ 624 GB

单块盘建议不超过 4TB(大了故障重建太慢),3 台每台挂 2 块 1TB 做 JBoss/RAID0 更现实。

网络 ​

text
入方向 = 生产速率
出方向 = 副本复制速率 (RF-1) × 生产速率 + 消费速率

RF=3、生产 100MB/s、消费 80MB/s:

text
入 ≈ 100 MB/s ≈ 800 Mbps
出 ≈ 2×100 + 80 = 280 MB/s ≈ 2240 Mbps

出方向是瓶颈。千兆网卡(125MB/s)根本扛不住这种量,需要万兆。

分区数 ​

约束上限
单 broker 分区数2000~4000(经验值)
单集群分区数20 万(ZK 模式瓶颈)、百万(KRaft)
每次 rebalance 的分区数几千以上就会明显变慢

八、告警阈值 ​

指标警告严重
UnderReplicatedPartitions> 0 持续 5 分钟> 0 持续 30 分钟
OfflinePartitionsCount> 0立即
ActiveControllerCount 总和≠ 1立即
RequestHandlerAvgIdlePercent< 0.3< 0.1
磁盘使用率> 70%> 85%
消费 lag> 10 万且增长> 100 万
UncleanLeaderElectionsPerSec> 0立即(已丢数据)
broker 数量少于预期少于 quorum 需求

九、踩过的坑 ​

现象根因处置
JMX 连不上java.rmi.server.hostname 没设填真实 IP
指标名字找不到版本差异(3.x 有部分改名)用 jconsole 或 jcmd 浏览 MBean 树确认
exporter 只连一个 broker只填了一个 --kafka.server每台都填
exporter 采集很慢集群 topic 太多加 --topic.filter 或调大 --kafka.version
lag 一直是 0消费组是 read_committed 且事务未提交看 LSO
告警风暴阈值太敏感(ISR 抖动就报)加 for: 持续时间
磁盘快满但 retention 很短segment 太大,删除粒度粗调小 log.segment.bytes