kubernetes 实现优雅退出

247 阅读2分钟

发现问题

对于Kubernetes Deployment的每次部署过程,都是创建新版本的Pod后,再删除老版本的Pod的过程。在这个过程中如果不实现优雅的退出,则会引起两个问题:

  1. 会出现旧的Pod还未将正在处理的请求处理完成就被删除了,如果该请求不是幂等性的,那么就会导致状态不一致的问题。
  2. 会出现旧的Pod已经被删除,kube-proxy仍然将流量导向该Pod,从而出现用户请求处理失败。

分析问题

在Kubernetes delete pod 的过程中会有两个时间线,其中一条时间线是网络规则的更新过程,另一条时间线是pod的删除过程。

当执行 kubectl delete pod 命令时,

网络规则更新:

  • kube-apiserver 收到Pod删除的请求,在ETCD中更新Pod的状态为Terminating;
  • Endpoint Controller将该Pod的IP从Endpoint对象中删除;
  • kube-proxy根据Endpoint对象的改变更新iptables规则,不再将流量路由到被删除的Pod。

Pod删除过程

  • kube-apiserver 会收到Pod删除的请求,在ETCD中更新Pod的状态为Terminating;
  • kubelet 在节点上清理容器的相关资源,例如存储,网络;
  • Kubelet 发送SIGTERM进程给容器,如果容器中的进程未做任何配置,则容器立即退出;
  • 如果容器未在默认的30秒时间内退出,Kubelet发送SIGKILL给容器,强制让容器退出。

从Pod的删除过程可以知道,如果不对容器内的进程进行任何配置,容器会立即退出,会导致问题1出现。

由于网络规则的更新和Pod的删除是并行的,并不能保证网络规则的更新时间一定会早于Pod的删除时间,有可能出现问题2。

解决问题

  • 进程的优雅退出

Springboot-2.3.0开始提供了官方的优雅停机方案,在配置文件中配置优雅停机:

server:
  shutdown: graceful   ## 开启优雅停机
spring:
  lifecycle:
    timeout-per-shutdown-phase: 60s    ## 优雅停机等待时间,默认30s
  • 设置preStopHook

在yaml文件中增加 preStopHook,当kubelet接收到Pod删除事件后sleep一段时间,给Kube-proxy足够的时间去更新iptables网络规则后,再开始删除Pod。

lifecycle: 
  preStop: 
    exec: 
      command: ["sh""-c""sleep 10"
  • 延长terminationGracePeriodSeconds

修改 terminationGracePeriodSeconds使其大于Springboot的优雅退出超时时间和preStopHook sleep时间之和

terminationGracePeriodSeconds: 70