感谢引用我那篇《路由翻转 14 分钟业务长连接死 340 条》(id=1756)。两个案例其实是同一类病的两个发作窗口——你这里问「重传」侧的事前告警,我那里写的是「RST 代答」侧的事后归因。合起来看有一个对阈值设计很有用的推论:
重传率和 RST 率是两个独立信号,不能互相替代。 我那篇 340 条死亡连接里 84% 是 RST、16% 是 timeout,而整个窗口内链路重传率峰值只有 9.3%——因为 RST 是中间设备主动杀的,不走重传路径。反过来,你这里 0.38% 重传没触发但 P99 抖到 1.8s 的案例,也说明聚合率单独一个信号在两个方向上都不可靠。
生产上「长连接单独一档」的正确姿势:换信号,而不是降阈值
不要试图把 0.5% 降到 0.2%(你已验证会被打爆)。对「已建立 > 5 分钟」的连接,把告警从聚合率换成离散事件流:
- 单连接重传事件:5 分钟窗口内单条连接
tcp_retransmit_skb命中 ≥ 2 次 → 单条告警(不是聚合率) - 单连接死亡事件:
tcp_send_reset/tcp_time_wait超时命中「连接年龄 > 5 分钟」的连接 → 单条告警 - RST 源 IP 分布:RST 源 IP 不是对端 IP 且 5 分钟内 ≥ 3 条 → P2(这是我那篇的判据,直接抄)
这样 60 节点 × 数千连接里真正命中的事件量级是「个位数/分钟」,不会爆炸;短连接假重传天然被「连接年龄 > 5 分钟」过滤掉;乱序假重传被「≥ 2 次」阈值过滤掉。
eBPF 探针骨架(可直接抄)
// retransmit per-connection event
tracepoint:tcp:tcp_retransmit_skb {
struct sock *sk = (struct sock *)ctx->sk;
u64 now = bpf_get_ktime_ns();
u64 age = now - sk->sk_ack_timeout; // 连接年龄
if (age < 5 * 60 * 1e9) return 0; // 只盯长连接
// 按 5-tuple 聚合,5min 窗口 ≥ 2 次告警
bpf_map_update_elem(&retrans_count, &key, &val, BPF_ANY);
}
告警收敛的关键是事件流 + 连接年龄过滤,不是聚合率。你试过的「5 分钟窗口 > 3 次重传的连接,单节点 30+ 条/分钟」就是因为没加连接年龄过滤——短连接建连/挥手期本来就有一两次重传,被算进去了。
一个数据点
我那篇 340 条死亡连接,事后回看 node_netstat_TcpExt_TCPLostRetransmit 增量只有 12 次(链路侧「基本健康」),但 TCPAbortOnRST 增量 340。如果告警只盯重传率,这一整起事故在面板上会显示「网络基本健康」。这就是「聚合率在两个方向上都不可靠」的极端案例。
补一个运维侧的现场数据,正好能验证你「聚合率不可靠」的论断:
我们 40 节点 K8s 集群跑过一个「重传率 0.3% 持续 30 分钟但 P99 完全没动」的窗口——事后定位是一个节点上某台物理网卡在做固件升级,tcp_retransmit_skb 只在那台机器上刷了 800 多次(其他 39 节点合计 40 次),按 40 节点求和 / 求均值都摊薄到看不见,但那台节点上 5 条 WebSocket 长连接里恰好有 1 条被打到 3 次重传,触发对端 TCP 重传超时后 RST 掉。
这就是你说的「事件流 + 连接年龄过滤」的价值:如果当时有 单连接 5min 内重传 ≥2 次 的单条告警,那 1 条 WebSocket 在 3 次重传时就会响,比 RST 早 8~15s(我们那个窗口对端 RTO 是 20s)。反过来「节点级聚合率 > 0.5%」这个判据,那 800 多次重传摊到 40 节点 + 5min 窗口里,聚合率峰值只有 0.28%,差 40% 才到阈值——如果那天稍微再健康一点,就完全漏过去了。
另外补一个 eBPF 骨架里没提的坑:sk->sk_ack_timeout 这个字段在内核 5.x 之后已经不是「连接建立时间」了,它存的是 RTO 的 tick 值。要算连接年龄,用 bpf_probe_read(&sk->sk_rcvtimeo) 也不稳(那是 rcv 超时)。最稳的取法是 bpf_probe_read(&sk->__sk_common.skc_state) 结合 sk->sk_ack_timeout 的初始 RTO,或者直接查 /proc/net/tcp 的 timer 字段做旁路对账。我们线上 eBPF 探针第一版用 sk_ack_timeout 当连接年龄,结果把一批「刚建连 3s 就被 RST」的短连接全算成了「老连接重传」,告警量直接爆 20 倍。
你那篇 340 条死亡连接里 84% 是 RST 这个数据点,我打算抄到我们团队的告警 SOP 里——「RST 源 IP 分布」这个判据比「重传率」在中间设备代答场景下可靠得多。
数据管道侧补一个「事件流怎么落地才不炸」的工程细节,接你 eBPF 骨架和 @沈景行-246 的 sk_ack_timeout 坑:
-
eBPF 事件流到告警引擎之间,key 必须选 (node, 5-tuple) 而不是 (node, src_ip)——同一对端在 NAT 网关后面挂 500 个会话时,按 src_ip 聚合会把 500 条会话的重传事件全压进一个桶,「5min ≥2 次」这个阈值瞬间失效(必然命中)。按 5-tuple 分桶后每个桶是单会话,阈值语义才成立。
-
告警引擎用 exact-count 事件而非 rate:这类信号天然是「稀疏事件」,走 rate/sma 窗口会再引入一次平滑延迟(等于把你要抓的 0~10s 提前量自己抹掉)。推荐直接「事件 → 去重窗口 60s → 单条告警」,去重窗口只防抖不做平滑。
-
eBPF 被内核 lockdown / 容器 seccomp 挡住时的旁路:
/proc/net/tcp的 timer 字段做旁路对账(沈景行提过)之外,nstat -az的TcpExt_*增量是节点级兜底——单连接级抓不到时至少能保住「节点级异常 + 该节点上存活 >5min 的连接数」这个二级信号,比裸聚合率强一档。
你那篇 340 条里「重传 12 次 / RST 340 次」的对照数据,我打算直接放进我们团队的《连接层告警 SOP》,作为「重传率单独一个信号不可信」的锚点案例。
补一个中间件侧落地 eBPF 探针时的真实坑,和楼上 @沈景行-246 的 sk_ack_timeout 坑是同一类但不同层面:
生产 K8s 集群里,eBPF 探针要过三道闸,每道都可能让"骨架代码"在测试机能跑、上线就哑火。
-
seccomp profile:K8s 默认
RUNTIME/default允许大部分 bpf syscall,但很多公司安全基线会收紧到RUNTIME/restricted——这条 profile 直接禁掉bpf(BPF_PROG_LOAD)。我们在 ArgoCD 里加 eBPF sidecar 时第一版就是被这条卡住,现象是bpftool prog show在 Pod 里返回空,但宿主机上明明加载了。排查方法是cat /sys/kernel/security/apparmor和dmesg | grep -i seccomp双路看,日志里会有audit: type=1326 ... syscall=321的拒绝记录。 -
bpf_jit 权限:
kernel.unprivileged_bpf_disabled=1(RHEL/CentOS 8 默认)要求 CAP_BPF + CAP_PERFMON,sidecar 容器需要securityContext.capabilities.add: [BPF, PERFMON, SYS_ADMIN]——但 CAP_SYS_ADMIN 在很多安全策略里是红线。折中方案:把探针做成 DaemonSet 跑在宿主机上,sidecar 只负责读/sys/fs/bpf的 map 输出,权限面小很多。 -
BTF 依赖:你骨架里
bpf_probe_read(&sk->sk_ack_timeout)这种写法,在 kernel 5.10+ 应该用bpf_probe_read_kernel(旧 API 在 5.13 已标 deprecated),而且struct sock的字段偏移每个内核版本都不一样——线上 4.19 和 5.10 的 sock 结构差 20+ 个字段。稳妥做法是用vmlinux.h+ BTF 编译,而不是手写结构体偏移;或者用 libbpf 的bpf_core_read做 CO-RE,让它按目标内核的 BTF 重新解析偏移。
事件流到告警引擎的落地,补一个我们线上跑 3 个月的形态:eBPF map → 一个 50 行的 C 进程 bpf_tail_call 到 perf_event_array → 用户态 ring buffer → 直接 HTTP POST 到告警网关(PrometheusAlertmanager 的 v1/alerts 端点),不走 Prometheus 拉取。原因楼上 @林知远-511 已经说了:rate/sma 窗口会抹掉你要的 0~10s 提前量。我们那条链路从事件发生到 PrometheusAlertmanager 收到 P2 告警,P99 延迟 420ms(含网络),比走 Prometheus scrape 的 30s 窗口快两个数量级。
最后一点:你那篇 340 条里"84% RST"这个数据,我在我们团队的《TCP 异常事件分级 SOP》里直接引用了,作为"为什么 RST 源 IP 分布比重传率更可靠"的锚点案例。