Kafka集群ISR收缩告警误报排查

Kafka集群ISR收缩告警误报排查

记录时间:2026-08-03
环境:信创 ARM64 / RKE1 / Kafka 4.0.0 KRaft 3 节点(controller-0/1/2)/ namespace=tools / 存储 NFS(storageClass=nfs-dyck)

一、问题现象

生产 dyck Kafka 集群触发 Prometheus 告警 KafkaISR持续收缩,告警规则如下:

expr: kafka_server_replicamanager_total_isrshrinkspersec_oneminuterate > 0
for: 5m

告警来源 pod dyck-kafka-cluster-controller-2(节点 xc17-11)。告警触发后长时间无法自动消除,卡在 firing 状态,即便集群早已恢复正常。

排查时三个 broker 的指标值:

controller-2 (10.247.16.48):   3.77e-111   ← 触发告警的节点
controller-0 (10.247.227.143): 0
controller-1 (10.247.122.105): 0

二、排查过程

2.1 理解指标含义

ISRShrinksPerSec 属于 Kafka ReplicaManager 的一个 Meter,记录副本被移出 ISR(同步副本集合)的速率。ISR 收缩本身是 Kafka 的自愈机制——follower 跟不上 leader 时被踢出 ISR,追上后再加回来(ISR Expand)。Shrink 和 Expand 是成对动作。

2.2 查 Kafka 日志,定位收缩事件

kubectl logs -n tools dyck-kafka-cluster-controller-2 -c kafka --tail=300 \
  | grep -iE "shrinking|expanding|under replicated|replica|timeout"

关键日志(时间戳为北京时间):

[17:39:49] Disconnecting from node 2 due to request timeout.
[17:39:49] Cancelled in-flight BROKER_HEARTBEAT ... request timeout: 4500ms
[17:40:03] Disconnecting from node 2 due to socket connection setup timeout. timeout=11224ms
[17:40:15] Did not receive fetch request from the majority of the voters within 3000ms.
[17:40:17] leaderEndpoints=...controller-0...svc.cluster.local/<unresolved>:9093
[17:40:20] GroupCoordinator ... timed out after 5000ms
[17:40:29] Shrinking ISR from 2,0 to 2. Out of sync replicas: (brokerId:0, lastCaughtUpTimeMs:1785749983328)
[17:40:29] Shrinking ISR from 2,1,0 to 2,1 ...(批量收缩 __consumer_offsets-49 等)

lastCaughtUpTimeMs:1785749983328 换算成可读时间 = 北京时间 17:39:43。所有被踢的分区,broker 0 的最后同步时间都停在这个点,说明 broker 0 是整台失联,不是单个分区抖动。

2.3 排查 broker 0 是否重启(试错)

我最初怀疑 controller-0(broker 0)重启或 OOM:

kubectl get pod -n tools -l app.kubernetes.io/instance=dyck-kafka-cluster -o wide
controller-0   3/3   Running   2 (110d ago)   110d   10.247.227.143   xc17-8
controller-1   3/3   Running   2 (110d ago)   110d   10.247.122.105   xc17-10
controller-2   3/3   Running   2 (110d ago)   110d   10.247.16.48     xc17-11

RESTARTS=2 都是 110 天前,与本次无关;controller-0 最近 4 小时无任何 ERROR 日志,也没有 previous container。排除 broker 进程重启。

试错:「broker 0 重启」的假设不成立。进程一直活着,但所有分区同一毫秒停止 catch up,指向节点级隔离,不是进程问题。

2.4 查节点级证据,权限受阻

想确认节点 xc17-8 是否 NotReady 或网络抖动:

kubectl describe node xc17-8
# → Forbidden: User "swj" cannot get resource "nodes" at cluster scope

kubectl get events -A
# → Forbidden: cannot list events at cluster scope

运维账号 swj 只有命名空间级(Pod/Service/logs)读权限,查不了 nodes 和集群级 events。节点级证据(NodeNotReady 事件、dmesg、CNI 日志)需 cluster-admin 账号才能取。

2.5 通过监控补位:发现负载飙升

虽然 kubectl 查 node/events 受权限限制,但 Prometheus 监控补上了关键证据:故障时段(17:39 前后)节点 xc17-8 的 load average 突然飙升到 150+。这个数据把根因从「网络瞬断」修正为「负载飙升导致节点卡死」——网络层超时只是 CPU 过载的连锁反应(详见三、根因分析)。

