admin管理员组

文章数量:1595917

我们在使用 Kubernetes 时遇到了设置 --grace-period 参数不生效的问题,从 kubelet 日志看是 kubelet 接受到 Pod DELETE 事件后在同一秒内又接受到了 REMOVE 事件,所以 Pod 立刻就会删掉了。经过比较曲折的排查最后终于解决了这个问题,下面分享一下 kubernetes grace period 相关的一些概念和理论然后再介绍一下我们踩到的坑。

根据 kubernetes 官方文档《Termination of Pods》这一节的介绍可知 Kubernetes 删除 Pod 时是可以配置 --grace-period 参数的,而且即使没设置这个参数它也有 30 秒的默认值。那么:

  • 这个宽限期到底有什么作用是怎么生效的呢?
  • grace-period 和 docker stop -t 参数有关系吗?
  • 在 Kubernetes 中使用 grace-period 的最佳实践应该怎样的?

在解答这些问题前我们从优雅下线的作用、 Pod 中容器的生命周期 和 Pod 删除的流程说起。

优雅下线有什么用?

我们知道集团的应用都有 online 和 offline 的操作。online 是在应用启动后可能会做一些注册服务或者开启告警之类的操作,offline 是在了停容器前会做关闭告警或者注销服务的操作。所以 online 和 offline 对于一个复杂的分布式集群来说是必不可少的操作。

Container Lifecycle Hooks

Kubernetes 给 Pod 中的 container 添加了两个 hook 点(官方文档看这里):容器启动后和容器停止前。容器启动后正是做 online 的好时机,容器停止前正是做 offline 的好时机。

  • PostStart hook

PostStart 执行的时机是在容器启动以后,但是并不是等容器启动完成再执行。容器启动以后和容器启动完成的区别是什么呢?我们先看一下 Docker 官网的 Entrypoint 和 CMD 的配置可知容器有可以通过 Entrypoint 和 CMD 配置启动指令,如果 Entrypoint 和 CMD 都做了配置那么 CMD 会作为 Entrypoint 的参数由 Entrypoint 来决定如何使用 CMD。但是 Entrypoint 执行之后是不会结束的,如果容器的一号进程结束容器也就退出了。所以在标准的容器玩法中是不知道容器什么时候启动成功的,只知道容器已经启动了。所以 kubelet 是在执行 Entrypoint 之后就会立即执行 PostStart hook,而不是等 Entrypoint 执行完再去执行的。所以理论上来说 PostStart 和 Entrypoint 是并行执行的。

这个 hook 点是执行应用 online 好时机,PostStart 可以探测应用是否启动成功,如果应用启动成功就执行 online 的动作

  • PreStop hook

PreStop 是在 Pod 销毁前 kubelet 对容器执行的指令,可以是到容器中执行一个命令也可以是向容器的某个端口发起一个 HTTP 请求。PreStop 的作用是做一些下线前的准备工作,比如集团的精卫应用再下线前需要从 zk 中注销当前的服务实例。

这个 hook 是执行 offline 的好时机,可以在下线前做一些清理动作。

Termination of Pods

当发起一个删除 Pod 的指令时 Pod 的删除逻辑是这样的:

  1. 调用 kube-apiserver 发起删除 Pod 请求,如果删除 Pod 时没有设置 grace period 参数那么就会使用 30 秒的默认值,否则就会使用用户指定的 grace period 进行优雅下线
  2. kube-apiserver 接受到这个请求以后给相应的 Pod 标记为“删除状态”。其实 Pod 没有“删除状态”,此时 Pod 的 status 还是 Running 状态,所谓的“删除状态”只是 deletionTimestamp 和 deletionGracePeriodSeconds 字段会被设置,这时候 kubelet 或者 kube-proxy 监听到这样的 Pod 就会认为此 Pod 已经不能提供服务了,然后开始做相应的清理操作。
  3. 此时如果通过 Dashbord 查看 Pod 的状态是 Terminating ,其实 Terminating 也不是 Pod status 的字段的值。只是因为设置了 deletionTimestamp 和 deletionGracePeriodSeconds 字段所以 Dashbord 就会把 Pod 标记为 Terminating 状态。这也是 Kubernetes 官方文档中提到的状态
  4. (和第三条同时发生)当 kube-proxy 监听到 Pod 处于 Terminatiing 状态时就把 Pod 从 Service 的 EndPoint 中摘掉,这样对外暴露的服务就摘掉了这个 Pod,防止新的请求发送到这个 Pod 上来
  5. kubelet 监测到 Pod 处于 Terminating 状态的话会下线 Pod,下线的过程分成两个步骤。1. 执行 PreStop 2. 杀死容器。第一步:如果 Pod 设置了 PreStop hook 的话 kubelet 监测到 Pod 处于 Terminating 状态后就会执行 PreStop 操作,执行 PreStop 设置的超时时间和删除 Pod 时指定的 grace period 一致(如果没设置默认是 30 秒)
  6. PreStop 执行完以后还有第二步杀死容器,第二部也有超时时间,这个超时时间是 grace period 减去 PreStop 耗时。如果执行 PreStop 超时或者 grace period 减去 PreStop 耗时剩余的时间不够两秒(甚至可能是负数) kubelet 会强制设置成两秒。第二部的超时时间暂且称之为 tm2, kubelet 停止容器时执行的是 docker stop -t tm2 命令。所以 tm2 的逻辑是:首先发送 term 信号到容器的一号进程,如果容器在 tm2 时间内没有停止就强制发送 kill 信号杀死容器
  7. kubelet 执行完 PreStop 和杀死容器两步以后会回调 kube-apiserver,把 Pod 从 kube-apiserver 中删除,这次的删除是真的删除,这时候通过 API 就再也看不到这个 Pod 的信息了

上面这些过程也可以查看Kubernetes 官方文档,官方文档也有详细的说明。从这里可以判断出来 Pod 下线现在 kube-apiserver 中标记为删除状态,然后 kubelet 执行完正真的删除动作才会真的删除 Pod。

优雅下线时间也可以设置成零,零的意思是立即从 Kubernetes 中删除此 Pod。参数设置方法是:--forc --grace-period=0 这两个参数同时被设置

回答上面的问题

  • 这个宽限期到底有什么作用是怎么生效的呢?

grace-period 的作用是让 kubelet 可以在删掉 Pod 前优雅的下线掉 Pod 中

本文标签: KubernetesgracePeriod