最近折腾起K8S来,顺便整理下遇到的问题。

首先是探针问题,早期版本只有存活探针(liveness probe)和就绪探针(readiness probe),这两个探针没有依赖关系,是同时开始探测的。我总想不明白,程序存活检测都没通过,为啥要浪费时间做就绪检查?等到新版本支持第三个探针:启动探针(startup probe),总算是没毛病了。 为什么要增加启动探针?我想到业务上有些程序启动确实太慢(比如几分钟),以前需要在存活探针加很长的延迟时间(delay),这个延迟其实不好设置,程序随着迭代可能启动更慢,导致需要在fail次数上容忍更多,fail次数更多导致程序运行时被重启的时间被迫也拉长了,这对服务故障恢复不利;又或者程序启动变快了,却又因为延迟是固定的,滚动更新时又白白浪费时间拖慢发布流程。 现在好了,设置启动探针后,只有等启动探针通过后,存活、就绪探针才会开始干活。这就解决了上面提的两个问题,只要启动成功就可以尽快开始服务,不需要像之前那样必须等一个写死的延迟时间;同时存活探针fail次数也可以设置更合理,以便在程序故障时尽快被重启。

另一个问题就是滚动更新时的优雅关机问题,实际情况就是程序开始关闭了,还有业务流量打到这个容器里,导致异常响应。这也很奇怪,K8S本身是知道关机事件的,应该可以提前切走流量才对。网上找到相关文章学习了下,原来是关闭容器开始,切流量和发送关闭信号给容器是同步进行的,但是发关闭信号非常快,而切流量过程本身链路较长(更新etcd,通知ingress,再变更iptables规则等等)实际是异步执行,这两件事之间有时间差,所以程序关闭了,流量还要延迟一会才会切走,导致用户遇到500。因此优雅关机要稳一点还需要在preStop里sleep一会,比如sleep 5,一般流量就切好了。关闭信号会在preStop执行完后发出。 我仔细看了下应用设置,发现preStop里就写了一句stop应用,难怪优雅不起来,流量还没切掉,就主动stop了,增加sleep后问题解决。

顺便又思考了下,为啥会在preStop里主动stop应用?明明K8S会发送关闭信号的啊,测试了下,发现应用没有关闭过程,只等30s超时后被SIGKILL。看来是关闭信号被忽略导致,这里就涉及到容器的init(1号)进程是否会传递信号了,回看应用设置,启动命令是sh,破案了,它不是一个合格的init进程,怪不得需要在preStop里写一个主动stop呢。如果要少写这个stop,可能需要把init程序换一个合适的轻量级init程序,比如tini

ref:

https://freecontent.manning.com/handling-client-requests-properly-with-kubernetes/

https://medium.com/@meng.yan/what-happens-when-deleting-a-pod-d1219c7e1b53