背景
5 年网络工程,现负责一批跨机房 HTTP/1.1 + gRPC over HTTP/2 调用链,单侧稳态存量长连接 2 万上下,新建连接速率峰值 ~800 conn/s,内核 5.15。日常告警体系是 Prometheus + node_exporter + eBPF 探针(bpftrace 挂 tcp_set_state),TCP 重传告警我按 #1753 的思路已经拆到连接级了,但 RST 这一族指标一直卡在「告警了不敢信、不告又怕漏」 的状态。
现象(3 次真实复盘)
- 某周三凌晨,RST 速率从基线 12/s 飙到 480/s,持续 90s。事后看是上游一次滚动发布:旧 Pod 优雅下线时连接池里的连接被主动 close,RST 源 IP 全是我们自己侧——完全正常行为,但告警按 P1 拉了人。
- 某次真故障(下游 accept 队列溢出,参考 #225 那类),RST 速率只涨了 40/s(基线 12/s → 52/s),因为业务是长连接池,新建连接本来就不多,RST 绝对值小,按绝对值告警漏报了,等 P99 涨到 300ms 才有人去看。
- 另一次更诡异:RST 源 IP 不是对端、不是我们侧,是中间某台 NAT 设备代发的(参考 #1756 的路由翻转案例),
nstat -az 里 TcpOutRsts 和 TcpInRsts 同时涨,抓包才定位到——用 nstat 两个计数器根本分不出是「我们主动发 RST」还是「中间设备代 RST」。
已尝试的排查与结果
nstat -az 采 TcpOutRsts / TcpInRsts,按 5min 窗口算 delta:能看趋势,但区分不了「应用主动 close」和「内核因 backlog 溢出代 RST」——两者的内核路径最终都走 tcp_send_active_reset 或 tcp_send_passive_reset,计数器层面没有更细的字段。
- eBPF 挂 tcp_set_state 抓 state 迁移到 TCP_CLOSE + reason:能拿到 4-tuple 和触发状态,但拿不到「为什么内核要发这个 RST」(是 backlog 满、是应用 close、是半开超时,还是中间设备),需要再挂更多 hook 点拼。
- 按「对端源 IP」聚合 RST 速率:解决了「我们侧 vs 对端侧」的二分,但没解决「我们侧内部:应用主动 vs 内核代 RST」的细分,也没解决中间设备代 RST 的识别。
- 告警阈值:目前写死 100/s,误报和漏报各占一半,没有推导依据。
期望得到的答案形态
- RST 的「最小可区分观测集」到底是哪几个指标:nstat 哪几个计数器 + eBPF 挂哪几个 hook 点 + 抓包看什么字段,才能把「应用主动 close / 内核 backlog 溢出代 RST / 中间设备代 RST / 半开超时」四种来源在 5 分钟内人工可判?(不需要完全自动化,能让人 5 分钟内定责就行)
- 阈值公式:你们线上 RST 告警是用绝对值、相对基线的 z-score、还是「RST 占新建连接的比例」?有没有一个能**同时压住「滚动发布误报」和「长连接池漏报」**的公式?最好给一个你们真实跑过的数。
- 中间设备代 RST 的识别信号:除了抓包看源 IP 不是对端,还有没有更轻量的在线信号(比如 RST 的 TTL、窗口值特征、ACK 号特征)能在告警时直接标出来?
- 排除「滚动发布」这个最大误报源:你们的做法是把 K8s 的 Pod 下线事件和 RST 告警时间窗对齐做抑制,还是有更干净的办法?
引用
- #1756(路由翻转 RST 排查,中间设备代 RST 的真实案例)
- #225(listen backlog 溢出导致 RST 的定位方法)
- #1753(TCP 重传率告警下沉到连接级的思路,我按这个做了重传但 RST 卡住了)
有真实线上数据的优先,公式和命令能贴最好。