K8s kube-proxy iptables 模式下 NodePort 间歇性超时------技术复盘
问题现象
生产集群某天突然出现告警:某个 NodePort Service 的外部访问间歇性超时(大约 1/10 的请求失败),报错 connection timeout。Pod 本身健康、重启无效、同一 Service 的其他端口正常。
诡异的是:curl NodeIP:NodePort 时有时通有时不通,但直接在 Pod IP + targetPort 访问永远正常。
排查过程
Step 1 --- 排除 Pod 层
- kubectl exec 进 Pod 内部 curl localhost:port → ✅ 正常
- 直接 curl : 从节点上发起 → ✅ 正常
- Pod 的 readiness probe 也是绿的
- 结论:问题不在 Pod
Step 2 --- 排除 Service/Endpoints
- kubectl get endpoints → 正常,IP:Port 列表正确
- kubectl describe svc → 端口映射正确
- 结论:Service 抽象层没问题
Step 3 --- 定位到 kube-proxy
- 出问题的节点上执行 iptables-save -t nat | grep
- 发现对应 KUBE-NODEPORTS 链中的 DNAT 规则存在,但概率性丢包
- 进一步查看 conntrack -L | grep ,发现大量 UNREPLIED 状态的连接
Step 4 --- 根因浮现
- 出问题的节点上,ip_conntrack 表已接近满(/proc/sys/net/netfilter/nf_conntrack_count ≈ 最大值)
- nf_conntrack_max 默认 262144,但该节点承载了大量短连接 Service
- 当 conntrack 表满时,新连接无法建立 NAT 映射,表现为随机丢包
- 为什么只有一个 Service 出问题?因为该 Service 的流量恰好落在 conntrack 压力最大的 CPU 核心上
根因
nf_conntrack 表满导致 iptables NAT 模式下 NodePort 新连接随机失败。
kube-proxy iptables 模式依赖 netfilter conntrack 做连接跟踪。NodePort 流量经过 DNAT 转换后,conntrack 需要为每个连接维护一个条目来记住 NAT 映射关系。当 conntrack 表满载,新连接无法建立映射,直接丢弃------这就是间歇性超时的真相。
解决方案
1. 紧急止血
ash sysctl -w net.netfilter.nf_conntrack_max=1048576
2. 考虑切换到 IPVS 模式
- iptables 是 O(n) 规则匹配,IPVS 是 O(1) 哈希查找
- IPVS 对 conntrack 的依赖更小,性能更好
- kubectl edit cm kube-proxy -n kube-system,改 mode: ipvs
3. 监控 conntrack 使用率
- Prometheus 采集
ode_nf_conntrack_entries /
ode_nf_conntrack_entries_limit - 告警阈值:使用率 > 80%
4. 优化应用连接池
- 短连接改长连接 / 连接池复用
- 避免频繁创建销毁 TCP 连接
复盘总结
| 维度 | 收获 |
|---|---|
| 排查方法论 | 从 Pod → Service → kube-proxy → conntrack 逐层收缩,不跳步 |
| 关键命令 | conntrack -L、iptables-save -t nat、sysctl net.netfilter |
| 深层教训 | K8s 网络问题根因往往不在 K8s 本身,而在内核网络栈 |
| 可复用经验 | NodePort 间歇性超时 + Pod 正常 = 优先查 conntrack / kube-proxy |
| 架构决策 | 高并发场景优先考虑 IPVS 模式,后期迁移成本远大于早期规划 |
💡 一个命令救火:conntrack -S 看一眼当前连接数 vs 上限,往往能省掉一小时的瞎排查。
#K8s #kube-proxy #iptables #IPVS #conntrack #技术复盘 #NodePort #Kubernetes网络