目录

背景

出现问题

问题追溯

解决方法

总结

参考文档(issue 和 pull request 已按照时间线梳理)


本篇建议建立在以下文档之上进行阅读:

 配置存活、就绪和启动探测器

关于 K8S 探针的最佳实践

背景

众所周知,日志作为实现软件可观测性的三大支柱之一,为了解系统运行状况,排查系统故障提供了关键的线索,在运维管理中起着至关重要的作用。Kubernetes 提供了两种原生的日志形式——审计(Audit)和事件(Event),它们分别记录了对于集群资源的访问以及集群中发生的事件信息。本篇的背景正是以K8S事件为基础建立可观测性:通过收集 Kubernetes Events 来进行事件告警,从而能够预知生产环境上可能出现的风险,及时应对故障。

出现问题

在实际应用的过程中,发现事件很大一部分来源于探针的检测,一开始的定位原因是探针中配置的 TimeoutSeconds 配置过小导致探测接口超时。但是实际上的原因并非如此。

我们知道,当修改  deployment 资源的属性时,deployment controller 会建立一个新的复制集(ReplicaSet),而kubelet会把原来的复制集中的容器的状态置为 Terminating,从而实现 RollingUpdate。

那么 Terminating 是一个什么状态呢?当我们对 deployment 更新或者删除 Pod 的时候,kubelet 会先给容器中的主进程发 SIGTERM 信号来通知进程进行 shutdown 以实现优雅停止,如果超时进程还未完全停止则会使用 SIGKILL 来强行终止。

通常我们可以修改 Pod  层面的 terminationGracePeriodSeconds 属性来设置这个超时时间,默认是30s,这段时间范围内我们也可以配置 PreStop 来做收尾工作。

而问题的原因就在于当 Pod 处于 Terminating 状态时,探针检测依旧在进行,但这个时候主进程已经收到了终止信号。所以当有大量 deployment 被更新,就像是微服务应用发布场景时,就会产生事件告警风暴。

问题追溯

我从 github 社区中追溯了一下这个问题,最早这个问题在 2017.9 被提出,随着越来越多人反馈这个问题,社区在 2021.2 修复了这个问题并将改动合入 master 分支,最终在 2021.4 于Kubernetes 1.20 release 版本推出。我所使用的环境是 AWS EKS,查看了一下版本是 1.20,按理说应该没有这个问题,但是在实际使用时并没有得到有效解决,这是为什么呢?2022.1 有人在社区里提出了相同的疑问。原来,在之前的pr中并没有彻底解决这个问题:

对该部分源码感兴趣想研究的朋友可以在 issue 中找到相关的代码片段解读。

 目前,该问题已经得到解决。

也就是说,云服务商提供的发行版中,依旧有这样的问题。

解决方法

升级集群是不可能了,解决该问题有两个思路:

1.通过事件发送的客户端上对信息进行过滤

2.通过配置相对合理的策略来减少事件的发生频率

从配置入手,主要是修改 Pod  层面的 terminationGracePeriodSeconds 和 LivenessProbe 以及 ReadinessProbe 的 PeriodSeconds 配置字段。

具体手法如下:

1.延长就绪探测和存活探测的检测周期,并减小其失败阈值 FailureThreshold。

2.(可选)适当缩小优雅关闭的期限,但需要根据应用本身情况定制。如果配置了 PreStop 的生命周期钩子,需要确保其回调时间小于 terminationGracePeriodSeconds 。官方有以下一段描述:

以上两个操作都是以在其收到强行终止信号之前都不会触发以上两种探活为目的预期的。

3.启动探针可以维持高频率的探测,目的是配合快速发布。因为其只在Pod启动时检测,所以检测失败并不会由事件通知出来。

总结

从这个 BUG 我们可以进一步了解容器在销毁的生命周期中发生了什么:

1.API Server 更新 Pod 对象状态为 "Terminating" (正在终止)

2.kubelet 看到 Pod 被标记为正在终止,并开始本地的 Pod 关闭过程

3.Pod 从 Endpoints 对象中被移除后,前面的负载均衡器就会将流量路由到其他(新的)Pod 中去

同时,如果 Pod 中的容器之一定义了 preStop 回调, kubelet 开始在容器内运行该回调逻辑

4.回调在体面终止期限内完成,kubelet 会先给容器中的主进程发 SIGTERM 信号来通知进程进行 shutdown 以实现优雅停止,如果超时进程还未完全停止则会使用 SIGKILL 来强行终止

(或)

如果超出体面终止限期时,preStop 回调逻辑 仍在运行,kubelet 会请求给予该 Pod 的宽限期一次性增加 2 秒钟(摘自官方文档,我的理解是 kubelet 会发送 SIGTERM,并增加 2 秒钟的宽限期,到期后执行 SIGKILL)

从这里我们也可以得出PreStop在实际应用场景中的作用,总的来说,就是优雅地完成进程的清理操作:

1.先从 Endpoint 中将 Pod 剔除,防止边摘流量边终止容器进程,LivenessProbe 与 ReadinessProbe 的设计有异曲同工之妙

2.预留足够的时间让内部程序的调用完成,再给容器主进程发送信号使其优雅的shutdown。

3.除此以外,我们可以在 lifercycle 层的 exec 中配置让服务在注册中心下线的命令,可以通过 curl 本地程序的接口来实现

参考文档(issue 和 pull request 已按照时间线梳理)

liveness/readiness probe is executed and failed while pod is terminated #52817

Automated cherry pick of #98571: Stop probing a pod during graceful shutdown #100525

k8s liveness probe fails during pod termination #107473

Fix:k8s liveness probe fails during pod termination #107514

Pod 的终止

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