Node工作负载异常,一部分pod状态为Terminating

2021-10-19 15:33:56 浏览数 (1)

pod状态为Terminating

在节点处于“NotReady”状态时,deployment控制器会迁移节点上的容器实例,并将节点上运行的pod置为“Terminating”状态。待节点恢复后,处于“Terminating”状态的pod会自动删除。偶现部分pod(实例)一直处于“Terminating ”状态,发现这部分的pod没有得到重新调度,不能提供服务。

Terminating不是pod生命周期PodStatus中的phase字段。会不会导致一些问题呢?我们来了解一下pod的生命周期与驱逐相关的概念。

pod的生命周期

Pod对象自从其创建开始至其终止退出的时间范围称为其生命周期。在这段时间中,Pod会处于多种不同的状态,并执行一些操作;其中,创建主容器(main container)为必需的操作,其他可选的操作还包括运行初始化容器(init container)、容器启动后钩子(post start hook)、容器的存活性探测(liveness probe)、就绪性探测(readiness probe)以及容器终止前钩子(pre stop hook)等,这些操作是否执行则取决于Pod的定义。如下图所示:

Pod 的生命周期

Pod的status字段是一个PodStatus的对象,PodStatus中有一个phase字段。无论是手动创建还是通过Deployment等控制器创建,Pod对象总是应该处于其生命进程中以下几个阶段(phase)之一。

  • 挂起(Pending):API Server创建了pod资源对象已存入etcd中,但它尚未被调度完成,或者仍处于从仓库下载镜像的过程中。
  • 运行中(Running):Pod已经被调度至某节点,并且所有容器都已经被kubelet创建完成。
  • 成功(Succeeded):Pod中的所有容器都已经成功终止并且不会被重启
  • 失败(Failed):Pod中的所有容器都已终止了,并且至少有一个容器是因为失败终止。即容器以非0状态退出或者被系统禁止。
  • 未知(Unknown):Api Server无法正常获取到Pod对象的状态信息,通常是由于无法与所在工作节点的kubelet通信所致。

注意:当一个 Pod 被删除时,它会Terminating被一些 kubectl 命令显示为。此Terminating状态不是 Pod 阶段之一。Pod 默认的正常终止的期限,默认为 30 秒。您可以使用该标志--force来强行终止pod。

Pod是kubernetes的基础单元,理解它的创建过程对于了解系统运作大有裨益。如下图描述了一个Pod资源对象的典型创建过程。

  • 用户通过kubectl或其他API客户端提交了Pod Spec给API Server。
  • API Server尝试着将Pod对象的相关信息存入etcd中,待写入操作执行完成,API Server即会返回确认信息至客户端。
  • API Server开始反映etcd中的状态变化。
  • 所有的kubernetes组件均使用“watch”机制来跟踪检查API Server上的相关的变动。
  • kube-scheduler(调度器)通过其“watcher”觉察到API Server创建了新的Pod对象但尚未绑定至任何工作节点。
  • kube-scheduler为Pod对象挑选一个工作节点并将结果信息更新至API Server。
  • 调度结果信息由API Server更新至etcd存储系统,而且API Server也开始反映此Pod对象的调度结果。
  • Pod被调度到的目标工作节点上的kubelet尝试在当前节点上调用Docker启动容器,并将容器的结果状态返回送至API Server。
  • API Server将Pod状态信息存入etcd系统中。
  • 在etcd确认写入操作成功完成后,API Server将确认信息发送至相关的kubelet,事件将通过它被接受。

删除 Pod的逻辑

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

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

Eviction介绍

Eviction,即驱逐的意思,意思是当节点出现异常时,为了保证工作负载的可用性,kubernetes将有相应的机制驱逐该节点上的Pod。目前kubernetes中存在两种eviction机制,分别由kube-controller-managerkubelet实现。

kube-controller-manager实现的eviction

kube-controller-manager主要由多个控制器构成,而eviction的功能主要由node controller这个控制器实现。该Eviction会周期性检查所有节点状态,当节点处于NotReady状态超过一段时间后,驱逐该节点上所有pod。kube-controller-manager提供了以下启动参数控制eviction:

  • pod-eviction-timeout:即当节点宕机该时间间隔后,开始eviction机制,驱赶宕机节点上的Pod,默认为5min。
  • node-eviction-rate:驱赶速率,即驱赶Node的速率,由令牌桶流控算法实现,默认为0.1,即每秒驱赶0.1个节点,注意这里不是驱赶Pod的速率,而是驱赶节点的速率。相当于每隔10s,清空一个节点。
  • secondary-node-eviction-rate:二级驱赶速率,当集群中宕机节点过多时,相应的驱赶速率也降低,默认为0.01。
  • unhealthy-zone-threshold:不健康zone阈值,会影响什么时候开启二级驱赶速率,默认为0.55,即当该zone中节点宕机数目超过55%,而认为该zone不健康。
  • large-cluster-size-threshold:大集群阈值,当该zone的节点多余该阈值时,则认为该zone是一个大集群。大集群节点宕机数目超过55%时,则将驱赶速率降为0.01,假如是小集群,则将速率直接降为0。

