适用场景

本文适用于 Linux 主机、NAT 网关、Docker 宿主机、Kubernetes Node 或开启防火墙规则的服务器。当业务出现偶发连接超时、DNS 查询失败、服务间调用间歇性失败,而 CPU、内存、磁盘都看起来正常时,可以把 conntrack 连接跟踪表作为重点排查对象。

典型环境包括:

  • 单机部署了 Nginx、Docker、iptables、firewalld 或 kube-proxy。
  • 节点承担 SNAT、DNAT、NodePort、Ingress 出入口流量。
  • 短连接很多,例如爬虫、网关、日志上报、健康检查、HTTP 回调。
  • 高峰期才出现异常,低峰期自动恢复。

现象描述

一次线上问题中,业务接口在高峰期偶发超时,表现为:

  • 应用日志出现 connect timeoutread timeoutconnection reset by peer
  • 部分容器访问外部 API 失败,但重试后成功。
  • DNS 查询偶发失败,报 Temporary failure in name resolution
  • Nginx upstream 偶发 502 或 504。
  • 主机 CPU、内存、磁盘 I/O 没有明显瓶颈。

查看内核日志时发现类似信息:

dmesg -T | grep -i conntrack

可能输出:

nf_conntrack: table full, dropping packet

这说明内核连接跟踪表已经达到上限,新连接无法被记录,相关数据包可能被丢弃。

conntrack 是什么

conntrack 是 Linux netfilter 框架中的连接跟踪机制。iptables、nftables、NAT、Docker 端口映射、Kubernetes Service 转发等功能,经常依赖它记录连接状态。

它会跟踪类似下面的信息:

  • 源 IP、源端口、目标 IP、目标端口、协议。
  • TCP 状态,例如 SYN_SENTESTABLISHEDTIME_WAIT
  • UDP、ICMP 等非 TCP 流量的超时时间。
  • NAT 前后的地址和端口映射。

当连接跟踪表满了,新的连接状态无法插入,依赖连接跟踪的转发、NAT、防火墙规则就可能出现异常。

可能原因

conntrack 表满通常不是单点原因,而是流量模型和系统参数共同导致:

  • 短连接突增,连接创建速度远大于过期速度。
  • 客户端没有复用连接,HTTP keep-alive 或连接池配置不合理。
  • TCP 连接异常关闭,残留大量 TIME_WAITCLOSE_WAIT 或未完成握手状态。
  • UDP 流量很多,例如 DNS、日志、监控打点。
  • Docker 或 Kubernetes 节点承担大量 NAT 流量。
  • nf_conntrack_max 配置过小,和机器内存、实际连接规模不匹配。
  • conntrack 相关超时时间过长,旧连接释放太慢。

快速确认是否命中

1. 查看当前 conntrack 使用量

cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max

第一行是当前已使用条目数,第二行是最大条目数。也可以合并查看:

printf "count=%s max=%s usage=%s%%\n" \
  "$(cat /proc/sys/net/netfilter/nf_conntrack_count)" \
  "$(cat /proc/sys/net/netfilter/nf_conntrack_max)" \
  "$(( $(cat /proc/sys/net/netfilter/nf_conntrack_count) * 100 / $(cat /proc/sys/net/netfilter/nf_conntrack_max) ))"

关键判断:

  • 使用率长期超过 70%,需要关注。
  • 使用率超过 90%,高峰期很容易触发丢包。
  • 使用率接近 100%,并且 dmesgtable full,基本可以确认。

2. 查看内核丢包日志

dmesg -T | grep -E "nf_conntrack|conntrack.*full"

如果输出中有:

nf_conntrack: table full, dropping packet

说明问题已经从容量风险变成实际丢包。

3. 使用 conntrack 工具统计状态

如果系统已安装 conntrack-tools

conntrack -S
conntrack -L -p tcp 2>/dev/null | awk '{print $4}' | sort | uniq -c | sort -nr | head

常见字段说明:

  • entries:当前连接跟踪条目数量。
  • searched:查询次数。
  • found:命中次数。
  • insert_failed:插入失败次数,这个值增加时要重点关注。
  • TCP 状态统计可以帮助判断是正常高并发,还是某类异常状态堆积。

4. 没有 conntrack 工具时的替代方法

