K8S – 如何平滑发布应用?

相信大家在使用K8S的时候,都会遇到发布应用引起调用方超时/中断报错的问题,要解决这个问题需要对K8S的工作原理有一个基本理解。

区分情况

发布应用的时候,引发报错的原因我分为2类:

  • 上线POD:应用没拉起来,流量就进入了。
  • 下线POD:流量没停掉,应用就关掉了。

Service与Pod联动原理

先说POD上线。

如果没有配置健康检查,那么POD启动瞬间就会被service加入负载均衡,可能这时候服务还没初始化完成,流量进来必然有损。

因此,POD上线最重要的就是配置健康检查,这样service仅在POD健康检查成功之后才会将POD加入endpoints列表,流量进入可以正常响应。

结论:POD上线只需要配置健康检查,即可避免流量损失。


再说POD下线。

POD下线时会在etcd中先标记POD状态为terminating退出中,其他相关联动资源会监听到变化并采取后续动作。

如果没有配置lifecycle preStop的话,那么POD(实际上是里面的container)会立即收到SIGTERM信号,你的程序应当开始优雅退出:

先关闭监听,处理完已有请求,退出进程。

但是这里要注意,给POD发送SIGTERM信号的同时,Service也会同时的摘除转发规则,所以可能出现一种时序关系,导致流量损失:

程序先关闭监听,Service才摘除转发规则。

结论:在没有使用preStop的情况下,除非你让程序收到SIGTERM后延迟一会再关闭监听,否则可能丢失流量。


带lifecycle preStop的POD下线更加常见。

这时候不会立即给POD发送SIGTERM,而是先调用preStop Hook处理,等其返回后再发送SIGTERM。

我们可以在preStop里sleep 1~2秒,确保Service先将转发规则下线(也就不会有新连接转发过来),然后按照后续SIGTERM流程就可以安全的关闭监听端口进行优雅退出了。

在preStop里也经常对程序进行主动关闭,这样后续K8S发起的SIGTERM也就没有什么意义了,这种做法也有它的实际意义,稍后会说。

结论:在preStop中sleep 1~2秒,用以确保Service先下线规则,从而程序可以开始优雅退出。另外,很重要的一个认识是:当你要下线POD的那一瞬间,Service就立即开始摘除转发了,与是否使用preStop无关。


无论是否使用preStop,POD如果在配置项termination_grace_period_seconds秒内没有退出程序的话,K8S会发送SIGKILL强行杀死程序,确保容器立即退出。

如果在termination_grace_period_seconds后,程序仍旧在处理现有流量没有得以退出的话,可能会因为SIGKILL而导致部分计算中的流量损失。

termination_grace_period_seconds包含preStop所花费的时间,所以配置termination_grace_period_seconds时要使用preStop+程序优雅退出耗时的总和。

结论:termination_grace_period_seconds非常重要,总是记得配置。


为什么preStop广泛被使用?

这个原因就有点意思了。

原因1

因为容器启动的时候往往需要做一些环境初始化之后再启动应用程序,所以镜像很少使用exec方式直接拉起二进制程序,而大多是使用shell方式执行shell命令来执行一连串的启动命令,所谓exec模式就是[xxx, xx, xxx]这样的写法。

举个例子,我在Dockerfile里是这样配置的启动命令:

这种没有[]的就是shell模式,其实容器启动时候变成了这样:

因此container的PID=1进程就是/bin/bash,而K8S的SIGTERM是发给PID=1进程的,所以我们的/usr/bin/go-xxx程序压根就不知道SIGTERM信号,也就不会开始优雅退出,所以最终的命运就是等待termination_grace_period_seconds秒后容器被强制SIGKILL杀死。

这时候我们就只能靠preStop了,可以采用pkill等命令发送SIGTERM信号给/usr/bin/go-xxx进程,这样才会让它真的开始优雅退出。

原因2

很多公司有自己的服务注册与发现系统,在POD下线时就会面临1个问题:

正在优雅退出的POD,其IP可能还缓存在其他某个调用方的内存里,所以调用方还会调用该POD,因此会导致连接失败。

这种情况就要依靠preStop,主动调用注册中心摘除注册,并且略作等待确保变更事件已经同步到所有调用方,然后再进行优雅退出(关闭监听,处理剩余流量)。

 

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~