由kube-controller-manager触发的驱逐,会留下一个状态为Terminating的pod,想要删除这些状态的 Pod 有三种方法:

  • 从集群中删除该 Node。使用公有云时,kube-controller-manager 会在 VM 删除后自动删除对应的 Node。而在物理机部署的集群中,需要管理员手动删除 Node(如 kubectl delete node。
  • Node 恢复正常。Kubelet 会重新跟 kube-apiserver 通信确认这些 Pod 的期待状态,进而再决定删除或者继续运行这些 Pod。
  • 用户强制删除。用户可以执行 kubectl delete pods--grace-period=0 --force 强制删除 Pod。除非明确知道 Pod 的确处于停止状态(比如 Node 所在 VM 或物理机已经关机),否则不建议使用该方法。特别是 StatefulSet 管理的 Pod,强制删除容易导致脑裂或者数据丢失等问题。

kubelet的eviction机制

如果节点处于资源压力,那么kubelet就会执行驱逐策略。驱逐Pod会考虑优先级,资源使用和资源申请。当优先级相同时,资源使用/资源申请最大的Pod会被首先驱逐。kube-controller-manager的eviction机制是粗粒度的,即驱赶一个节点上的所有pod,而kubelet则是细粒度的,它驱赶的是节点上的某些Pod,驱赶哪些Pod与Pod的Qos机制有关。该Eviction会周期性检查本节点内存、磁盘等资源,当资源不足时,按照优先级驱逐部分pod。驱逐阈值分为软驱逐阈值(Soft Eviction Thresholds)和强制驱逐(Hard Eviction Thresholds)两种机制,如下:kubelet提供了以下参数控制eviction:

  • 软驱逐阈值:当node的内存/磁盘空间达到一定的阈值后,kubelet不会马上回收资源,如果改善到低于阈值就不进行驱逐,若这段时间一直高于阈值就进行驱逐。
  • 强制驱逐:强制驱逐机制则简单的多,一旦达到阈值,直接把pod从本地驱逐。
  • eviction-soft:软驱逐阈值设置,具有一系列阈值,比如memory.available<1.5Gi时,它不会立即执行pod eviction,而会等待eviction-soft-grace-period时间,假如该时间过后,依然还是达到了eviction-soft,则触发一次pod eviction。
  • eviction-soft-grace-period:默认为90秒,当eviction-soft时,终止Pod的grace的时间,即软驱逐宽限期,软驱逐信号与驱逐处理之间的时间差。
  • eviction-max-pod-grace-period:最大驱逐pod宽限期,停止信号与kill之间的时间差。
  • eviction-pressure-transition-period:默认为5分钟,脱离pressure condition的时间,超过阈值时,节点会被设置为memory pressure或者disk pressure,然后开启pod eviction。
  • eviction-minimum-reclaim:表示每一次eviction必须至少回收多少资源。
  • eviction-hard:强制驱逐设置,也具有一系列的阈值,比如memory.available<1Gi,即当节点可用内存低于1Gi时,会立即触发一次pod eviction。

由kubelet触发的驱逐,会留下一个状态为Evicted的pod,此pod只是方便后期定位的记录,可以直接删除。

总结:

偶现部分pod(实例)一直处于“Terminating ”状态,发现这部分的pod没有得到重新调度,不能提供服务。这一类deployment其发布策略是Recreate模式(先删旧POD,再启动新POD)。该问题对于rollout滚动发布的deployment没有影响,仅对recreate的造成影响(类似statefulset也有影响)根据以上描述deployment最好使用rollout滚动发布策略。

部分pod(实例)一直处于“Terminating ”状态,情况分为很多种,这里腾讯云做过一个总结:

《Pod 一直处于 Terminating 状态》。 https://cloud.tencent.com/document/product/457/43238

有兴趣的可以去了解一下。

想要删除这些状态的 Pod 有三种方法:

  • 从集群中删除该 Node。使用公有云时,kube-controller-manager 会在 VM 删除后自动删除对应的 Node。而在物理机部署的集群中,需要管理员手动删除 Node(如 kubectl delete node。
  • Node 恢复正常。Kubelet 会重新跟 kube-apiserver 通信确认这些 Pod 的期待状态,进而再决定删除或者继续运行这些 Pod。
  • 用户强制删除。用户可以执行 kubectl delete pods--grace-period=0 --force 强制删除 Pod。除非明确知道 Pod 的确处于停止状态(比如 Node 所在 VM 或物理机已经关机),否则不建议使用该方法。特别是 StatefulSet 管理的 Pod,强制删除容易导致脑裂或者数据丢失等问题。

参考文章:

https://feisky.gitbooks.io/kubernetes/content/troubleshooting/pod.html

https://v1-20.docs.kubernetes.io/docs/concepts/workloads/pods/#

https://v1-20.docs.kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/

https://cloud.tencent.com/document/product/457/43238

0 人点赞