我不会起名字322· 后端 / 算法 / 数据库
📘 技术栈 | 🧩 力扣 Hot100 | 🐹 Go 项目 | 🟥 Redis | 🐬 MySQL
文章目录
- conntrack 表满导致丢包:nf_conntrack_max 与 3 个排查命令
- 一、先认识 conntrack
- 二、症状:这些现象都可能是 conntrack 满了
- 三、3 个排查命令
- 命令 1:看表的使用率
- 命令 2:看谁在占表
- 命令 3:看各项超时配置
- 四、怎么调大
- 第一步:算合适的大小
- 第二步:持久化
- 第三步:治本
- 五、K8s 环境下的特别说明
- 六、带走物
conntrack 表满导致丢包:nf_conntrack_max 与 3 个排查命令
一、先认识 conntrack
conntrack(connection tracking)是 Linux 内核 netfilter 的一个模块,它记录每条经过本机的网络连接的状态。
它在这些场景下是必需的:
- 有 iptables NAT 规则(做 SNAT/DNAT);
- 有基于状态的防火墙规则(
-m state --state ESTABLISHED); - Kubernetes 环境:kube-proxy 的 iptables 模式、大部分 CNI 插件、Service 的 DNAT,全都依赖 conntrack。
所以只要你在跑 K8s,或者机器上有 Docker 的 bridge 网络,conntrack 就在工作。
它维护的是一张哈希表,每条连接一项。表有上限:
cat/proc/sys/net/netfilter/nf_conntrack_max# 常见默认 262144(25 万),老版本可能是 65536# 当前已用了多少cat/proc/sys/net/netfilter/nf_conntrack_count表满之后,新的连接会被直接丢弃(drop),而且是在内核层面静默丢弃——应用层完全不知道发生了什么,只会表现为"连接超时"。
二、症状:这些现象都可能是 conntrack 满了
conntrack 满的表现非常"不像网络问题",这也是它难排查的原因:
- 间歇性的
Connection timed out,重试试试又能通; curl卡住不返回,也没有 RST;- 同一台机器上,有的连接能建、有的不能,看起来完全随机;
- DNS 解析偶尔超时;
- K8s 里 Pod 访问 Service 时好时坏;
- 业务日志里出现
No buffer space available/Cannot assign requested address。
最直接的证据在内核日志里:
dmesg|grep-iconntrack# nf_conntrack: table full, dropping packet或者:
journalctl-k|grep"table full"只要看到这一行,就可以确诊——不用再怀疑应用、怀疑网络、怀疑 DNS。
三、3 个排查命令
命令 1:看表的使用率
# 当前值 / 上限cat/proc/sys/net/netfilter/nf_conntrack_countcat/proc/sys/net/netfilter/nf_conntrack_max# 一行看比例echo"scale=2;$(cat/proc/sys/net/netfilter/nf_conntrack_count)/$(cat/proc/sys/net/netfilter/nf_conntrack_max)"|bc超过 80% 就该警惕,到 100% 就开始丢包了。
如果装了conntrack-tools,可以看更细:
# 按状态统计conntrack-L|awk'{print $4}'|sort|uniq-c# 看哪种协议占得最多conntrack-L|awk'{print $3}'|sort|uniq-c|sort-rn|head命令 2:看谁在占表
# 按目标 IP:端口 聚合,找出连接数最多的下游conntrack-L2>/dev/null|awk'{print $6}'|sort|uniq-c|sort-rn|head-20常见的"表杀手":
- DNS 查询:每个 UDP 短连接也会占一项,而且 UDP 的超时默认是 30 秒。DNS 抖动 + 重试会瞬间灌满表;
- 没有连接池的 HTTP 客户端;
- 大量的短连接(爬虫、压测);
- 到同一个下游的 TIME_WAIT 堆积。
命令 3:看各项超时配置
sysctlnet.netfilter.nf_conntrack_tcp_timeout_established# 默认 432000 秒 = 5 天!sysctlnet.netfilter.nf_conntrack_udp_timeout# 默认 30 秒ESTABLISHED状态的默认超时是 5 天——这意味着一条已经"半死不活"的连接(比如对端早就关了但没走完四次挥手)会在表里躺 5 天才被清理。这是表被占满的一个常见原因。
合理的值:
sysctl-wnet.netfilter.nf_conntrack_tcp_timeout_established=1200# 20 分钟sysctl-wnet.netfilter.nf_conntrack_tcp_timeout_time_wait=60sysctl-wnet.netfilter.nf_conntrack_tcp_timeout_close_wait=60sysctl-wnet.netfilter.nf_conntrack_udp_timeout=30注意:把established调得太短会有副作用——真正空闲的长连接会被 conntrack 提前遗忘,之后来的包会被当成"无效连接"丢弃。所以对有长连接的机器,这个值要和 TCP keepalive 配合(keepalive 间隔 < established 超时)。
四、怎么调大
第一步:算合适的大小
conntrack 表是哈希表,每项大约占300 字节内核内存。经验公式:
nf_conntrack_max = 期望并发连接数 × 1.2对于一台 8GB 内存的机器,设 262144 大约占 78MB 内核内存,是可以接受的。
sysctl-wnet.netfilter.nf_conntrack_max=524288但要注意哈希表桶的数量:
cat/sys/module/nf_conntrack/parameters/hashsize# 建议 hashsize = nf_conntrack_max / 4echo131072>/sys/module/nf_conntrack/parameters/hashsize桶太少会导致哈希链变长,查找变慢,表现为 CPU 升高而不是丢包。
第二步:持久化
sysctl -w重启就没了。写进配置:
cat>/etc/sysctl.d/90-conntrack.conf<<'EOF' net.netfilter.nf_conntrack_max = 524288 net.netfilter.nf_conntrack_tcp_timeout_established = 1200 net.netfilter.nf_conntrack_tcp_timeout_time_wait = 60 net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60 net.netfilter.nf_conntrack_udp_timeout = 30 EOFsysctl-p/etc/sysctl.d/90-conntrack.conf第三步:治本
调大表只是缓解,真正要做的通常是这几件:
- DNS 用本地缓存(nscd / systemd-resolved / CoreDNS 的 cache),减少 UDP 短连接;
- 客户端开连接池,别每次都新建连接;
- 排查短连接来源——往往是某个客户端没复用连接;
- 如果是 K8s 节点,考虑节点规格是否与并发量匹配,或者把高并发服务分散到更多节点。
五、K8s 环境下的特别说明
在 K8s 节点上,conntrack 问题会表现为非常随机的故障,因为每条 Service 访问都要走 DNAT + conntrack:
- Pod 访问 ClusterIP 时好时坏;
- NodePort 偶发不通;
- CoreDNS 解析偶发超时(DNS 是 UDP,非常吃 conntrack)。
CoreDNS 相关的 conntrack 问题尤其值得单独说:DNS 是 UDP,默认 30 秒超时。如果集群里 DNS 查询量大(比如应用没做缓存、或者ndots配置导致放大查询),conntrack 表会被 DNS 连接快速填满,然后所有网络都开始出问题——因为 DNS 是网络访问的前置环节。
这就是为什么很多"K8s 网络抖动"的根因其实是:DNS 查询放大 → conntrack 满 → 全节点丢包。
六、带走物
排查流程:
# 1. 确诊dmesg|grep-i"conntrack: table full"# 看到就是它# 2. 看水位cat/proc/sys/net/netfilter/nf_conntrack_countcat/proc/sys/net/netfilter/nf_conntrack_max# 3. 看谁在用conntrack-L|awk'{print $6}'|sort|uniq-c|sort-rn|head# 4. 临时缓解sysctl-wnet.netfilter.nf_conntrack_max=524288sysctl-wnet.netfilter.nf_conntrack_tcp_timeout_established=1200# 5. 治本# DNS 本地缓存 / 客户端连接池 / 消灭短连接一句话结论:conntrack 满导致的丢包是内核静默行为,应用层只会看到"随机超时";确诊只看一处——dmesg里有没有nf_conntrack: table full。