我有k8s集群,在其中使用Helm部署Spring应用程序。
我想设置“宽限期”,让旧容器在终止并被新吊舱替换之前完成其工作。
deployment.yml
terminationGracePeriodSeconds: 600 # ~ 10 minutes
containers:
- name: receiver
lifecycle:
preStop:
exec:
command: ["/bin/sleep","600"]
但是我可以在kubectl get events
中看到奇怪的错误:
3m15s Warning FailedPreStopHook pod/robot-7bd4c6956f-ltbpn Exec lifecycle hook ([/bin/sleep 600]) for Container "receiver" in Pod "robot-7bd4c6956f-ltbpn_rpa-uat(b0d17f4f-4adf-4b8b-a4df-fd84f694b92c)" failed - error: command '/bin/sleep 600' exited with 137: , message: ""
有人知道如何让容器/吊舱等待那600秒吗?
答案 0 :(得分:1)
在k8s docs on lifecycles中,您可以阅读:
PreStop - 在容器终止之前会立即调用此钩子[...]
这意味着当pod终止开始时,在将SIGTERM发送到容器之前,将执行此prestop挂钩。
在启动preStop挂钩的同时,k8s正在启动倒数计时器以等待terminationGracePeriodSeconds
秒,然后才将SIGKILL发送到容器。
请注意,在您的preStop睡眠600秒并且terminationGracePeriodSeconds
也设置为600秒的情况下,可能会导致比赛状态。
在kubernetes源代码中查看this piece of code:
select {
case <-time.After(time.Duration(gracePeriod) * time.Second):
klog.V(2).Infof("preStop hook for container %q did not complete in %d seconds", containerID, gracePeriod)
case <-done:
klog.V(3).Infof("preStop hook for container %q completed", containerID)
}
如您所见,kubelet等待首先发生的一切。
但是,在您的情况下,这两种情况都需要600秒,并且它们在同一时间或多或少地完成比赛,因此可能会导致比赛情况。因此,要么preStop先成功完成,然后倒计时完成,以杀死吊舱,要么倒数计时器首先完成,将SIGKILL发送到容器,以杀死内部运行的所有内容,这也意味着终止preStop,然后以错误Warning FailedPreStopHook
完成preStop。还要注意,exited with 137
意味着该进程被SIGKILL(137-128 = 9,其中9是SIGKILL编号)杀死了k8s。
最重要的是,您的应用程序甚至都不知道它即将终止。
好的,那你该怎么办?如何正确使用preStop?
preStop应该用于通知正在容器中运行的主进程即将终止,并且应该开始为其进行准备,或者用于通知应用集群的其他成员该应用实例将被终止。它也可以按您描述的那样(带睡眠)使用一段时间,以便iptables中的更改有时间传播。您可能还没有意识到,但是在终止过程开始后,k8s正在重新配置网络,因此不会创建新的连接。这就是为什么有时会使用较小的延迟来使k8s有时间传播更改并允许应用在终止之前响应正在进行的请求。
因此,您能做的最好的事情是使您的应用程序了解终止过程,并使其对SIGTERM信号做出适当的响应。 长时间睡眠并不能解决您面临的问题。
此外,这是一些有关springboot的信息,您可能会觉得有用。