conntrack 表满导致新连接失败但 netstat/ss 看不出异常的排查顺序

11次阅读
.netstat 和 ss 不显示异常是因为它们只查看 socket 层连接状态,而 conntrack 表满时新连接在进入协议前就被 nf_conntrack 拒绝,根本未到达 socket 层。

conntrack 表满导致新连接失败但 netstat/ss 看不出异常的排查顺序

conntrack 表满时为什么 netstatss 都不显示异常

因为 netstatss 只查 socket 层的连接状态(如 ESTABLISHED、TIME_WaiT),而 conntrack 是 netfilter 的连接跟踪表,用于 NAT、防火墙规则匹配等。新连接在进入协议前就被 nf_conntrack 拒绝了,根本到不了 socket 层,所以这两个工具完全看不到任何“新连接尝试”的痕迹。

快速确认 conntrack 表是否已满

直接看内核统计和当前使用量,比翻日志更快:

  • 运行 cat /proc/sys/net/netfilter/nf_conntrack_count 查当前条目数
  • 运行 cat /proc/sys/net/netfilter/nf_conntrack_max 查上限值
  • 如果前者接近或等于后者(比如 >90%),基本就是瓶颈所在
  • 补充验证:dmesg -T | tail -20 | grep -i "nf_conntrack: table full",有这条日志就实锤了

常见导致 conntrack 泛滥的场景和对应检查点

不是所有连接都会进 conntrack 表,但以下几类特别容易积:

  • 大量短连接 + 未启用 nf_conntrack_tcp_be_liberal=1:TCP FIN/RST 乱序时会卡住连接状态,建议开启
  • udp 流量(尤其是 dns、NTP):默认超时长达 30 秒,且无握手确认,极易堆积;可通过 sysctl net.netfilter.nf_conntrack_udp_timeout_stream_timeout 分别调低
  • 容器环境(docker/podman):每个容器网络命名空间都独立维护 conntrack 表,宿主机上看到的是总和,但实际可能某个 Namespace 已满;用 nsenter -n -t $PID -- cat /proc/sys/net/netfilter/nf_conntrack_count 检查具体容器
  • iptables 规则中用了 -m state-m conntrack:即使没做 NAT,只要加载了 nf_conntrack 模块并触发匹配,就会建连接记录

临时缓解与长期调优的关键参数

别只加 nf_conntrack_max,要结合业务特征调整超时和回收逻辑:

  • 临时扩容(重启后失效):sysctl -w net.netfilter.nf_conntrack_max=131072
  • 缩短 TCP 非活跃连接超时:sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=43200(默认 43200 秒 = 12 小时,对多数服务太长)
  • 加快 TIME_WAIT 回收:sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30(默认 120 秒)
  • 启用自动垃圾回收(尤其高并发突增场景):sysctl -w net.netfilter.nf_conntrack_gc_thresh_high=110000,同时设 _low_max 形成水位区间
  • 注意:修改后需运行 sysctl -p 持久化,且某些参数(如 nf_conntrack_max)在模块加载后不可动态增大,必须先 rmmod nf_conntrack 再重设再 modprobe

真正麻烦的不是调参本身,而是不同协议、不同 namespace、不同内核版本对同一参数的响应差异——比如 5.10+ 内核里 nf_conntrack_tcp_be_liberal 默认已开,但 4.19 可能还关着,得挨个核对。

text=ZqhQzanResources