一、k8s删除Pod的过程
在删除pod 的过程中,有两条平行的时间线。一是改变网络规则的时间线,另一个是 pod 的删除。
网络规则生效
kube-apiserver 接收到 pod 删除请求,将 pod 在 Etcd 中的状态更新为 Terminating;
Endpoint Controller 从 Endpoint 对象中删除 pod 的 IP;
kuber-proxy 根据 Endpoint 对象的变化更新 iptables 的规则,不再将流量路由到被删除的 Pod。
删除 pod
kube-apiserver 接收到 Pod 删除请求,将 Pod 的在 Etcd 中的状态更新为 Terminating
preStop 钩子被执行
Kubelet 向容器发送 SIGTERM
继续等待,直到容器停止,或者超时 spec.terminationGracePeriodSeconds,这个值默认为 30s
如果超过了 spec.terminationGracePeriodSeconds 容器仍然没有停止,k8s 将会发送 SIGKILL 信号给容器
进程全部终止后,整个 Pod 完全被清理掉
注意:这个优雅退出的等待计时spec.terminationGracePeriodSeconds是与 preStop 同步开始的,而且它也不会等待 preStop 结束
二、可能遇到的问题
502
应用程序在收到 SIGTERM
信号后直接终止了运行,导致部分还没有被处理完的请求直接中断,代理层返回 502
504
Service Endpoints 移除不够及时,在 Pod 已经被终止后,仍然有个别请求被路由到了该 Pod,得不到响应导致 504
三、如何避免上述问题
为容器内的进程设置正常关闭
以 SpringBoot 为例,启用优雅关闭可以 Spring Boot 配置文件中添加下面设置
server: shutdown: gracefulspring: lifecycle: timeout-per-shutdown-phase: 30s
可以解决502的问题
添加 preStopHook
当kube-apiserver 接收到 pod 删除请求后,必须要预留一段时间,来等待网络规则的更新,避免新的流量路由到一个不可用的pod上 。
因此,应该让 Kubelet 在收到删除 pod 事件时“sleep 一下”,并在给Pod发送SIGTERM之前留出足够的时间来更新网络规则。
containers: - name: my-app # 添加下面这部分 lifecycle: preStop: exec: command: - /bin/sh - -c - "sleep 10"
可以解决504的问题
修改终止 GracePeriodSeconds
Kubernetes 为容器删除留下了 30 秒的最大时间尺度。如果 Spring 的优雅关闭超时时间和 Kubernetes 的 preStopHooks 之和超过 30 秒,可能会导致 Kubernetes 在 Spring Boot 处理完请求之前强行删除容器。因此,如果过程超过 30 秒,则应将 timerminationGracePeriodSeconds 调整为大于30秒
下图显示了设置后的时间线:
四、制作一个可以优雅关闭的镜像
参考文章:docker制作java镜像
五、参考文章
K8s里Spring 微服务项目,Pod 关闭对用户的影响比较大!
在 Kubernetes 容器集群,微服务项目最佳实践