k8s集群高并发场景,kube-proxy使用ipvs模式的优缺点

k8s集群高并发场景,kube-proxy使用ipvs模式的优缺点

Deng YongJie's blog 1,229 2023-01-22

k8s集群高并发场景下,kube-proxy ipvs模式下的优缺点

问题说明

k8s集群在IPVS模式下,通过Service方式访问集群内部服务,偶现1秒延时的情况,引起该问题的主要原因为社区IPVS连接复用Bug。

IPVS连接复用参数说明

IPVS对端口的复用策略主要由内核参数net.ipv4.vs.conn_reuse_mode决定。

  1. 当net.ipv4.vs.conn_reuse_mode=0时,IPVS不会对新连接进行重新负载,而是复用之前的负载结果,将新连接转发到原来的RS(IPVS的后端)上。
  2. 当net.ipv4.vs.conn_reuse_mode=1时,IPVS则会对新连接进行重新负载。

在 Debian 10 操作系统中,没有内核参数名为 net.ipv4.vs.conn_reuse_mode,但是在使用 IPVS(IP Virtual Server)时,可以设置 net.ipv4.vs.conn_reuse_mode 参数来控制 IPVS 是否复用连接。具体含义和作用如下:

需要安装ipvs模块:apt install conntrack ipset ipvsadm -y

  1. 默认值 net.ipv4.vs.conn_reuse_mode 的默认值为 0,表示 IPVS 不会复用连接。
  2. 含义 net.ipv4.vs.conn_reuse_mode 参数用于控制 IPVS 是否复用已经建立的连接。当该参数设置为 1 时,IPVS 会尝试复用已经建立的连接;当该参数设置为 0 时,IPVS 每次需要建立新的连接。
  3. 作用 TCP 连接的复用可以减少连接建立的开销,提高网络传输的效率。当 IPVS 需要大量的 TCP 连接时,使用连接复用模式可以显著减少连接建立的时间和开销,提升网络性能。但是,如果网络传输的数据量较小,连接复用的效果可能不明显。
  4. 与 IPVS 的关系 IPVS 是一个高性能的负载均衡器,通过在内核空间实现负载均衡,可以提高网络传输的效率和可靠性。net.ipv4.vs.conn_reuse_mode 参数是 IPVS 的一个参数,用于控制连接复用的方式。通过设置该参数,可以调整 IPVS 的连接复用模式,提高负载均衡的效率和性能。 总的来说,net.ipv4.vs.conn_reuse_mode 参数可以根据网络传输的特点和需求进行配置,以提高网络传输的效率和性能。

kube-proxy 使用 IPVS 、iptables模式时,默认情况下会将 net.ipv4.vs.conn_reuse_mode 参数设置为 1,即开启连接复用模式。

如何查看ipvs内核参数?

sysctl net.ipv4.vs.conn_reuse_mode

所以需要手动把该参数关闭

#永久关闭

vim /etc/sysctl.conf
# Disable IPVS connection reuse mode
net.ipv4.vs.conn_reuse_mode=0

sysctl -p

如果将 net.ipv4.vs.conn_reuse_mode 参数设置为 0,即禁止连接复用模式,可能会导致以下问题:

  • 增加连接创建和销毁的开销,降低网络吞吐量和性能。
  • 由于 kube-proxy 在 IPVS 模式下使用了连接池技术,禁用连接复用可能会导致连接池中的连接不能被有效地复用和管理,从而可能会出现连接耗尽的情况。 如果开启和关闭连接复用模式的性能区别,取决于具体的网络环境和负载情况。一般来说,开启连接复用模式可以提高网络性能和吞吐量,但也可能会增加一些额外的开销和复杂性。如果禁用连接复用模式,在某些情况下可能会提高连接的可靠性和稳定性,但也会降低网络性能和吞吐量。因此,需要根据具体的应用场景和负载情况来选择是否开启连接复用模式。

