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
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