一次Kubernetes节点conntrack表满导致服务间TCP连接超时的排查记录

K8s集群中部分Pod间TCP连接间歇性超时,最终定位到节点conntrack表满导致新连接被内核丢弃。

一、问题背景

某生产 Kubernetes 集群(v1.28,containerd 运行时,Calico CNI)承载了约 200 个微服务 Pod,分布在 12 个 worker 节点上。近期业务团队反馈:部分 Pod 之间的 HTTP/gRPC 调用出现间歇性超时,频率大约每小时 2-3 次,每次持续 10-30 秒后自动恢复。因为不是持续故障,问题排查起来格外棘手。

二、故障现象

业务方反馈的现象汇总如下:

  • 受影响的服务:主要集中在某两个节点上的 Pod,调用其他节点上的服务时偶发 connection timeout
  • 时间规律:无明显时间段规律,白天和凌晨都有发生
  • 恢复方式:无需人工介入,10-30 秒后自动恢复正常
  • 影响范围:仅影响新建 TCP 连接,已建立的连接不受影响
  • 监控表现:Prometheus 监控显示对应时间段内 Pod 的 container_network_receive_errors_totalcontainer_network_transmit_errors_total 均有异常增长

初步怀疑方向:CNI 插件问题、kube-proxy 异常、或者底层网络设备丢包。

三、排查过程

3.1 排除 CNI 层

首先检查 Calico 状态:

1
calicoctl node status

所有节点 BGP peer 状态正常,无异常日志。calicoctl get ippool -o wide 确认 IPAM 未耗尽。

在受影响节点上抓包:

1
tcpdump -i caliXXXX -nn 'tcp[tcpflags] & (tcp-syn) != 0'

发现一个关键现象:Client Pod 发出的 SYN 包确实到达了宿主机网卡,但没有进入目标容器的 veth pair。这意味着丢包发生在宿主机内核网络栈层面。

3.2 检查内核日志

1
dmesg -T | grep -i -E "conntrack|nf_conntrack|drop"

输出直接揭示了根因:

1
2
3
[Mon Jul  6 09:12:33 2026] nf_conntrack: nf_conntrack: table full, dropping packet
[Mon Jul  6 09:12:34 2026] nf_conntrack: nf_conntrack: table full, dropping packet
[Mon Jul  6 09:12:38 2026] nf_conntrack: nf_conntrack: table full, dropping packet

conntrack 表满了! 内核在 conntrack 表容量耗尽时,会直接丢弃新连接的 SYN 包,导致客户端 TCP 连接超时。

3.3 深入分析 conntrack 使用情况

查看当前 conntrack 条目数:

1
2
3
4
cat /proc/sys/net/netfilter/nf_conntrack_count
# 输出:262144
cat /proc/sys/net/netfilter/nf_conntrack_max
# 输出:262144

果然是打满了。再查看 conntrack 条目的构成:

1
conntrack -L -o extended | awk '{print $4}' | sort | uniq -c | sort -rn | head -20

发现大量 TIME_WAIT 状态的连接条目,且主要来自几个特定的源 IP —— 对应一台高频调用外部 API 的数据同步服务。

进一步查看 conntrack 超时参数:

1
2
cat /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_time_wait
# 输出:120(秒)

TIME_WAIT 状态默认超时 120 秒,在连接量大的场景下,这个时长足以让 conntrack 表被 TIME_WAIT 条目迅速填满。

3.4 确认问题节点

对比各节点的 conntrack 使用率发现,问题节点运行了一个高频 HTTP 调用的数据同步 Pod,每秒向外发起约 800-1000 个短连接请求。每个请求完成后进入 TIME_WAIT,在 120 秒内堆积的条目数:

1
1000 连接/秒 × 120 秒 = 120,000 个 TIME_WAIT 条目

加上其他 Pod 的正常连接追踪条目(约 14 万个),轻松突破默认的 262144 上限。

四、解决方案

