遇到报错

k8s内容器请求外部服务,无法接受到响应报文,日志报错:connection reset by peer 等连接重置错误

产生原因

如下图所示,我们将数据包的生命周期分为 5 个阶段,问题就出在第三阶段。当 conntrack 不能识别返回的包时,就会将其标记为 INVALID 状态,包括以下几种情况:由于内存溢出,conntrack 无法继续跟踪连接;数据包超过了 TCP 窗口长度;等等。被 conntrack 标记为 INVALID 的数据包,没有相应的 iptables 规则来丢弃它,所以会被转发到客户端,但源地址没有被修改(图中的第4阶段)。因为该响应包的源 IP 是 Pod 的 IP,不是 Service 的 IP,所以客户端无法识别该响应包。这时客户端会说:“等一下,我不记得和这个 IP 有过任何连接,为什么这个家伙要向我发送这个数据包?” 然后客户端就会发送一个 RST 包给服务端的 Pod,也就是图中的第 5 阶段。不幸的是,这是 Pod 到 Pod 之间的合法数据包,会被安全送达服务端的 Pod。服务端 Pod 并不知道 DNAT 的过程,从它的视角来看,数据包 5 和 数据包 2 与 3 一样是合法的,现在服务端 Pod 只知道:“客户端准备跑路了,不想和我继续通信了,那我们就关闭连接吧!” 当然,如果想要正常关闭 TCP 连接,RST 包必须也是合法的,比如要使用正确的 TCP 序列号等。协商完成后,客户端与服务端都各自关闭了连接。

示例图

解决方案

给 conntrack 提供更多的自由,让它无论什么情况下都不会将数据包标记为 INVALID。可以通过以下命令在k8s各个节点实现:

echo 1 > /proc/sys/net/netfilter/nf_conntrack_tcp_be_liberal 

或者(根据服务器内核版本来确定使用哪种)

echo 1 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_be_liberal
作者:于浩  创建时间:2023-03-22 13:42
最后编辑:于浩  更新时间:2024-04-17 08:28