Kubernetes 零宕机滚动更新

 无论我们如何连接到应用程序,Kubernetes 的目标都是在滚动更新的过程中最大程度地减少服务的中断。一旦新的 Pod 处于活动状态并准备就绪后,K8s 就将会停止旧的 Pod,从而将 Pod 的状态更新为 “Terminating”然后从 Endpoints 对象中移除,并且发送一个 SIGTERM 信号给 Pod 的主进程。SIGTERM 信号就会让容器以正常的方式关闭并且不接受任何新的连接。

Pod 从 Endpoints 对象中被移除后,前面的负载均衡器就会将流量路由到其他(新的)Pod 中去。这个也是造成我们的应用可用性差距的主要原因,因为在负责均衡器注意到变更并更新其配置之前,终止信号就会去停用 Pod,而这个重新配置过程又是异步发生的,所以并不能保证正确的顺序,所以就可能导致很少的请求会被路由到终止的 Pod 上去。如图

                     

 

 

 

 

 总结一下:在滚动更新,或者缩容的时候 会发生两个事件 一个是POD  的停止和删除 ,一个是更新相应的EP 资源和 kube-proxy 更新server资源的EP

同时发生了多个event 事件,所以有可能不符合预期,比如我POD 已经终止了,但是我的EP 资源还没更新,或者kube-proxy 没有更新IPVS 规则。这些都会引发访问异常

 

 

解决这个问题的方式: 

首先,要实现这个目标的先决条件是我们的容器要正确处理终止信号,在 SIGTERM 信号上实现优雅关闭。下一步需要添加 readiness 可读探针,来检查我们的应用程序是否已经准备好来处理流量了。

可读探针只是我们平滑滚动更新的起点,为了解决 Pod 停止的时候不会阻塞并等到负载均衡器重新配置的问题,我们需要使用 preStop 这个生命周期的钩子,在容器终止之前调用该钩子。

生命周期钩子函数是同步的,所以必须在将最终终止信号发送到容器之前完成,在我们的示例中,我们使用该钩子简单的等待,然后 SIGTERM 信号将停止应用程序进程。同时,Kubernetes 将从 Endpoints 对象中删除该 Pod,

所以该 Pod 将会从我们的负载均衡器中排除,基本上来说我们的生命周期钩子函数等待的时间可以确保在应用程序停止之前重新配置负载均衡器

所以我们通常的解决方案是在容器结束的时候 做一个脚本或者运行一个sleep命令

lifecycle:
preStop:
exec:
command: ["sleep", "20"]

这里有一点要注意,Kubernetes 将在 30 秒后强行终止该进程(除非我们更改 Pod 定义中的 terminationGracePeriodSeconds) terminationGracePeriodSeconds默认值是30s 

我们线上遇到一个问题是kube-proxy 更新 EP 规则的时候,也就是更新IPVS  规则的时候花费了大概5分钟左右,所以我们定义了300s以上, terminationGracePeriodSeconds=600s

定义太长 Pod 的 endpoint 将 unreachable。如果我们公开指标以监控 Pod,将无法访问 Pod。Prometheus 之类的工具依赖于 Endpoints 在集群中 scrape Pod。一旦删除 Pod,endpoint 删除信息就会在集群中传播,甚至传播到 Prometheus,所以一般我们处理完没及时更新的流量就结束就可以了,这个值一般也不会太大,如果太大,就需要找问题了

真有长时间的任务需要处理,通过不同的deploy 来达到目的,然后处理完以后,删除旧的deploy 就可以

kube-proxy 使用lvs 代理更新rs规则慢的原因:

查看源码

大概逻辑是:对于非UDP协议,ipvs 中的活跃连接+不活跃连接为0才能去刷新对应vip和rip对应关系。

activeConnn + InactiveConn == 0 立刻刷新对应关系

如果不等于0  会等activeConnn + InactiveConn == 0  才去刷新

https://github.com/kubernetes/kubernetes/blob/master/pkg/proxy/ipvs/graceful_termination.go#L170

 官方讨论的这个问题帖子:

https://github.com/kubernetes/kubernetes/issues/81775

找到了腾讯容器云团队提供的解决方案,但是没有发布到官方内核,需要自己找补丁打

需要设置两个参数

net.ipv4.vs.conn_reuse_mode=1 

K8S 1.13 里为了提升性能,把 /proc/sys/net/ipv4/vs/conn_reuse_mode 从默认值1 改成 0 了,