很多生产环境没有预装 conntrack 命令,可以直接读 /proc

awk '{print $4}' /proc/net/nf_conntrack 2>/dev/null | sort | uniq -c | sort -nr | head

如果路径不存在,可能在旧内核上是:

awk '{print $4}' /proc/net/ip_conntrack 2>/dev/null | sort | uniq -c | sort -nr | head

还可以统计访问最频繁的目标:

awk '
{
  for (i=1; i<=NF; i++) {
    if ($i ~ /^dst=/) dst=$i
    if ($i ~ /^dport=/) dport=$i
  }
  if (dst && dport) print dst, dport
}' /proc/net/nf_conntrack 2>/dev/null | sort | uniq -c | sort -nr | head

这可以帮助判断是某个外部依赖、DNS 服务、数据库代理还是内部服务制造了大量连接。

定位示例

假设当前主机是 Kubernetes Node,业务高峰时出现接口超时。先看使用率:

count=$(cat /proc/sys/net/netfilter/nf_conntrack_count)
max=$(cat /proc/sys/net/netfilter/nf_conntrack_max)
echo "$count / $max"

输出:

259800 / 262144

使用率已经达到 99%。继续看内核日志:

dmesg -T | tail -n 100 | grep -i conntrack

输出:

[Wed Jul 22 09:42:11 2026] nf_conntrack: table full, dropping packet

再看条目分布:

awk '{print $1,$4,$5,$6,$7}' /proc/net/nf_conntrack 2>/dev/null | head

可能看到大量 UDP DNS 请求和 TCP 短连接。如果 DNS 条目明显偏多:

grep "dport=53" /proc/net/nf_conntrack 2>/dev/null | wc -l

如果某个业务外呼端口偏多:

grep "dport=443" /proc/net/nf_conntrack 2>/dev/null | awk '
{
  for (i=1; i<=NF; i++) {
    if ($i ~ /^dst=/) print $i
  }
}' | sort | uniq -c | sort -nr | head

这个结果能把排查方向从“网络不稳定”收敛到“某类目标连接激增”。

临时止血方案

1. 调大 nf_conntrack_max

如果机器内存足够,可以先临时调大:

sysctl -w net.netfilter.nf_conntrack_max=1048576

同时建议调整 hash bucket。该参数通常只能通过模块参数或启动参数设置,不一定能在线修改:

cat /sys/module/nf_conntrack/parameters/hashsize

经验上,hashsize 可以按 nf_conntrack_max / 4 估算。不同发行版配置方式不同,需要结合启动方式处理。

注意:调大上限只是止血,不是根因修复。conntrack 条目会占用内存,不能无限增大。

2. 缩短部分超时时间

高短连接场景可适当调小 TCP 已关闭、等待类状态的超时:

sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_close_wait=30
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_fin_wait=30

UDP 场景,例如 DNS 请求过多,可以关注:

sysctl -w net.netfilter.nf_conntrack_udp_timeout=15
sysctl -w net.netfilter.nf_conntrack_udp_timeout_stream=60

不要盲目把超时时间调得过低。过低可能导致长连接或慢请求中途失去跟踪状态,反而制造新的异常。

3. 清理特定异常流量

如果确认某个目标产生了大量异常条目,可以按条件删除。示例:删除目标端口 53 的 UDP 条目:

conntrack -D -p udp --dport 53

删除操作有风险,可能影响正在进行的请求。生产环境建议只在明确异常、业务可接受短暂重试时执行。

根因治理方案

1. 应用侧复用连接

大量短连接是最常见根因。应用需要检查:

  • HTTP 客户端是否启用了连接池。
  • keep-alive 是否被代理或服务端提前关闭。
  • 连接池最大连接数、空闲连接数、超时时间是否合理。
  • 是否每个请求都新建客户端对象。

以 Python httpx 为例,不建议每次请求都创建 Client

import httpx

limits = httpx.Limits(max_connections=200, max_keepalive_connections=50)
timeout = httpx.Timeout(connect=2.0, read=5.0, write=5.0, pool=2.0)

with httpx.Client(limits=limits, timeout=timeout) as client:
    response = client.get("https://api.example.com/v1/orders")
    response.raise_for_status()

