K8s 服务间歇性 503 与 conntrack 表溢出排查
2026-08-11 00:53:03 # Kubernetes

用户访问偶发 503,重启 Pod 能短暂恢复,但应用日志里完全查不到报错。这种”网关报 503、应用无日志”的组合,往往不是应用层问题,而是内核连接跟踪表(conntrack)溢出。本文给出完整的定位与解决路径。

问题现象与背景原因

典型现象:请求随机返回 503,且网关(Ingress Nginx / 业务网关)日志里有大量 502/503,但后端 Pod 的 access log 里查不到对应请求——说明请求在到达应用之前就被丢了。

1
2
curl https://pay.example.com/callback
<html><body><h1>503 Service Temporarily Unavailable</h1></body></html>

核心原因:conntrack 表溢出

  • kube-proxy 在 iptables 模式 下,依赖 Linux 内核的连接跟踪表(nf_conntrack)做 NAT 转发。
  • 高并发(尤其短连接、支付回调、健康检查探测这类每秒成千上万次新建连接)场景下,conntrack 表项来不及回收,表被填满。
  • 表满后,新连接在内核 netfilter 层被直接 DROP,表现为随机 503 / 超时,而应用层和 kube-proxy 日志都看不出异常(连接在内核就被丢了)。
  • 重启 Pod 会清空其相关 conntrack 表项,所以”重启能短暂恢复”,但流量一上来表又满了。

提示:503 但应用日志全干净、且重启能短暂恢复——这是 conntrack 溢出最典型的特征,优先往这个方向查。

排查过程(思路与定位方法)

第一步:登录任意工作节点,看 conntrack 当前用量与上限。

1
2
3
4
5
# 当前已用 conntrack 表项
sysctl net.netfilter.nf_conntrack_count
# 表容量上限
sysctl net.netfilter.nf_conntrack_max
# 两者对比:count 接近 max 即溢出

nf_conntrack_count 长期接近 nf_conntrack_max,基本可确定溢出。

第二步:看内核有没有丢包记录。

1
2
3
4
5
# 查看 conntrack 因表满而丢包的次数
dmesg | grep -i conntrack
grep 'nf_conntrack: table full' /var/log/messages
# 网络层丢包统计
nstat -az | grep -i conntrack

nstatnf_conntrack 相关计数持续增长也佐证溢出。

第三步:确认 kube-proxy 当前模式(iptables 模式风险最高)。

1
2
3
4
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode
# 或在节点看规则类型
sudo iptables-save -t nat | head
sudo ipvsadm -Ln 2>/dev/null | head

第四步:确认是不是短连接洪峰导致。 看节点新建连接速率、Ingress 每秒请求数,与 503 出现时间是否吻合。

最终的解决方法

临时止血(立刻生效,重启节点后失效):

1
2
3
4
5
# 调大 conntrack 表容量(按内存估算,一般设为 内存KB/16K 或更大,如 4 倍)
sysctl -w net.netfilter.nf_conntrack_max=1048576
sysctl -w net.netfilter.nf_conntrack_buckets=262144
# 缩短表项回收时间(默认 120s 可降到 60s,加快释放)
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=600

长期根治(三选一或组合):

  1. 切换 kube-proxy 为 IPVS 模式:IPVS 基于哈希表,不依赖 per-connection conntrack,能扛更高并发,是社区推荐的高性能方案。
  2. 前置 Nginx / 网关做连接收敛:把海量短连接收拢为少量长连接打到后端,降低 conntrack 压力。
  3. 服务改长连接 / 连接池:客户端复用连接,减少新建连接数。

提示:IPVS 模式同样会在某些场景(如 masquerade)用到 conntrack,但整体并发能力远强于 iptables,是生产环境首选。

操作命令(可直接复制执行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 查 conntrack 用量与上限
sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max

# 2. 查内核丢包日志
dmesg | grep -i conntrack
nstat -az | grep -i conntrack

# 3. 确认 kube-proxy 模式
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode

# 4. 临时调大表容量(节点上执行,重启失效)
sysctl -w net.netfilter.nf_conntrack_max=1048576
sysctl -w net.netfilter.nf_conntrack_buckets=262144

# 5. 切换 kube-proxy 为 IPVS(改 ConfigMap 的 mode: ipvs 后重启)
kubectl edit configmap kube-proxy -n kube-system # 改 mode: "ipvs"
kubectl rollout restart daemonset kube-proxy -n kube-system