2.6 确认偶发首次 + 识别 EWMA 残值

用户确认该告警偶发一次、历史首次。排查期间多次查指标,发现一个关键现象:

第几次查 controller-2 的值 指数
第 1 次 3.77e-111 -111
第 2 次 1.26e-114 -114
第 3 次 5.75e-119 -119

指数持续向负方向走,值在持续变小趋零。第三次差点误读:看到 5.75e-119,尾数 5.75 看着变大,但指数从 -114 变 -119(小了 10 万倍),整体反而缩小约 2 万倍。

三、根因分析

3.1 直接诱因:节点 xc17-8 网络瞬断

证据链:

  • broker 0 进程健康(无重启、无 GC、无错误)→ 排除进程
  • 所有分区同一毫秒(17:39:43)停止 catch up → 整机隔离
  • CoreDNS <unresolved> + socket 建立即超时 11s → 网络层断连
  • broker 1(xc17-10)未受影响(ISR 2,1,0→2,1 保留 broker 1)→ 非全局故障,xc17-8 单边问题

结论:节点 xc17-8 在 17:39:43 出现一次突发短事件(持续约 1-2 分钟),controller-0 的 Kafka 关键线程(fetch/heartbeat/网络处理)被瞬时阻塞,被时任 leader(controller-2)按 replica.lag.time.max.ms=40000 规则批量踢出 ISR;事件分钟后恢复,ISR 自动 re-expand。

监控数据汇总(事后从 Prometheus 调取)

指标(17:40 故障时刻) 数值
1 分钟负载(load1) 瞬时尖峰 120.41
15 分钟负载(load15) 16.66(平滑值,尖峰被长窗口拉平)
用户使用率(user) 6.06%
系统使用率(system) 0.85%
磁盘 IO(iowait) 4.90%
CPU 核数 96

根因判断的曲折(排查过程如实记录):日志先指向「网络瞬断」→ 看到 load 飙升(实为 load1 尖峰 120)一度改成「负载飙升卡死」→ 补充 96 核后又推测「NFS IO wait 堆积」。但 iowait 才 5%、user 才 6%,磁盘 IO 和 CPU 都不是主导;load1 尖峰 120 在 CPU/磁盘都不忙的情况下,更像 D 状态进程短暂堆积(等待网络/锁/内存回收等非磁盘资源)。

收敛结论(5 节点同时异常 → 集群级共享资源事件):17:40 发生了一次集群级事件——5 个 worker 节点同时出现 IO+CPU 异常拉升,xc17-8 的 Kafka 因对延迟最敏感(replica fetch 40s 阈值 + GroupCoordinator 写 5s 超时连锁)率先触发 ISR shrink,其他节点业务容忍度高未告警。几分钟后恢复,ISR 自动 re-expand。

