conntrack表持续满的根本原因是连接生命周期过长,尤其是established状态连接滞留;需结合业务模型合理调小net.netfilter.nf_conntrack_tcp_timeout_established,并验证条目是否真实衰减,同时排查assured标记、unreplied半开连接及应用层关闭逻辑。

conntrack 表满后清空即复满,本质是连接生命周期过长
这不是 conntrack -F 没用,而是新连接持续涌入、旧连接迟迟不释放。典型表现是:nf_conntrack_count 瞬间回到上限,dmesg 里反复出现 "nf_conntrack: table full, dropping packet"。根本原因往往不是并发量突增,而是大量连接卡在 ESTABLISHED 状态不退场。
- 常见于长连接服务(如 websocket、http/2 后端、数据库连接池未正确关闭)
- 也见于客户端异常断连但服务端未触发 FIN/RST,导致 conntrack 条目长期滞留
- linux 默认
net.netfilter.nf_conntrack_tcp_timeout_established是 432000 秒(5 天),远超业务实际需要
tcp_timeout_established 调优必须结合业务连接模型
盲目调小 net.netfilter.nf_conntrack_tcp_timeout_established 可能引发连接被误杀,尤其对长周期数据传输或心跳保活的场景。得先确认你的连接“应该”存活多久:
- HTTP API(短连接):30–120 秒足够,设为
120即可 - WebSocket 或 MQTT 长连接:依赖心跳间隔,timeout 应设为心跳周期的 2–3 倍(例如心跳 30s,设为
90) - 数据库连接池(如 pgBouncer、HikariCP):需与池的 idle timeout 对齐,通常
300(5 分钟)较安全 - 不要低于
60:太短易受网络抖动影响,TCP 重传或延迟 ACK 可能导致连接提前被回收
调优后必须验证 conntrack 条目真实衰减行为
改完 sysctl 不代表生效——要确认内核已加载且条目开始自然释放。容易忽略的点:
- 执行
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=120后,用sysctl net.netfilter.nf_conntrack_tcp_timeout_established确认值已更新 -
conntrack -L | wc -l看总数没用,要观察conntrack -L | grep "ESTABLISHED" | head -20,检查每个条目的[ASSURED]和时间戳是否开始滚动更新 - 如果仍不下降,检查是否有连接被标记为
[ASSURED]:这类连接不受 timeout 控制,通常由 iptables 的CTtarget 或 nfqueue 主动保活,需排查规则链 - 容器环境(如 kubernetes)要注意:节点级 sysctl 修改不自动透传到 Pod,需通过
securityContext.sysctls显式配置
别只盯 ESTABLISHED,TIME_WAIT 和 UNREPLIED 也可能是隐形主力
用 conntrack -S 查看各状态计数,常发现 unreplied 或 time_wait 占比异常高:
-
unreplied高:说明有大量半开连接(SYN 到了,SYN-ACK 没回),检查防火墙拦截、后端宕机或 SYN flood 攻击 -
time_wait高:不是 conntrack 问题,是 socket 层资源,需调net.ipv4.tcp_fin_timeout或启用net.ipv4.tcp_tw_reuse(仅客户端适用) - 真正卡住 conntrack 表的,往往是
established+unreplied组合——前者靠 timeout 缩短,后者得从网络连通性或应用层握手逻辑查起
调参只是止血,conntrack 持续满,终究是连接没按预期关闭。得顺着日志和 conntrack -E 实时事件,找到哪类连接不走 FIN 流程,这才是根因。