conntrack 表满导致新连接失败但 ss -s 显示正常连接数的排查路径

8次阅读

conntrack表满导致新SYN包被丢弃,而ss -s仅显示socket层连接状态,故二者数值不一致;需通过conntrack -S的insert_failed、/proc.net/nf_conntrack条目数及日志“table full”确认溢出。

conntrack 表满导致新连接失败但 ss -s 显示正常连接数的排查路径

conntrack 表满但 ss -s 显示连接数正常?这是两个维度的问题

没错,ss -s 统计的是当前 socket 层的连接状态(ESTAB、TIME-WAIT 等),而 conntrack 表满影响的是 netfilter 的连接跟踪状态——新 TCP 三次握手 SYN 包进不来,根本到不了 socket 层。所以你会看到 ss -s 里 ESTAB 数量稳定甚至下降,但客户端连不上、超时重传、SYN timeout 错误频发。

确认 conntrack 表是否真的溢出

别只看 sysctl net.netfilter.nf_conntrack_count,它只是当前 tracked 连接数,不等于是否丢包。关键要看内核是否开始 drop:

  • 执行 cat /proc/net/nf_conntrack | wc -lsysctl net.netfilter.nf_conntrack_max 对比,接近或等于即为满
  • 检查丢包:grep "nf_conntrack: table full" /var/log/messagesdmesg -T | grep "table full"
  • 更直接:conntrack -S 输出中关注 insert_failed 计数器,非零说明已开始丢新连接

为什么 conntrack -L 看不到大量连接,但表却满了?

常见于短连接 + 高频新建场景,比如 http 轮询、健康检查、dns 查询。这些连接很快关闭,但 conntrack 条目不会立刻释放——默认要等 net.netfilter.nf_conntrack_tcp_timeout_time_wait(通常 120s)甚至更久。你用 conntrack -L 抓快照时可能刚巧没捕获到峰值,但瞬时插入失败已经发生。

  • watch -n 1 'conntrack -S' 观察 entriesinsert_failed 实时变化
  • conntrack -L | awk '{print $4}' | sort | uniq -c | sort -nr | head 查看哪些源 IP/端口占最多条目(常暴露扫描或 misbehaving client)
  • 注意:启用 net.netfilter.nf_conntrack_tcp_be_liberal=1 可缓解乱序/重传导致的假连接残留,但不解决根本容量问题

调优与规避的关键参数和限制

单纯加大 nf_conntrack_max 不是万能解,它吃内存(每个条目约 376 字节),且在高并发下锁竞争明显。优先做减法:

  • 对明确不需要跟踪的流量 bypass conntrack:iptables -t raw -A PREROUTING -p tcp --dport 8080 -j NOTRACK(适用于直通代理、L4 LB 后端
  • 缩短超时时间:sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30(需同步确认应用无 TIME-WAIT 相关异常)
  • 避免在容器环境重复跟踪:docker 默认启用 conntrack;kubernetes 中若用 kube-proxy iptables 模式,又叠加 host 上的 conntrack,容易 double-track
  • 注意:修改 nf_conntrack_max 后,nf_conntrack_buckets(哈希桶数)建议设为 max 的 1/4 ~ 1/2,否则哈希冲突加剧性能下降

真正难处理的是“连接未完成就积”的情况——比如 SYN flood、后端响应慢导致连接卡在 SYN_RECV 状态过久,这种 conntrack 条目会一直占着直到超时,比正常 ESTAB 更耗资源,也最难从日志里直接定位。

text=ZqhQzanResources