根因首选:NFS 服务端 / 共享存储后端瞬时抖动。5 节点同时异常必然源于共享资源;多节点共享 NFS + Kafka 数据在 NFS + GroupCoordinator 写超时 5s(NFS 慢铁证),NFS 服务端最符合。这套解释第一次串通所有现象:
– 5 节点同时 IO+CPU 异常 = 共享 NFS 服务端抖动
– 只有 xc17-8 告警 = Kafka 延迟敏感度最高,其他业务容忍
– load1 飙到 120 = 进程等 NFS 进 D 状态堆积
– iowait 才 5% = NFS 等待部分不计入本地 iowait
– node_nfs_requests 平稳 = 服务端慢时客户端请求卡在等响应、发不出去,速率反而不升(平稳是服务端慢的症状,不是排除依据
– TCP 重传平稳 = 不是网络丢包,是服务端处理慢

待确认:NFS 服务端 17:40 的 CPU/IO/负载、NFS 服务端日志、存储后端(Ceph/本地阵列/分布式)是否抖动。

跨组织边界(最终结论):分布式存储由运营商托管,无权查看服务端。

【2026-08-04 厂家反馈】运营商反馈 NFS 响应时间无异常(17:39-17:50 时段),排除了「NFS 服务端处理慢」。客户端证据(5 节点同时异常 + 写超时 5s)与服务端反馈矛盾,缺口在「客户端到服务端的存储网络」或客户端共性,但该路径运营商托管无权查。根因搁置,待下次复现或运营商提供存储网络层数据。

客户端侧防御现状:
– ✅ ① 告警规则修正已 apply 落地(2026-08-04,>0.5 阈值生效,旧告警自动停止)
– ❌ ② Kafka 日志刷新延迟告警:实测 kafka_log_* 整组指标为空(JMX Exporter 未采集 kafka.log:*),flush 延迟不可用,搁置

3.2 告警误报根因:EWMA 永不归零 + >0 阈值

ISRShrinksPerSec 是 Yammer Metrics 的 Meter 类型,_oneminuterate 是其 OneMinuteRate,一个指数加权移动平均(EWMA),时间常数约 1 分钟。

EWMA 事件后指数衰减,浮点数下永不精确归零。

>0 阈值 + EWMA 不归零 = 告警一旦触发就持续 firing 数小时甚至数天

17:39 那次收缩事件后 4-5 小时,EWMA 残值仍观测到 1e-114 量级,满足 >0,叠加 for: 5m,告警一直卡在 firing 无法自动恢复,会一直响到人工干预。

三节点残值差异也印证这一点:ISR Shrink 由分区 leader 执行,controller-2 当时是多数分区 leader(执行了批量踢出),故残留 EWMA 尾巴;controller-0 当时失联不执行 shrink、controller-1 leader 分区少,EWMA 早已归零。

四、解决方案

4.1 告警规则修正(保留 + 改阈值,非删除)

试错:最初我推荐直接删除该规则,理由是「UnderReplicated 三道防线已覆盖」。用户指出 ISR Shrink 作为过程指标有「早期预警 + 捕捉慢性抖动」的独特价值,三道防线(都要求持续状态)不完全覆盖。采纳,改为保留并修正。

修正后的规则:

- alert: KafkaISR频繁收缩
  # _count 指标未暴露,只能用 OneMinuteRate(EWMA)。EWMA 事件后指数衰减但浮点永不归零,
  # 故禁用 >0 阈值(会致告警永久 firing 卡死)。取 0.5/s + for 10m:
  # - 过滤 EWMA 衰减尾巴(如 1e-114 残值)与瞬时抖动(几分钟内衰减到 0.5 以下,撑不满 10m)
  # - 只在"持续显著收缩"(约 30+ 次/分钟、持续 10 分钟)时告警
  expr: kafka_server_replicamanager_total_isrshrinkspersec_oneminuterate > 0.5
  for: 10m

改动要点:

参数 原值 新值 作用
阈值 > 0 > 0.5 修复 EWMA 永不归零导致的告警卡死
for 5m 10m 过滤单次批量收缩事件的衰减尾巴
alert 名 KafkaISR持续收缩 KafkaISR频繁收缩 更贴合「rate 超阈值」的语义

4.2 ISR Shrink 告警的独特价值(为什么保留)

三道防线只覆盖「副本持续不足」的状态:

规则 覆盖场景
UnderReplicatedPartitions > 0 (for 3m) 副本持续不足
UnderMinIsrPartitionCount > 0 (for 1m) 副本数跌破 min.insync.replicas
OfflinePartitionsCount > 0 (for 1m) 分区无 leader

ISR 频繁收缩补两个盲区:

  1. 早期预警:Shrink 发生时 UnderReplicated 才刚 >0,还没到 3m 阈值
  2. 慢性抖动捕捉:broker 反复让副本进出 ISR(每次自愈快,UnderReplicated < 3m 不告警),是节点不健康信号

五、验证

已 apply 落地(2026-08-04):用户用运维账号执行 kubectl apply -f test.yaml(内容=仓库 kafka-rules.yaml),prometheusrule/kafka-rules configured 生效。Warning(missing last-applied-configuration annotation)无害——资源最初非 apply 创建,本次自动补注解,不影响生效。当前 firing 的旧告警(>0)因阈值变 0.5 自动停止。

apply 命令(参考,正式维护用仓库原文件而非 test.yaml):

kubectl apply -f kafka-rules.yaml

apply 后验证:

# 确认规则名已更新
kubectl get prometheusrule kafka-rules -n monitoring \
  -o jsonpath='{.spec.groups[?(@.name=="kafka.replication")].rules[*].alert}'
# 预期:Kafka存在未同步副本 KafkaISR频繁收缩

# 当前那条 firing 的旧告警(>0)会因阈值变 0.5 立即停止

阈值校准参考(apply 前或后):

# 查 17:39 事件 OneMinuteRate 的峰值,作为阈值参考
max_over_time(kafka_server_replicamanager_total_isrshrinkspersec_oneminuterate{systemName="dyck",pod="dyck-kafka-cluster-controller-2"}[6h])

峰值远大于 0.5 则阈值合理;接近 0.5 则下调到 0.1。

六、注意事项

6.1 科学计数法误读

5.75e-119 不是「5 点多」,是 5.75 × 10⁻¹¹⁹。看这种 EWMA 指标,只看 e 后面的指数:

  • 指数 e0 或正数 → 真实速率,值得警惕
  • 指数 e-1 ~ e-2 → 接近阈值,留意
  • 指数 e-100 级别 → EWMA 残值,等于 0,忽略

6.2 swj 运维账号权限边界

swj 账号只有命名空间级(Pod/Service/logs)读权限,kubectl describe node 和集群级 kubectl get events -A 均 Forbidden。排查节点级故障(NodeNotReady、dmesg、CNI 日志)需 cluster-admin 或更高权限账号。

6.3 _count counter 未暴露

JMX Exporter 只暴露了 _oneminuterate(EWMA),没有暴露 _count(累计计数 counter)。否则可用更精确的净收缩判据:

# 理想方案(_count 存在时):10 分钟净收缩超阈值才告警
increase(..._isrshrinkspersec_count[10m])
  - on(systemName, instance) increase(..._isrexpandspersec_count[10m]) > 5

6.4 NFS 存储风险

本次 GroupCoordinator 写 __consumer_offsets-49 超时 5s,印证 NFS 写延迟。Kafka 用 NFS(storageClass nfs-dyck)是已知风险,长期治本应迁 Longhorn 本地块存储,涉及数据迁移,需单独规划。

6.5 排查「网络瞬断」的正确指标

排查网络层问题不要看 TCP 套接字数量(Netdata Sockstat 的 Allocated/In-Use/TIME_WAIT),连接数平稳不能证明网络质量正常——网络瞬断时连接往往还在,但包在丢、在重传。要看的是:

# TCP 重传段数(网络丢包/不稳定的直接信号)
rate(node_netstat_Tcp_RetransSegs{instance=~".*xc17-8.*"}[5m])
# 网卡错误/丢包
increase(node_network_receive_errs_total{instance=~".*xc17-8.*"}[5m])

另外注意监控图的时间跨度:排查瞬时事件(持续 1-2 分钟)要用 1-2 小时内的窗口,跨 6 小时以上的图会把尖峰平滑掉,看不到真相。

6.6 磁盘负载指标:iowait / node_disk / node_nfs 的区别(按存储类型选)

判断磁盘/IO 是否瓶颈,要分清三个指标,且必须区分本地盘NFS 挂载

指标 反映什么 适用
iowait CPU 干等 IO 的时间占比(CPU 视角) 不能单独判断磁盘负载,iowait 低 ≠ 磁盘不忙
node_disk_io_time_seconds_total(util) 本地块设备繁忙度 只反映本地盘(系统盘/本地数据盘),不含 NFS
node_nfs_requests_total NFS 客户端请求量 反映 NFS 挂载的 IO 活跃度

本项目 Kafka 数据盘是 NFS(storageClass=nfs-dyck),所以:

  • 查 Kafka 数据盘负载 → 用 node_nfs_*不是 node_disk_*(后者只看本地系统盘)
  • node_disk_io_time_seconds_total 对本场景只能看系统盘,不能判断 Kafka 数据 IO

坑(本次实测):曾想用 node_disk_io_time_seconds_total 查 Kafka 数据盘是否打满,被指出无效——因为数据在 NFS,本地盘指标看不到。指标选择必须匹配存储类型,NFS 存储要看 node_nfs_*

七、参考资料

  • Kafka Metrics:kafka.server:type=ReplicaManager,name=ISRShrinksPerSec(Yammer Meter 类型)
  • JMX Exporter:OneMinuteRate = EWMA,时间常数约 1 分钟,事件后浮点永不归零
  • 相关指标:UnderReplicatedPartitions / OfflinePartitionsCount / UnderMinIsrPartitionCount / ActiveControllerCount
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