线上接口偶发超时,最后发现是 conntrack 打满:一次网络故障排查实战
凌晨一点,业务群里先响的是一句很朴素的话:"接口没挂,但就是时不时 504。"
现象很怪。应用日志里没有明显报错,Nginx 的 upstream timeout 只在少量请求上出现;监控看 CPU、内存、磁盘都不高,Pod 也没重启。更麻烦的是,这不是全量故障,而是高峰期偶发超时:同一批用户里,有的人秒开,有的人一直转圈。
这类问题最容易把人带进"应用没报错,那就不是网络问题"的坑里。可现实往往相反:**很多接口超时,根因不在应用层,而在连接建立和转发路径上。**这次事故最后定位到的,就是 Linux 节点上的 conntrack 表被打满,导致新连接无法稳定建立。
本文按真实排障顺序,拆一遍这类故障该怎么查,重点放在:现象识别、抓包验证、关键指标、修复动作,以及如何避免下次再被同一种坑教育。
一、先描述故障,而不是先猜原因
当时的路径大致是:
用户请求 → LB → Nginx Ingress → Service → Pod
表面症状:
- 只有一部分请求超时,不是全挂
- 超时集中在流量上涨时段
- 应用实例健康检查正常
- 单机压测未必复现,线上突发流量更容易触发
这时不要上来就改超时时间,也别先重启一排 Pod。先问三个问题:
- 超时发生在连接建立前,还是建立后?
- 是某个实例异常,还是节点级/网络层异常?
- 失败请求有没有明显的协议层特征?
这三个问题决定你后面是看应用线程池、GC、SQL,还是看 NAT、连接跟踪、队列、丢包。
二、先看基础面:不是 CPU 问题,不代表不是内核问题
很多同学看到节点资源空闲,就会下意识排除主机层问题。其实网络类故障常见的一种误判,就是把"资源利用率低"理解成"系统没压力"。
先在异常节点上看几项:
bash
uptime
ss -s
netstat -s
cat /proc/sys/net/netfilter/nf_conntrack_max
cat /proc/sys/net/netfilter/nf_conntrack_count
如果你看到 nf_conntrack_count 已经非常接近 nf_conntrack_max,就要立刻警觉。因为在 NAT、Kubernetes Service 转发、容器网络这些场景里,conntrack 是核心路径之一。表满了以后,新连接可能直接被丢弃或无法正确建立,应用层只会看到"超时"。
一次典型现场可能是这样:
bash
$ cat /proc/sys/net/netfilter/nf_conntrack_max
262144
$ cat /proc/sys/net/netfilter/nf_conntrack_count
261981
看起来还没满?但在突刺流量下,它可能在几秒内冲顶。此时你再结合内核日志:
bash
dmesg -T | grep -i conntrack
如果出现类似下面的内容,方向基本就对了:
text
nf_conntrack: table full, dropping packet
这句话的翻译很直接:连接跟踪表满了,包开始丢。
三、为什么会是"偶发超时"而不是"全站挂掉"?
因为 conntrack 打满之后,不是所有已建立连接都会同时断掉,更多是新建连接受影响。于是你会看到:
- 长连接业务还勉强正常
- 部分复用连接的请求继续成功
- 新连接比例高的接口更容易超时
- 某些客户端重试后又能成功
这也是为什么故障在业务侧看起来像"抖一下",而不是完全不可用。
如果服务之间大量使用短连接,或者上游网关到后端没有很好地复用连接,问题会放大得更快。
四、抓包别贪大,先抓最能说明问题的阶段
很多人一听抓包就直接全接口开抓,最后得到一个 2GB 的 pcap,然后在 Wireshark 里迷路。正确做法是:围绕连接建立过程抓关键包。
先在异常节点或网关上抓目标端口:
bash
tcpdump -i any host 10.0.12.34 and port 8080 -nn -tttt -vv -c 200
如果问题是连接建立阶段异常,你会重点看到几种模式:
1)只有 SYN,没有后续 ACK
text
10:11:02.101 client > server: SYN
10:11:03.105 client > server: SYN
10:11:05.110 client > server: SYN
这通常说明新连接发起了,但后续握手没走完。原因可能是:
- 包在中间路径被丢
- 目标没收到
- 目标回复了,但回包没回来
- conntrack/NAT 状态异常,导致转发链路不完整
2)SYN、SYN-ACK 出现了,但客户端还在重传
这种情况要结合双向抓包看。单边抓包很容易误判成"对端不回",实际可能是中间设备或主机内核把状态搞丢了。
3)RST 异常增多
bash
tcpdump -i any 'tcp[tcpflags] & (tcp-rst) != 0' -nn
如果 RST 飙高,通常意味着连接被中途拒绝、回收或状态错乱。
在这次事故里,抓包不是用来"直接证明 conntrack 满",而是用来证明:**问题主要发生在新连接建立阶段,而不是应用处理阶段。**这一步非常关键,因为它能帮你把排查面从应用线程池迅速收缩到主机网络栈。
五、再看连接结构,而不是只看总量
单纯看到 conntrack_count 高,还不够。你还要问:到底是谁把表吃满了?
可以先从连接状态和来源分布看:
bash
conntrack -L 2>/dev/null | awk '{print $4}' | cut -d= -f2 | sort | uniq -c | sort -nr | head
conntrack -L 2>/dev/null | awk '/tcp/ {print $6}' | sort | uniq -c | sort -nr
或者直接看 socket 状态:
bash
ss -ant | awk 'NR>1 {print $1}' | sort | uniq -c | sort -nr
如果你看到大量 TIME-WAIT、SYN-SENT、SYN-RECV 或海量短连接,那就得结合业务流量模型去想问题:
- 某个爬虫或内部任务是否突然放大请求数?
- 网关到服务是否关闭了 keepalive?
- 某次发布是否让连接复用配置失效?
- 某个 Sidecar/代理是否把连接数放大了?
技术上,conntrack 满只是结果,连接模式失控才经常是根因。
六、这次事故的真正触发点
最终回看变更,发现故障前一天为了"提高隔离性",某个上游组件把连接池策略改得更保守,导致到后端服务的短连接数明显增加;叠加晚高峰流量,节点上的 conntrack 占用迅速冲高。
与此同时,默认超时时间偏长,旧状态回收不够快,导致表项积压。于是就出现了经典组合拳:
- 短连接暴增
- 旧连接回收慢
- conntrack 表接近上限
- 新连接建立失败
- 网关表现为偶发 504 / upstream timeout
从复盘角度看,这不是单一 bug,而是流量模型变化 + 内核参数上限 + 连接复用不佳共同触发的结果。
七、止血动作怎么做
线上先救火,原则是:先恢复,再优化。
1)临时提高 conntrack 上限
bash
sysctl -w net.netfilter.nf_conntrack_max=524288
如果机器内存允许,这能快速缓解新连接被拒的问题。但要强调:这只是止血,不是治本。
2)检查并优化超时参数
不同协议、不同流量模型下,连接跟踪超时配置不能一刀切。常见会关注:
bash
sysctl net.netfilter.nf_conntrack_tcp_timeout_established
sysctl net.netfilter.nf_conntrack_tcp_timeout_time_wait
如果超时时间过长,而业务又是高频短连接,表项就容易堆积。
3)恢复连接复用
比如 Nginx upstream、应用 HTTP client、RPC 框架连接池等,优先确认 keepalive 没被误伤。很多"看起来像网络问题"的事故,本质是上层把连接用法改坏了。
4)必要时做流量削峰
当连接表已经持续在危险线附近,临时限流、熔断低优先级任务,比硬扛更理性。别把生产环境当压力测试平台,那是对值班同学的精神攻击。
八、长期方案:把"快满了"变成可观测事件
真正有效的优化,不是等业务群里有人喊超时,而是提前看到风险。
建议至少监控这些指标:
nf_conntrack_count / nf_conntrack_max- 新建连接速率
- TCP 各状态分布(尤其 SYN-RECV、TIME-WAIT)
- RST、重传率、丢包率
- 网关 499/502/504 比例
- 上游连接池复用率、空闲连接数、建连耗时
告警也别只设"超过 95%"。更有价值的是:
- 5 分钟内增长速度异常
- 高峰期持续贴边
- 某节点显著高于同集群其他节点
这样你能在"还没炸,但快了"的阶段介入。
九、给一套实战排查顺序
以后再遇到类似"偶发超时、应用没明显报错"的情况,可以按这个顺序走:
- 先确认故障范围:实例级、节点级还是全链路级
- 看网关日志,判断是连接建立慢还是上游处理慢
- 在异常节点检查
conntrack_count/max - 查
dmesg是否有table full, dropping packet - 用
tcpdump抓建连阶段包,确认是否卡在三次握手 - 用
ss -s、ss -ant看连接状态结构 - 回看近期变更:连接池、代理、超时、重试、Sidecar、NAT
- 先止血,再做参数与架构优化
这个顺序的好处是:**先缩面,再钻深。**不会一上来就陷进某个局部细节。
十、写在最后
网络故障排查最怕两件事:一种是只看应用日志,另一种是抓了一堆包却没明确假设。真正高效的排障,不是命令背得多,而是你知道每一步在排除什么、在验证什么。
这次事故给团队的核心教训只有一句:偶发超时,不等于应用偶发抽风;很多时候,是网络状态表先扛不住了。
如果你最近也在做接口稳定性、链路观测或网络分析,AnaTraf(www.anatraf.com)这类流量可观测工具会比单看日志更容易把问题收敛到具体链路与异常模式,适合做日常巡检和故障复盘时的辅助视图。