成功最有效的方法就是向有经验的人学习!

Kubernetes 优雅停止 Pod,Pod停止前处理设定任务的最佳方式

何谓优雅停止?
优雅停止(Graceful shutdown)这个说法来自于操作系统,我们执行关机之后都得 OS 先完成一些清理操作,而与之相对的就是硬中止(Hard shutdown),比如拔电源。

到了分布式系统中,优雅停止就不仅仅是单机上进程自己的事了,往往还要与系统中的其它组件打交道。比如说我们起一个微服务,网关把一部分流量分给我们,这时:

假如我们一声不吭直接把进程杀了,那这部分流量就无法得到正确处理,部分用户受到影响。不过还好,通常来说网关或者服务注册中心会和我们的服务保持一个心跳,过了心跳超时之后系统会自动摘除我们的服务,问题也就解决了;这是硬中止,虽然我们整个系统写得不错能够自愈,但还是会产生一些抖动甚至错误;
假如我们先告诉网关或服务注册中心我们要下线,等对方完成服务摘除操作再中止进程,那不会有任何流量受到影响;这是优雅停止,将单个组件的启停对整个系统影响最小化;
按照惯例,SIGKILL 是硬终止的信号,而 SIGTERM 是通知进程优雅退出的信号,因此很多微服务框架会监听 SIGTERM 信号,收到之后去做反注册等清理操作,实现优雅退出.

PreStop Hook
回到 Kubernetes(下称 k8s),当我们想干掉一个 Pod 的时候,理想状况当然是 k8s 从对应的 Service(假如有的话)把这个 Pod 摘掉,同时给 Pod 发 SIGTERM 信号让 Pod 中的各个容器优雅退出就行了。但实际上 Pod 有可能犯各种幺蛾子:

已经卡死了,处理不了优雅退出的代码逻辑或需要很久才能处理完成;
优雅退出的逻辑有 BUG,自己死循环了;
代码写得野,根本不理会 SIGTERM;
因此,k8s 的 Pod 终止流程中还有一个”最多可以容忍的时间”,即 grace period (在 pod 的 .spec.terminationGracePeriodSeconds 字段中定义),这个值默认是 30 秒,我们在执行 kubectl delete 的时候也可通过 --grace-period 参数显式指定一个优雅退出时间来覆盖 pod 中的配置。而当 grace period 超出之后,k8s 就只能选择 SIGKILL 强制干掉 Pod 了.

很多场景下,除了把 Pod 从 k8s 的 Service 上摘下来以及进程内部的优雅退出之外,我们还必须做一些额外的事情,比如说从 k8s 外部的服务注册中心上反注册。这时就要用到 PreStop hook 了,k8s 目前提供了 ExecHTTP 两种 PreStop hook,实际用的时候,需要通过 Pod 的 .spec.containers[].lifecycle.preStop 字段为 Pod 中的每个容器单独配置,比如:

spec:
  contaienrs:
  - name: my-awesome-container
    lifecycle:
      preStop:
        exec:
          command: ["/bin/sh","-c","/pre-stop.sh"]

/pre-stop.sh 脚本里就可以写我们自己的清理逻辑.

我们来总结一下k8s中pod终止过程

在 k8s 中,当一个 pod 被指定要终止时,k8s 会尽量确保 container 可以优雅地按照正常的流程停止,没必要不会直接送 SIGKILL 去清除对应的 process,以下是一个 pod 终止时的流程范例:

使用者送出指令要删除 pod,预设的宽限期(grace period)是 30 秒

超过宽限期后,pod 状态在 API server 会被更新为 dead

接着同步执行以下工作:

3.1 此时若是在 CLI 中列出 pod,状态会显示 terminating
3.2 当 kubelet 发现 pod 被标记为 terminating 状态时(目前尚在宽限期内),开始停止 pod 的流程:
3.2.1 如果在pod中定义了preStop hook,此时会被呼叫;如果preStop hook 在超过宽限期过后依然在运行,第二步会再增加2 秒的宽限期
3.2.2 向 Pod 中的 process 发送 TERM 信号;
3.3 该 Pod 会从 service 的 endpoint list 中移除,replication controller 不会再进行管理;此时若是有终止比较慢的 pod,也不会继续处理 load balancer 转发过来的流量

过了宽限期后,将会向 Pod 中还在运作的 process 发送 SIGKILL 来清除 process。