关键点:

  • max_connections 控制总连接数,避免无限并发打爆下游和本机。
  • max_keepalive_connections 保留可复用连接,减少短连接创建。
  • pool 超时能暴露连接池耗尽,而不是把问题伪装成慢请求。

2. 减少不必要的 NAT

在 Kubernetes 或容器环境中,NAT 会增加 conntrack 压力。可以评估:

  • 集群内访问是否可以走 ClusterIP 或服务发现,避免绕外部网关。
  • 大流量服务是否需要独立节点、hostNetwork 或专用网关。
  • 出口流量是否集中到少数 NAT 节点,是否需要水平拆分。
  • kube-proxy、CNI、Service 拓扑是否导致不必要的跨节点转发。

3. 对异常来源限流

如果是某个客户端、任务或定时脚本制造连接风暴,应该从源头治理:

ss -tan state syn-sent | awk '{print $4,$5}' | sort | uniq -c | sort -nr | head
ss -tan state established | awk '{print $4,$5}' | sort | uniq -c | sort -nr | head

结合应用日志确认调用方后,可以做:

  • 调整并发。
  • 加入退避重试。
  • 限制定时任务批量大小。
  • 对高频失败请求增加熔断。
  • 修复连接未关闭或客户端重复初始化问题。

持久化配置

确认参数后写入 sysctl 配置:

cat >/etc/sysctl.d/99-conntrack.conf <<'EOF'
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 30
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 30
net.netfilter.nf_conntrack_udp_timeout = 15
net.netfilter.nf_conntrack_udp_timeout_stream = 60
EOF

sysctl --system

再次确认:

sysctl net.netfilter.nf_conntrack_max
sysctl net.netfilter.nf_conntrack_tcp_timeout_time_wait

如果需要配置 hashsize,可以在模块加载配置中处理:

echo "options nf_conntrack hashsize=262144" >/etc/modprobe.d/nf_conntrack.conf

该配置通常需要重载模块或重启后生效。生产环境不要直接卸载 nf_conntrack 模块,因为 Docker、Kubernetes、防火墙、NAT 可能正在依赖它。

监控和告警

建议至少采集两个指标:

  • nf_conntrack_count:当前使用量。
  • nf_conntrack_max:最大容量。

node_exporter 通常会暴露相关指标,可以在 Prometheus 中查看:

node_nf_conntrack_entries
node_nf_conntrack_entries_limit

告警规则示例:

groups:
  - name: linux-conntrack
    rules:
      - alert: ConntrackUsageHigh
        expr: node_nf_conntrack_entries / node_nf_conntrack_entries_limit > 0.8
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "conntrack usage is higher than 80%"
          description: "instance={{ $labels.instance }} conntrack usage is above 80% for 5 minutes"

      - alert: ConntrackUsageCritical
        expr: node_nf_conntrack_entries / node_nf_conntrack_entries_limit > 0.95
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "conntrack usage is higher than 95%"
          description: "instance={{ $labels.instance }} may drop new connections"

告警阈值建议:

  • 80% 持续 5 分钟:预警,安排扩容或流量分析。
  • 95% 持续 2 分钟:严重,可能已经影响新连接。
  • 如果 insert_failed 持续增加,应按故障处理。

预防措施

  • 高并发服务默认开启连接池,避免每次请求新建连接。
  • 网关、Node、NAT 实例按峰值连接数评估 conntrack 容量。
  • 定期检查内核日志中的 nf_conntrack: table full
  • 对 DNS、HTTP 回调、日志上报等短连接来源做并发控制。
  • Kubernetes 节点把 conntrack 使用率纳入基础监控。
  • 变更 NAT、Service、Ingress、CNI 配置后观察 conntrack 曲线。
  • 压测时不仅看 QPS 和延迟,也看连接数、端口数、conntrack 使用率。

总结

conntrack 表满的典型误导点在于:系统资源看起来正常,但网络请求却随机失败。排查时可以按“内核日志确认丢包、查看 count/max 使用率、分析协议和目标分布、定位异常来源、再决定扩容或治理”的顺序推进。

临时处理可以调大 nf_conntrack_max、缩短部分超时、清理异常条目;长期治理必须回到连接复用、NAT 架构、客户端并发、监控告警上。只调内核参数不处理短连接风暴,问题往往会在下一次流量高峰再次出现。