TMD,K8S Ingress 流量转发给我整出幺蛾子,深挖一下 conntrack 这坨屎

刚他妈开完会,一帮产品经理和不懂技术的领导在那儿扯淡,什么用户体验,什么快速迭代,迭代你奶奶个腿!出问题还不是老子来擦屁股?就说昨天晚上,K8S 集群里的一个 Ingress,流量突然就断了,监控告警刷屏一样。查了半天,发现是 conntrack 表满了!
Conntrack 这玩意儿,说白了就是 Linux 内核用来跟踪 TCP 连接状态的。在 NAT 环境下,它尤其重要。因为 Ingress Controller 作为流量入口,通常会做 SNAT,把客户端 IP 转换成自己的 IP 去访问后端服务。没有 conntrack,内核就不知道哪个响应包该发给哪个客户端,整个连接就乱套了。
现在 K8S 动不动就上千个 Pod,每个 Pod 后面可能还有成百上千的连接。Conntrack 表默认大小就那么点儿,稍微有点并发就满了。更操蛋的是,现在这帮年轻人,一上来就搞什么 Service Mesh,Envoy 代理满天飞,每经过一个代理,就多一层 NAT,conntrack 的压力就更大。他们懂个屁!就知道抄概念,底层的玩意儿一概不关心。
好了,废话不多说,直接上排查步骤和解决方案。
一、排查步骤
1. 确认 conntrack 表是否满了:
bash
sysctl net.netfilter.nf_conntrack_count
sysctl net.netfilter.nf_conntrack_max

如果 nf_conntrack_count 接近或等于 nf_conntrack_max,那就是满了。
2. 查看 conntrack 的丢包情况:
bash
ss -s

看输出里的 TCP establishedTCP orphanedTCP timewait 等指标。如果 TCP timewait 数量异常高,说明 TIME_WAIT 状态的连接太多,占用了 conntrack 的资源。
3. 抓包分析:
bash
tcpdump -i any -n -nn -vvv -s 0 port 80 or port 443

抓包看看是不是有大量的 SYN 包,或者 RST 包。如果是 SYN 包,说明有大量的连接请求被拒绝;如果是 RST 包,说明连接被异常关闭。这两种情况都会导致 conntrack 资源快速消耗。
二、解决方案
1. 调整 conntrack 表大小:
bash
sysctl -w net.netfilter.nf_conntrack_max=262144
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600

nf_conntrack_max 调大一点,但也不能太大,否则会占用大量的内存。nf_conntrack_tcp_timeout_established 可以适当调小,缩短 ESTABLISHED 状态连接的超时时间,释放 conntrack 资源。
注意: 这个只是临时生效,重启机器就没了。要永久生效,需要修改 /etc/sysctl.conf 文件,然后执行 sysctl -p
net.netfilter.nf_conntrack_max = 262144
net.netfilter.nf_conntrack_tcp_timeout_established = 3600

2. 优化 TCP 连接:
* 启用 TCP Fast Open (TFO): 可以减少 TCP 连接建立的时间,降低 SYN 包的重传率。
bash
sysctl -w net.ipv4.tcp_fastopen=3

同样,要永久生效,需要在 /etc/sysctl.conf 文件中添加 net.ipv4.tcp_fastopen = 3
* 调整 TIME_WAIT 状态的连接: TIME_WAIT 状态的连接过多,会占用大量的 conntrack 资源。可以通过以下参数进行调整:
bash
sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_tw_recycle=1

注意: tcp_tw_recycle 在 NAT 环境下可能会有问题,不建议开启。
3. 优化 Ingress Controller 配置:
* 合理设置健康检查: 频繁的健康检查会产生大量的连接,增加 conntrack 的压力。
* 使用连接池: Ingress Controller 可以使用连接池来复用 TCP 连接,减少连接建立和关闭的开销。
4. 使用 eBPF 进行 conntrack 优化 (高级玩法,慎用):
eBPF 允许你在内核中运行自定义代码,可以用来优化 conntrack 的行为。例如,你可以使用 eBPF 来快速删除不活跃的连接,或者根据特定的策略来选择是否跟踪某个连接。但这玩意儿调试起来很麻烦,一不小心就把内核搞崩了。
总结
Conntrack 满了,是个很常见的 K8S 问题。解决思路就是:要么增加 conntrack 表的大小,要么减少 conntrack 表的占用。但是,治标不治本。根本的解决方案还是要优化你的应用程序和网络架构,减少不必要的连接,提高连接的复用率。
现在的年轻人,总想着搞一些花里胡哨的东西,Service Mesh、Serverless,搞得越来越复杂。底层的这些东西,还是要好好学学,否则,出了问题,只能抓瞎。
TMD,说了这么多,还是解决不了这帮 SB 搞出来的烂摊子。老子要下班!