kublete 会在 API server 中设定 grace period 为 0,表示完成 pod 的删工作已经完成。此时 Pod
已经在 在 API server 中消失,CLI 也无法看见

預設的 grace period 為 30 秒,而使用 kubectl delete 指令可以直接透過 --grace-period= 參數來改變這個預設值,若設定為 0 則是強制刪除 pod。

这个过程很不错,但它存在一个问题就是我们无法预测 Pod 会在多久之内完成优雅退出,也无法优雅地应对”优雅退出”失败的情况。

有状态分布式应用的挑战
为什么说无法接受这个流程呢? 其实这个流程对无状态应用来说通常是 OK 的,但对于有状态的应用很多情况下并不可行,使用kunernetes接管容器化的kubernetes是一个比较常见的场景:

在elasticsearch数据节点中,主分片接受写入请求,并将数据请求同步至副本分片。每个data节点上分布着不同的分片,日常运维中,比如安装插件重启节点(对于容器化的es集群相当于滚动升级),迁移节点难免进行容器重启.

在这个场景下,尽管es集群可以接受小于半数的节点宕机,但前提也是data节点有顺序性的依次重启,我们要尽量做到优雅停止,不然很容器造成分片的主副本同时脱离集群,造成数据丢失。要做到这点,所以我们需要在es实例容器停止前,确保分片进行完全的迁移,而整个所花费的时间随着根据集群规模、数据结构等因素会有较大差异,迁移过程中也难免遇到各种问题而迁移失败。 这时,上面所说的 Pod 退出流程就不再适用了.

另辟蹊径: 解耦 Pod 删除的控制流
复杂的逻辑总是没有简单的逻辑好维护,能不能有一种更简洁,更通用的逻辑,能实现”保证优雅关闭(否则不关闭)“的需求呢?

有,办法就是 ValidatingAdmissionWebhook

这里先介绍一点点背景知识,Kubernetes 的 apiserver 一开始就有 AdmissionController 的设计,这个设计和各类 Web 框架中的 Filter 或 Middleware 很像,就是一个插件化的责任链,责任链中的每个插件针对 apiserver 收到的请求做一些操作或校验。举两个插件的例子:

DefaultStorageClass,为没有声明 storageClass 的 PVC 自动设置 storageClass
ResourceQuota,校验 Pod 的资源使用是否超出了对应 Namespace 的 Quota
虽然说这是插件化的,但在 1.7 之前,所有的 plugin 都需要写到 apiserver 的代码中一起编译,很不灵活。而在 1.7 中 k8s 就引入了 Dynamic Admission Control 机制,允许用户向 apiserver 注册 webhook,而 apiserver 则通过 webhook 调用外部 server 来实现 filter 逻辑。1.9 中,这个特性进一步做了优化,把 webhook 分成了两类: MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook,顾名思义,前者就是操作 api 对象的,比如上文例子中的 DefaultStroageClass,而后者是校验 api 对象的,比如 ResourceQuota。拆分之后,apiserver 就能保证在校验(Validating)之前先做完所有的修改(Mutating),下面这个示意图非常清晰:

而我们的办法就是,利用 ValidatingAdmissionWebhook,在重要的 Pod 收到删除请求时,先在 webhook server 上请求集群进行下线前的清理和准备工作,并直接返回拒绝。这时候重点来了,Control Loop 为了达到目标状态(比如说升级到新版本),会不断地进行 reconcile,尝试删除 Pod,而我们的 webhook 则会不断拒绝,除非集群已经完成了所有的清理和准备工作.

下面是这个流程的分步描述:

用户更新资源对象;

  • controller-manager watch 到对象变更;
  • controller-manager 开始同步对象状态,尝试删除第一个 Pod;
  • apiserver 调用外部 webhook;
  • webhook server 请求集群做 tikv-1
  • 节点下线前的准备工作(这个请求是幂等的),并查询准备工作是否完成,假如准备完成,允许删除,假如没有完成,则拒绝,整个流程会因为controller manager 的控制循环回到第 2 步;
赞(0) 打赏
未经允许不得转载:陈桂林博客 » Kubernetes 优雅停止 Pod,Pod停止前处理设定任务的最佳方式
分享到

大佬们的评论 抢沙发

全新“一站式”建站,高质量、高售后的一条龙服务

微信 抖音 支付宝 百度 头条 快手全平台打通信息流

橙子建站.极速智能建站8折购买虚拟主机

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续给力更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫打赏

微信扫一扫打赏

登录

找回密码

注册