Kubernetes集群滚动更新导致Pod应用程序流量丢失的原因

Kubernetes滚动更新导致Pod流量丢失的原因

1.Pod流量丢失的原因

我们在K8S集群中的Pod应用程序需要经常性的进行滚动更新,每次滚动更新都会导致Pod流量的丢失,每次滚动更新之后,新的Pod虽然被创建出来了,但是只要新的创建并且启动成功后,就会将旧的Pod资源立刻删除,旧的Pod应用程序很有可能此时正在提供服务,直接将旧的Pod资源删除,如果没有做任何处理的话,是会对应用程序的流量造成一定的影响,正在访问被删除的旧Pod资源的用户就可能会出现接口异常的报错信息。

如果我们的Pod资源没有设置健康检查探针(readlinessprobe/livenessprobe)的话,Pod的应用状态一直会被认为是就绪状态的,即使Pod出现了故障,也会将用户的请求转发到该Pod上,并且在滚动更新过程中,新的Pod资源只要一处于Running状态,Kube-Proxy更新第一个iptables转发规则后,K8S就会将流量转发到应用程序Server资源下管理的Pod资源,Pod资源这时也会告诉K8S集群已经就绪了,其实这时候Pod资源只是启动了,里面的应用程序换没有真正的启动完毕,还不能正常处理流量的转发,那么用户的请求落在这个Pod应用程序后,就会收到节点异常连接失败的报错信息。

2.如何优雅的处理Pod流量丢失的问题

2.1.理解Pod删除过程

Kubernetes的各个组件之间都是独立运行的,是一个巨大的整体,这些组件在同步数据过程中会花费一定的时间,并不是实时性的同步。

当APIserver收到一个Pod资源删除的请求后,首先去修改Etcd数据库中的Pod资源状态,也就是修改成Terminating状态。然后通知Kubelet组件删除具体的Pod资源,删除掉Pod资源后,会调用Kube-Proxy将该Pod资源的转发规则在iptables中删除,此时就会迅速在Endpoints控制器中将删除的Pod资源剔除,由于APIserver调用Kube-Proxy删除iptables转发规则并不是实时性的,会有一定的延迟,也就是说在iptables规则还没有得到删除时,用户的流量还是会被分配到被删除的Pod资源上,但是实际上提供服务的Pod资源已经被删除了,就会造成流量的丢失。

Pod的删除过程一共分为两个事件

  • 事件A:APIserver收到Pod的删除请求后,会尽快通知各个组件停止Pod一系列操作,此时就会调用Kubelet删除Pod资源,并且会调用Kube-proxy将该Pod资源的iptables转发规则删除,如果Pod资源没有响应,就会将Pod资源强制删除。如果Pod在这些操作过程中相应了Kubelet的删除请求,就会迅速进行删除,此时用户在该Pod上的流量就会丢失。
  • 事件B:收到Pod的删除请求后,APIserver会向Endpoints资源发送一个RESET请求,然后将Service管理的Pod副本中将这个被删除的Pod资源剔除。

在这里插入图片描述

事件A与事件B都是同一时刻去处理的,但是从Endpoints中剔除应用程序的时间要比清除iptables转发规则时间要短一些,APIServer需要向每一个节点的Kube-Proxy发送删除Pod的iptables的转发规则的请求,所以用时会稍微长一些,但是删除资源的信息先被Endpoints控制器处理完了,转发请求还没有清理,用户的请求就会被转发到一个被删除的Pod资源上,此时就产生了用户访问应用程序的流量丢失。
在这里插入图片描述

2.2.如何优雅的避免Pod流量丢失

我们已经明确了Endpoints会在转发请求删除之前就会将Pod资源删除,如果我们可以让Endpoints处理删除Pod资源的请求时,让Pod资源不要立刻删除,稍微等待几十秒,再让Endpoints删除该Pod,就可以避免由于iptables规则没有清除导致Pod流量丢失的问题,因为删除Pod资源和清除iptables规则是同时去做的,只是iptables的清除时间稍微长一些而已。

官方之前也提到过采用健康检查探针的方式去解决这个问题,但是健康检查探针是在容器启动之前去处理的,并不能在容器退出时去完成这些操作。

针对如果让Pod资源等待一段时间再去删除的情况,我们可以通过Pod的钩子函数来实现。

Pod的钩子函数提供了容器在运行前和退出后执行一些操作的功能,我们可以配置一个容器退出后的钩子函数,让容器接收到被删除的指令,在准备退出时执行一条命令比如sleep 30,让容器等待几十秒之后再退出,再这个时间内足够能让iptables的转发规则得到清除,避免用户的流量造成丢失。

        lifecycle:        
          preStop:       
            exec:       
              command: ["sh","-c","sleep 5"]

关于Pod钩子函数在这里也有详细讲解到:Kubernetes集群Pod资源钩子函数详解

猜你喜欢

转载自blog.csdn.net/weixin_44953658/article/details/121901265