这个值为 1 时,会导致某种情况下新建连接有 1s 的额外延迟,而改成 0 的效果是, 如果请求 cluster IP 的源端口被重用了,也就是在 conntrack table 里已经有了 <src_ip, src_port, cluster_ip, cluster_port> 条目了,

那么 IPVS 会直接选择之前服务这个 <src_ip, src_port> 的 pod,并不会真的按权重走负载均衡逻辑,导致新的连接去了那个要被删除的 pod,当那个 pod 30s 后被删除后,这些重用了源端口的流量就会连接失败了。

在这个四元组里 src_ip 是请求服务的客户端 pod ip,对于这一个客户端 pod 来说是固定的,cluster 的 ip 和 port 也都是固定的,只有 src_port 可以在 net.ipv4.ip_local_port_range 规定的不到三万个端口里变化,在高并发短链接情况下非常容易发生源端口复用的情况。

net.ipv4.vs.conn_reuse_old_conntrack =1 (只有用他们补丁才会有)

下面的2个链接就是补丁的说明:

其中一个修复了上面conntrack表项被重用时的1秒延迟问题,

另一个修复了使用IPVS表中的过期连接表项时丢弃数据包的问题:

http://patchwork.ozlabs.org/project/netfilter-devel/patch/20200701151719.4751-1-ja@ssi.bg/

http://patchwork.ozlabs.org/project/netfilter-devel/patch/20200708161638.13584-1-kim.andrewsy@gmail.com/

下一个官方提供的讨论帖子

 https://github.com/kubernetes/kubernetes/issues/90042

https://maao.cloud/2021/01/15/%E6%B7%B1%E5%85%A5kube-proxy%20ipvs%E6%A8%A1%E5%BC%8F%E7%9A%84conn_reuse_mode%E9%97%AE%E9%A2%98/

更新系统到最新版内核的方式看看是否解决上面的两个问题,占时没测试,线上的项目,占时不敢做内核升级的操作,等后续测试环境在测试这个问题吧

http://elrepo.org/tiki/HomePage

https://blog.csdn.net/qq_27281257/article/details/82049634

还有一种解决方案是用iptables 代替LVS:根据各方反馈可以解决问题,但是需要控制集群规模

具体对比

 

 总结:

在Service数量为100和500时,iptables转发性能要优于IPVS;Service数量达到1000时,两者大体持平;Service数量继续增大,IPVS的性能优势则越发明显

 

https://docs.ucloud.cn/uk8s/userguide/kubeproxy_mode

下面是参考的一些相关文章

https://maao.cloud/2021/01/15/%E6%B7%B1%E5%85%A5kube-proxy%20ipvs%E6%A8%A1%E5%BC%8F%E7%9A%84conn_reuse_mode%E9%97%AE%E9%A2%98/

 

最终优化方案

centos 8 的系统都是默认值

net.ipv4.vs.conntrack=0

net.ipv4.vs.conn_reuse_mode=1

net.ipv4.vs.expire_nodest_conn=1

 

 

https://tencentcloudcontainerteam.github.io/2019/12/15/no-route-to-host/

https://zhuanlan.zhihu.com/p/115746269 

https://learnk8s.io/graceful-shutdown

https://zhuanlan.zhihu.com/p/127099484

https://www.qikqiak.com/post/zero-downtime-rolling-update-k8s/

https://mp.weixin.qq.com/s?__biz=MzI5ODQ2MzI3NQ==&mid=2247492422&idx=2&sn=9bea0f7bca63b897f5a8f3f0a53e5f2e&chksm=eca7da02dbd0531440baea6935531201842adca5f46c0e32fe45e7e00081da947f41791a498c&scene=126&sessionid=1600831337&key=ec3f6bfa6a9a817a3883eb3dd676eeaa2fb7cc9f4911487e8c6e65bb733330c4a687f3f75fdac502ae65768bace7b1e169ffb21b05fb547e5b8398429228c8983f6497bbd458c9bfdd720cc3650bde34b44bd64cedfc5e844780a590fb424544daf0055496270d0ece2f0c3e05d476fbfbed6f927687a0e4e06c36c0b3a917e8&ascene=1&uin=NjkxMzI3MDAw&devicetype=Windows+10+x64&version=62090538&lang=zh_CN&exportkey=A2QkN2AiEdjVghZ3cqCIs04%3D&pass_ticket=oX99%2BSNUMDdaQuz6b02VAdas0kro3lvY9f4gBISzZHcm4%2BDwyIj8ij7ZX46%2B2vBT&wx_header=0

 

posted @ 2020-09-23 09:24  屌丝的IT  阅读(1418)  评论(0)    收藏  举报