如何防止Kubernetes(例如RabbitMQ)队列工作器Pod在部署期间处理消息时被杀死?

每当为部署设置新的容器映像时,Kubernetes都会替换每个Pod。 默认情况下,这是使用rolling-rollout策略来完成的,其中会先启动带有新的Pod,并逐步杀死旧的Pod。
但是如果一个带有旧镜像的Pod在那个确切的时刻处理一项重要的持久请求或任务会怎样? 是否可以防止它在完成任务之前被终止?图像的Pod在那个确切的时刻处理一项重要的持久请求或任务会怎样? 是否可以防止它在完成任务之前被终止?

Kubernetes提供了生命周期挂钩,通过该钩子可以延迟终止。 开发者需要正确实施此操作。

Pod生命周期
如果Pod在部署期间通过kubectl或任何k8s控制器手动被杀死,它将立即从“运行”变为“终止”状态。同时,SIGTERM信号将发送到该Pod内的所有容器。
处于终止状态的Pod
当Pod处于终止状态时,它仍像以前一样进行调度,并使用相同的资源(CPU /内存)。但是k8s服务不会将任何新请求重定向到终止Pod。处于终止状态时,Pod可以并且应该正确关闭自身。
在处于终止状态时,如果Pod的容器结束,则它们不会重新启动。在运行状态下,只要Pod内的容器停止,容器就会重新启动。这样做是因为Pod应该始终处于运行状态,除非发生错误。
Pod处于“终止”状态的时间最多为“ terminationGracePeriodSeconds”所设置的最大值,默认情况下为30秒。如果所有Pod的容器都以自身终止,则可以提早终止Termination状态。吊舱离开“终止”状态后,便被删除且未计划。
观察到的Pod生命周期Hooks
有可能涉足Pod的生命周期事件。我们可以使用以下简单示例查看这些事件:

新Pod并使用以下命令检查其日志:kubectl logs -f Runner。 然后杀死Pod以查看preStop日志输出。

控制Pod生命周期
我们可以提高Pod在被完全杀死之前处于“终止”状态的时间:

这意味着我们可以简单地将TerminationGracePeriodSeconds设置为一个值,该值使我们的队列工作者有足够的时间完成一项任务,例如5个小时。 这将是最简单的解决方案。
但是,这意味着对于每个部署,只要设置了terminationGracePeriodSeconds,副本的数量就会再次添加到顶部。 这会消耗宝贵的资源。
只要Pod能够处理任务,就可以保持它的生命
这就是我们想要的。 如果我们部署一个新的镜像版本,我们希望所有未做任何事情的Worker Pod都可以立即进行更新,那么其他所有Pod都应该保持存活。
应用程序容器需要监听SIGTERM信号
为此,容器中运行的主进程需要侦听SIGTERM信号并正确终止自身。 当Pod处于状态时,当每个容器都发生这种情况时,即使TerminationGracePeriodSeconds仍处于活动状态,终止Pod也会被提前终止。
让我们来看这个例子:

在上面的示例中,只要文件/ tmp / kill_me不存在,容器就会输出文件/ tmp / messaging的内容。 睡眠10秒钟后,将由preStop生命周期挂钩创建文件/ tmp / kill_me。
现在,我们可以将terminationGracePeriodSeconds设置为更大的数字,并相信我们的应用程序可以正确并尽早结束。
伪代码
这是用于任何本身可能无法处理SIGERM信号的工作进程的示例伪代码:

上面的示例假设/ bin / worker中有一个长时间运行的工作进程,该进程从消息队列接收消息。 此过程接受参数来控制它处理的消息数(–message-count = 1)以及退出前总共等待新消息的时间(–timeout = 1m)。
只要/ bin / worker处理任务,Pod最多可以保持活动状态1小时(3600秒),而保持最少状态。
如果您的工作进程未提供这些参数,并且其本身无法正确处理SIGTERM信号,那么我现在就没主意了。
如果有一个pods正在运行怎么办?
为了使Pod真正从终止状态中被杀死,所有Pod容器的主要进程都需要处理SIGTERM并正确终止。 如果您的pod无法处理SIGTERM,请检查以下示例方法:

在这里sidecar容器是一个长期运行的Python进程。如果主工作容器没有问题,那么该python sidecar容器规范会侦听生命周期并杀死其主进程。
确定是通过创建文件/ tmp / share / kill_sidecar通过共享文件卷进行的。仅在带有工作进程的主容器结束自身之后,才会发生这种情况。
结合HPA /水平pod自动缩放器
应该有可能实现用于部署工作人员Pod的HPA,并使用自定义队列指标来放大和缩小这些指标。例如,这些度量可能来自RabbitMQ,具体取决于那里有多少滞后消息。结合正确定义的终止/生命周期处理,可以很好地发挥作用。
为什么不使用Jobs或临时Pod?
另一种方法可能是为到达队列并需要处理的每条消息增加单独的Pod。作业会创建一个Pod,该Pod并非旨在无限期运行,而只是在其主要任务完成之前。作业模板容器镜像的更改不会影响该作业的任何正在运行的Pod,只会影响新创建的Pod。
参见https://keda.sh/concepts/scaling-jobs
尽管如果您每秒处理数千条消息,这种方法可能会导致很多开销。为了解决这个问题,您可以定义一个Pod在退出之前不仅要处理一个消息,还要处理一定数量的消息。
Kubernetes Operator
一个避免在部署期间杀死您的工作人员/运行人员的好方法是Kubernetes Operator,它可以处理消息的Pod创建。我一直在寻找RabbitMQ的东西,它可以基于来自RabbitMQ等各种来源的事件来创建Kubernetes对象。
Tekon用于流水线还创建了CRD,以启动Pod来完成特定任务。

原文:https://itnext.io/k8s-prevent-queue-worker-pod-from-being-killed-during-deployment-4252ea7c13f6