如果将 net.ipv4.vs.conn_reuse_mode 参数设置为 1,即开启连接复用模式,可能会导致以下问题:

  • 开启连接复用模式可以提高网络性能和吞吐量,因为它可以减少连接创建和销毁的开销,同时减少了连接的延迟和开销,从而提高了网络的响应速度和效率。但是,在高并发和高负载的情况下,如果连接复用过多,可能会导致连接池中的连接资源被过度占用,从而影响其他连接的使用和管理,甚至可能导致网络拥塞和故障。 另外,需要注意的是,开启连接复用模式可能会增加一些额外的开销和复杂性,例如需要对连接池进行管理和优化,以及处理连接复用带来的一些关联问题。因此,在选择是否开启连接复用模式时,需要根据具体的网络环境和负载情况,结合实际的需求和性能指标进行权衡和选择。 总之,在使用 ipvs 时,需要综合考虑连接复用模式的优缺点,根据实际的需求和性能指标进行选择和调整,以达到最优的网络性能和效率。同时,还需要加强对连接池的管理和优化,避免出现连接资源过度占用和管理不当的情况。
  • 只要有 client ip:client port 匹配上 ip_vs_conn (发生复用),就直接转发给对应的 rs,不管 rs 当前是什么状态,即便 rs 的 weight 为 0 (通常是 TIME_WAIT 状态) 也会转发,TIME_WAIT 的 rs 通常是 Terminating 状态已销毁的 Pod,转发过去的话连接就必然异常。
  • 在高并发的情况下,如果大量地复用连接,可能会导致新的连接没有被正确地调度到后端的服务上,而是直接被转发到已经复用的连接所对应的服务上,导致一些服务被过度占用,而其他服务却空闲。这样就会导致一些新的连接被“固化”到部分服务上,而其他服务却无法得到合理的利用。
  • 举个例子来说,假设有三个后端服务 A、B、C,它们都可以处理客户端的请求。当客户端发起一个新的连接时,如果连接池中已经存在一个与客户端 IP 和端口相同的连接,那么这个新连接就会被复用,并直接转发到这个已有连接所对应的服务上。但是,如果复用过多,就可能会出现这样的情况:如果大量的连接都被复用到了服务 A 上,而服务 B 和 C 却一直处于空闲状态,这就会导致新的连接无法得到合理的分配和利用,从而影响整个系统的性能和效率。 在使用连接复用时,需要注意对连接的调度和分配,避免出现连接资源被过度占用和服务资源利用不均衡的情况,从而保证整个系统的稳定性和性能。
  • 再举个列子,如果有新的连接请求与已有连接的 IP 和端口相同,就会直接复用已有的连接,而不会去调度新的后端服务。 如果服务 A 正在滚动更新,也就是说服务 A 正在逐步停止服务并更新,那么这时候如果有新的连接请求到达,如果这些新的连接与已有连接的 IP 和端口相同,就会直接复用已有的连接,即使这个连接对应的是服务 A,也不会去调度其他后端服务,因为连接复用模式是不会改变已有连接对应的服务的,还是会转发到服务 A 上。因此,在这种情况下,流量仍然会转发到正在销毁的服务 A身上,这就导致了请求异常。 所以,在进行滚动更新等操作时,需要注意控制连接复用模式的使用,避免出现连接资源被过度占用和服务资源利用不均衡的情况,从而保证整个系统的稳定性和性能。

总结:业务中实际遇到的现象可能有很多种:

  1. 滚动更新连接异常。 被访问的服务滚动更新时,Pod 有新建有销毁,ipvs 发生连接复用时转发到了已销毁的 Pod 导致连接异常 (no route to host)。
  2. 滚动更新负载不均。 由于复用时不会重新调度连接,导致新连接也被 “固化” 在某些 Pod 上了。
  3. 新扩容的 Pod 接收流量少。 同样也是由于复用时不会重新调度连接,导致很多新连接被 “固化” 在扩容之前的这些 Pod 上了。

规避方案:

南北向流量

  1. 使用 LB 直通 Pod。对于南北向流量,通常依赖 NodePort 来暴露,前面的负载均衡器将流量先转到 NodePort 上,然后再通过 ipvs 转发到后端 Pod。现在很多云厂商都支持 LB 直通 Pod,这种模式下负载均衡器直接将请求转发到 Pod,不经过 NodePort,也就没有 ipvs 转发,从而在流量接入层规避这个问题。
  2. 使用 ingress 转发。在集群中部署 ingress controller (比如 nginx ingress),流量到达 ingress 再向后转时 (转发到集群内的 Pod),不会经过 service 转发,而是直接转发到 service 对应的 Pod IP:Port,也就绕过了 ipvs。Ingress controller 本身结合使用前面所说的 LB 直通 Pod 方式部署,效果更佳。(推荐)

东西向流量

集群内的服务间调用 (东西向流量),默认还是会走 ipvs 转发。对于有这种高并发场景的业务,我们可以考虑使用 Serivce Mesh (如 istio) 来治理服务流量,服务间转发由 sidecar 代理,并且不会经过 ipvs。

出于稳定性考虑,建议不开启连接复用功能, net.ipv4.vs.conn_reuse_mode 参数设置为 0

如何模拟复现连接是否被复用?

下面是一个可能的复现步骤:

  • 在 Kubernetes 集群中启用 IPVS 模式,使用端口复用。
  • 创建一个 Service,并将其绑定到一个端口上,同时指定多个 Pod 作为其 Endpoint。
  • 删除其中一个 Pod,查看连接池中,该 Pod 是否能够正常从 Endpoint 列表中移除。
kubectl get endpoints dnstools

image-1680148762136

  • 同时在worker节点查看ipvsadm规则
ipvsadm  -Sn |grep 10.43.23.126

image-1680148777106

如果出现了 RS 无法移除的问题,可能需要检查以下几点:

  1. 端口复用是否已经启用,如果没有启用,可以尝试启用端口复用。

  2. 是否存在其他未知的问题,例如网络延迟、防火墙设置等等。

  3. 检查 IPVS 的相关配置和内核参数是否正确。 在 Kubernetes 中,RS(Real Server)指的是 Service 的后端 Pod,用于接收来自客户端的请求,并提供相应的服务。在 IPVS 模式下,RS 可以通过配置 Endpoint 来进行定义和管理。