4.1 紧急处理(不中断业务)

立即调整 conntrack 表大小:

1
2
3
4
5
6
7
8
# 临时生效
echo 524288 > /proc/sys/net/netfilter/nf_conntrack_max

# 永久生效
cat >> /etc/sysctl.d/99-conntrack.conf << EOF
net.netfilter.nf_conntrack_max = 524288
EOF
sysctl -p /etc/sysctl.d/99-conntrack.conf

表大小翻倍后抖动立即消失。

4.2 降低 TIME_WAIT 超时

1
2
# 将 TIME_WAIT 超时从 120 秒降低到 60 秒
echo 60 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_time_wait

4.3 启用 TCP 连接复用

指导业务方在高频调用服务中启用 HTTP Keep-Alive 和连接池:

1
2
3
4
5
# 应用侧配置示例(Go net/http)
transport:
  max_idle_conns: 100
  max_idle_conns_per_host: 10
  idle_conn_timeout: 90s

Keep-Alive 复用连接后,新建连接速率从 1000/秒下降到约 80/秒,conntrack 压力直接降低了 90% 以上。

4.4 增加监控告警

在 Prometheus 中添加 conntrack 使用率告警规则:

1
2
3
4
5
6
7
- alert: ConntrackTableHigh
  expr: node_nf_conntrack_entries / node_nf_conntrack_entries_limit > 0.8
  for: 5m
  labels:
    severity: warning
  annotations:
    summary: "节点 {{ $labels.instance }} conntrack 表使用率超过 80%"

五、根因分析

本次问题的根因链条:

  1. 应用层:数据同步服务使用短连接方式调用外部 API,未启用连接池复用
  2. TCP 协议层:每个短连接断开后进入 TIME_WAIT 状态,持续 120 秒
  3. 内核层:conntrack 模块为每个 TCP 连接维护追踪条目,TIME_WAIT 条目在超时前不会被回收
  4. 资源瓶颈:默认 conntrack_max=262144,在 1000 cps 的场景下,仅 TIME_WAIT 就占用了近一半容量
  5. 故障表现:conntrack 表满后内核丢弃新 SYN 包,TCP 客户端多次重传超时后报 connection timeout

本质上是一个应用层设计缺陷(短连接)被底层内核资源限制(conntrack 表大小)放大的典型案例。

六、预防措施

  1. 应用层面:所有服务间调用默认启用连接池和 Keep-Alive,避免短连接模式。Code Review 阶段增加连接复用检查项。

  2. 内核参数标准化:将以下参数纳入节点初始化基线 ——

    1
    2
    3
    
    net.netfilter.nf_conntrack_max = 524288
    net.netfilter.nf_conntrack_tcp_timeout_time_wait = 60
    net.netfilter.nf_conntrack_tcp_timeout_established = 86400
    
  3. conntrack 监控:所有 K8s 节点接入 conntrack 使用率监控,设置 80% 预警、95% 严重告警。

  4. 容量规划:根据节点上预期 Pod 数量和连接模型,提前计算 conntrack 需求:

    1
    
    预期条目数 ≈ Pod数量 × 平均连接数 × 连接超时/平均连接时长
    
  5. 定期巡检:将 conntrack -S 输出纳入日常巡检脚本,关注 insert_failed(插入失败计数)的增长趋势。

七、总结

conntrack 表满是一个典型的"静默故障"——不会触发明显的错误日志,监控面板上可能只是偶尔几个超时曲线的小抖动,容易被忽视。但一旦触发,影响面很大,且排查依赖 dmesg 内核日志和 conntrack 工具的熟练使用。

这次排查再次印证了一个原则:在排查 K8s 网络问题时,不要只盯着 CNI 和 Service 层,宿主机内核网络栈的参数同样是关键变量。 很多时候问题的根因不在容器编排层,而在更底层的内核资源配置上。

调整后的集群运行至今再未出现类似问题,conntrack 使用率稳定在 40%-55% 之间。