Docker、Kubernetes 节点连接偶发超时?排查 conntrack 表耗尽
容器服务出现间歇性超时时,应用日志可能只有 connection timed out、DNS 查询失败或访问上游偶发中断。Pod 重启后故障短暂消失,但节点过一段时间又复现。如果节点内核同时记录下面的日志,应把排查范围移到宿主机连接跟踪表:
nf_conntrack: table full, dropping packet
Docker 网桥、Kubernetes Service、节点 SNAT 和出口网关都可能依赖 Netfilter 连接跟踪。表满之后,新流量在到达应用之前就可能被丢弃,因此只检查容器端口和进程状态往往找不到原因。
一、必须在节点上检查,而不是只进容器
先登录出现故障的宿主机或 Kubernetes 节点:
dmesg -T | grep -i 'nf_conntrack.*table full'
journalctl -k --since '-1 hour' | grep -i conntrack
sysctl net.netfilter.nf_conntrack_count
sysctl net.netfilter.nf_conntrack_max
计算使用率:
count=$(sysctl -n net.netfilter.nf_conntrack_count)
max=$(sysctl -n net.netfilter.nf_conntrack_max)
awk -v c="$count" -v m="$max" \
'BEGIN {printf "usage=%.1f%% (%d/%d)\n", c*100/m, c, m}'
nf_conntrack_count 表示当前分配的流条目数,nf_conntrack_max 表示允许的最大条目数。两者由宿主机内核维护,普通容器看到或修改这些参数的能力取决于网络命名空间和权限。(docs.kernel.org)
Kubernetes 环境先找出异常 Pod 所在节点:
kubectl get pod -A -o wide
kubectl get node -o wide
如果只有部分节点报错,不要立即把问题归因于整个集群网络。优先比较异常节点与正常节点的条目上限、当前使用率、Pod 数量和出口流量。
二、区分套接字耗尽和 conntrack 耗尽
查看本机套接字摘要:
ss -s
ss -ant | awk 'NR>1 {print $1}' | sort | uniq -c | sort -nr
再查看连接跟踪数量:
conntrack -C
conntrack -S
conntrack 工具可以显示条目计数、内核统计和经过筛选的连接记录。(netfilter.org)
两组数字不相等并不异常。ss 主要观察节点上的套接字,而 conntrack 还会记录经过节点转发、NAT 或状态防火墙处理的连接。例如,承担 Pod 出口 SNAT 的节点可能有大量连接跟踪记录,却没有同等数量的本地应用连接。
三、找出条目主要来自哪些容器或 Pod
先对来源地址做采样统计:
conntrack -L -o extended 2>/dev/null |
awk '{
for (i=1; i<=NF; i++) {
if ($i ~ /^src=/) {
split($i, a, "="); print a[2]; break
}
}
}' |
sort | uniq -c | sort -nr | head -n 30
连接表很大时,完整转储可能给繁忙节点增加负担。先执行 conntrack -C 和 conntrack -S,确认有必要后再在单台节点上短时间采样。
Docker:把来源 IP 映射回容器
docker ps -q | xargs -r docker inspect \
--format '{{.Name}} {{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}'
将统计结果中的来源 IP 与容器 IP 对照。某个容器条目明显偏多时,继续检查它是否频繁请求不可达地址、关闭了连接池、不断重试,或在短时间创建大量 DNS 和 HTTP 请求。
Kubernetes:把来源 IP 映射回 Pod
kubectl get pod -A -o wide --sort-by=.status.podIP
如果使用了覆盖网络、隧道或特定 CNI,节点上看到的地址可能经过转换。应结合当前 CNI 的数据路径确认原始来源,不能假设所有 conntrack 地址都能直接对应 Pod IP。
四、重点排查四类容器流量
1. 没有复用的 HTTP 请求
应用每次请求都新建 TCP 连接,会快速增加连接创建和销毁频率。检查客户端是否启用了 Keep-Alive、连接池以及合理的空闲连接数量。
ss -ant state established | wc -l
ss -ant state time-wait | wc -l
如果修复连接池后,业务请求量不变但 conntrack 增长速度明显下降,说明优化方向有效。
2. 失败后无退避重试
上游故障时,多个副本同时快速重试,会持续创建新连接。重试应设置次数上限、指数退避和随机抖动,避免所有 Pod 在同一时间发起下一轮请求。
检查应用日志中的单位时间重试次数,并与下面的连接跟踪趋势对照:
while sleep 5; do
printf '%s ' "$(date '+%T')"
sysctl -n net.netfilter.nf_conntrack_count
done
3. 高频 DNS 或短 UDP 事务
查看协议分布:
conntrack -L 2>/dev/null |
awk '{print $1}' |
sort | uniq -c | sort -nr
检查 UDP 超时:
sysctl net.netfilter.nf_conntrack_udp_timeout
sysctl net.netfilter.nf_conntrack_udp_timeout_stream
内核为普通 UDP 和 UDP 流分别提供超时参数。(docs.kernel.org)
如果确认节点只承载短事务,并完成了测试,可以小幅降低参数:
sysctl -w net.netfilter.nf_conntrack_udp_timeout=20
sysctl -w net.netfilter.nf_conntrack_udp_timeout_stream=60
不要在不了解实时音视频、隧道或其他长 UDP 会话的情况下直接应用这些值。
4. 出口集中在少数节点
如果大量 Pod 通过少数节点进行 SNAT,即使各应用流量正常,节点也可能承担过多连接状态。检查节点间 Pod 数、出口流量和 conntrack 使用率是否明显不均衡。
可选处理方式包括:
- 调整 Pod 调度,分散高出口流量工作负载;
- 扩展出口节点或 NAT 网关容量;
- 减少多层重复 NAT;
- 为高并发客户端启用连接复用;
- 根据实际流量提高节点 conntrack 上限。
五、应急扩容节点连接跟踪表
节点还有足够内存时,可临时提高上限:
sysctl -w net.netfilter.nf_conntrack_max=262144
持续观察:
watch -n 2 'sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max'
journalctl -kf | grep --line-buffered -i conntrack
如果使用率快速再次达到新上限,应优先限制异常来源或减少无效重试,不要继续机械翻倍。
在普通 Linux 节点上,可持久化为:
cat >/etc/sysctl.d/90-conntrack.conf <<'EOF'
net.netfilter.nf_conntrack_max = 262144
EOF
sysctl --system
受管 Kubernetes 的节点镜像可能在重建后覆盖本地文件。此时应通过节点模板、启动脚本、受支持的节点配置接口或镜像构建流程保存参数,并在节点替换后重新验证。
六、不要把清空连接表当成节点修复
conntrack 支持删除和清空记录,但这些操作会影响正在使用的连接。(netfilter.org)
# 高风险:不要在承载流量的节点上直接执行
conntrack -F
清空后数字会立即下降,但 NAT 映射和状态防火墙记录也会丢失。更稳妥的流程是先将节点摘出负载、等待连接排空,再根据明确条件删除异常记录或重启网络组件。
也不要为了降低条目数而全局关闭 conntrack。Docker 端口映射、Kubernetes Service、SNAT 和状态防火墙可能依赖它;改变跟踪规则前必须理解当前数据路径。
七、完成修复后的验证
节点侧
sysctl net.netfilter.nf_conntrack_count
sysctl net.netfilter.nf_conntrack_max
journalctl -k --since '-15 min' | grep -i conntrack
slabtop -o | grep -E 'nf_conntrack|nf_conntrack_expect'
确认条目数在峰值结束后回落,内核不再记录表满丢包,同时内存消耗处于可接受范围。
Docker 侧
docker ps
docker stats --no-stream
检查异常容器是否仍在高频创建连接,连接池和重试配置是否已经随新版本生效。
Kubernetes 侧
kubectl get pod -A -o wide
kubectl get events -A --sort-by=.lastTimestamp | tail -n 50
从集群内连续请求 DNS、ClusterIP 服务和外部依赖,并从集群外执行健康检查。只验证 Pod 状态为 Running 不够,Pod 可以正常运行但节点仍在网络层丢弃新连接。
八、建议建立的监控
至少记录每个节点的:
nf_conntrack_count;nf_conntrack_max;- 使用率及增长速度;
- 内核 conntrack 丢包或插入失败;
- 节点内存;
- Pod 数量和出口流量;
- DNS、服务间调用及外部请求成功率。
告警应带上节点名并与业务探测关联。这样可以区分单节点容量不足、异常 Pod 制造连接风暴和集群整体出口设计不足。
总结
容器网络中的 nf_conntrack: table full 是节点级故障,不是简单的 Pod 端口问题。先在宿主机确认条目使用率,再把来源地址映射回容器或 Pod,检查连接池、重试、UDP 事务和出口分布。临时扩容可以恢复新连接,但长期修复要减少无效连接、分散 NAT 压力,并确保节点重建后参数和监控仍然存在。