当前位置: 首页 > 科技观察

Node工作负载异常,部分Pod处于Terminating

时间:2023-03-13 20:51:43 科技观察

状态本文转载请联系运维开发故事公众号。当pod状态为Terminating且节点处于“NotReady”状态时,Deploymentcontroller会迁移节点上的容器实例,并将运行在该节点上的pod设置为“Terminating”状态。节点恢复后,处于“Terminating”状态的Pod将被自动删除。偶尔会出现一些pod(实例)一直处于“Terminating”状态,发现这些pod没有重新调度,无法提供服务。Terminating不是pod生命周期的PodStatus中的阶段字段。会不会引起一些问题?让我们了解与Pod生命周期和驱逐相关的概念。Pod生命周期Pod对象从创建到终止的时间范围称为它的生命周期。在这段时间里,Pod会处于各种不同的状态,并进行一些操作;其中,创建主容器(maincontainer)是必须的操作,其他可选操作包括运行初始化容器(initcontainer)、容器poststarthook、containerlivenessprobe、readinessprobe、containerprestophook等。这些操作的执行取决于Pod的定义。如下图所示:Pod生命周期Pod的status字段是一个PodStatus对象,PodStatus中有一个phase字段。无论是手动创建还是通过Deployment等控制器创建,Pod对象在其生命过程中都应始终处于以下阶段之一。Pending:APIServer已经创建了pod资源对象并存储在etcd中,但是还没有被调度,或者还在从仓库下载镜像中。运行中(Running):Pod已经被调度到一个节点上,所有的容器都已经被kubelet创建。成功(Succeeded):Pod中所有容器都已成功终止,不会重启失败(Failed):Pod中所有容器均已终止,至少有一个容器因失败而终止。即容器以非零状态退出或被系统禁止。未知(Unknown):ApiServer无法正常获取Pod对象的状态信息,通常是因为无法与工作节点的kubelet通信。注意:当一个Pod被删除时,它会被一些kubectl命令显示为Terminating。此终止状态不是Pod阶段之一。Pod正常终止的默认时间段,默认为30秒。您可以使用标志--force强制终止pod。Pod是kubernetes的基本单元,了解它的创建过程对于理解系统的运行有很大的帮助。下图描述了一个典型的Pod资源对象的创建过程。用户通过kubectl或其他API客户端将PodSpec提交给APIServer。APIServer尝试将Pod对象的相关信息存储在etcd中。写操作完成后,APIServer会向客户端返回确认信息。APIServer开始反映etcd中的状态变化。所有的kubernetes组件都使用“watch”机制来跟踪和检查APIServer上的相关变化。kube-scheduler(调度器)通过它的“watcher”知道APIServer已经创建了一个新的Pod对象,但还没有绑定到任何工作节点。kube-scheduler为Pod对象选择一个worker节点,并将结果信息更新到APIServer。调度结果信息由APIServer更新到etcd存储系统,APIServer也开始反映这个Pod对象的调度结果。调度Pod的目标worker节点上的kubelet尝试调用Docker启动当前节点上的容器,并将容器的结果状态返回给APIServer。APIServer将Pod状态信息存储在etcd系统中。在etcd确认写操作成功完成后,APIServer将确认发送到相关的kubelet,通过它接收事件。Pod删除逻辑当发起删除Pod的命令时,Pod删除逻辑如下:调用kube-apiserver发起Pod删除请求。如果删除Pod时没有设置graceperiod参数,则使用默认值30秒,否则将使用用户指定的graceperiod优雅下线。kube-apiserver收到这个请求后,会将对应的Pod标记为“已删除状态”。实际上,Pod并没有“删除状态”。此时Pod的状态还是处于Running状态。所谓“删除状态”,就是会设置deletionTimestamp和deletionGracePeriodSeconds字段。这时候kubelet或者kube-proxy在监控这样一个Pod的时候,就会认为这个Pod已经被删除了。无法提供服务,然后开始做相应的清理操作。这时候如果通过Dashbord查看Pod状态为Terminating,其实Terminating并不是Podstatus字段的值。仅仅因为设置了deletionTimestamp和deletionGracePeriodSeconds字段,Dashbord会将Pod标记为Terminating。(与第3条同时发生)当kube-proxy检测到Pod处于Terminating状态时,将Pod从Service的EndPoint中移除,这样对外暴露的service移除Pod,防止新的请求被发送到Pod当kubelet启动并检测到Pod处于Terminating状态时,它将使Pod离线。离线过程分为两个步骤。1.执行PreStop2.杀死容器。第一步:如果Pod设置了PreStophook,kubelet会在检测到Pod处于Terminating状态后执行PreStop操作。PreStop设置的超时时间和删除Pod时指定的宽限期一致(如果不设置,默认30秒)period,这是宽限期减去PreStop时间。如果PreStop超时或宽限期减去PreStop的执行时间少于两秒(甚至是负数),kubelet将被强制设置为两秒。第二部分的超时时间暂且称为tm2。当kubelet停止容器时,它会执行dockerstop-ttm2命令。所以tm2的逻辑是:先给容器的第一个进程发送一个term信号,如果在tm2时间内容器没有停止,就会强行发送一个kill信号杀死容器,kubelet会回调kube-apiserver在执行PreStop和kill容器这两个步骤之后。从kube-apiserver中删除Pod。这次删除是真正的删除。这时候就不能再通过API看到Pod的信息了。Eviction引入了Eviction,即驱逐的意思,意思是当节点出现异常时,为了保证工作负载的可用性,kubernetes会有相应的机制驱逐节点上的Pod。目前kubernetes中有两种驱逐机制,分别由kube-controller-manager和kubelet实现。kube-controller-manager实现的evictionkube-controller-manager主要由多个controller组成,eviction的功能主要由nodecontroller实现。Eviction会周期性的检查所有节点的状态,当节点处于NotReady状态一段时间后,该节点上的所有pod都会被驱逐。kube-controller-manager提供了以下启动参数来控制驱逐:pod-eviction-timeout:当节点宕机一定时间后,驱逐机制开始驱逐宕机节点上的pod。默认值为5分钟。node-eviction-rate:逐出率,即Node的逐出率,由令牌桶流控算法实现。默认为0.1,即每秒驱逐0.1个节点。请注意,这不是Pod的驱逐率,而是节点的驱逐率。相当于每10s清空一个节点。secondary-node-eviction-rate:二次驱逐率。当集群中宕机节点过多时,相应的驱逐率也会降低。默认值为0.01。unhealthy-zone-threshold:不健康区阈值,会影响何时开始秒级驱逐率,默认0.55,即当区内节点数下降超过55%时,认为该区不良。large-cluster-size-threshold:大集群阈值,当zone中的节点数超过该阈值时,认为该zone是一个大集群。当大型集群节点宕机次数超过55%时,驱动率会降为0.01,如果是小型集群,则直接降为0。kube-controller-manager触发的驱逐会离开处于Terminating状态的pod。可以通过三种方式删除处于这些状态的Pod:从集群中删除节点。使用公有云时,kube-controller-manager会在删除VM后自动删除对应的Node。在部署在物理机上的集群中,管理员需要手动删除Node(比如kubectldeletenode。Node恢复正常。Kubelet会再次与kube-apiserver通信,确认这些Pod的预期状态,然后决定删除或者继续运行这些Pod。用户强制删除。用户可以执行kubectldeletepods--grace-period=0--force强制删除Pod。除非明确Pod确实处于停止状态(对于例如Node所在的VM或物理机已经关闭),不推荐这种方式。特别是StatefulSet管理的Pod,强行删除很容易导致脑裂或者数据丢失等问题。如果kubelet的驱逐机制受到资源压力,kubelet将执行驱逐策略。驱逐Pod会考虑优先级、资源使用情况和资源请求。当优先级相同时,资源使用/资源申请量最大的Pod最先被驱逐。kube-controller-manager的驱逐机制是粗粒度的,即驱动一个节点上的所有pod,而kubelet是细粒度的,驱动节点上的部分pod,驱动哪些pod与Qos机制有关豆荚的。Eviction会定期检查节点的内存、磁盘等资源。当资源不足时,会根据优先级驱逐部分pod。驱逐阈值分为软驱逐阈值(SoftEvictionThresholds)和强制驱逐阈值(HardEvictionThresholds)两种机制,如下:kubelet提供以下参数来控制驱逐:软驱逐阈值:当节点的内存/磁盘空间不足时达到一定阈值,kubelet不会立即回收资源。如果改进低于阈值,则不会被驱逐。如果这段时间一直高于阈值,就会被逐出。强制驱逐:强制驱逐机制要简单得多。一旦达到阈值,pod将被直接驱逐出本地区域。eviction-soft:软驱逐阈值设置,有一系列阈值,例如当memory.available<1.5Gi时,不会立即执行pod驱逐,而是等待eviction-soft-grace-period时间,如果时间到了通过,仍然是eviction-soft到达时,触发pod驱逐。eviction-soft-grace-period:默认为90秒。eviction-soft时,终止Pod的宽限时间为软驱逐宽限期,软驱逐信号与驱逐过程的时间差。eviction-max-pod-grace-period:最大驱逐pod宽限期,停止信号和kill之间的时间差。eviction-pressure-transition-period:默认为5分钟,离开压力状态的时间。当超过阈值时,节点将被设置为内存压力或磁盘压力,然后启用Pod驱逐。eviction-minimum-reclaim:表示每次驱逐至少要回收多少资源。eviction-hard:强制驱逐设置,也有一系列的阈值,比如memory.available<1Gi,即当节点的可用内存低于1Gi时,会立即触发一次pod驱逐。由kubelet触发的驱逐将使pod处于Evicted状态。这个pod只是为了以后定位的记录,可以直接删除。总结:偶尔会有一些pod(实例)一直处于“Terminating”状态,发现这些pod没有重新调度,无法提供服务。此类部署的发布策略为Recreate模式(先删除旧的POD,再启动新的POD)。这个问题对rolloutrollout部署没有影响,只对recreate有影响(类似statefulset)。根据上面的描述,最好使用rolloutrollout策略进行部署。某些pod(实例)始终处于“终止”状态。有很多情况。下面是腾讯云的总结:《Pod 一直处于 Terminating 状态》。https://cloud.tencent.com/document/product/457/43238有兴趣的可以去看看。可以通过三种方式移除处于这些状态的Pod:从集群中移除节点。使用公有云时,kube-controller-manager会在删除VM后自动删除对应的Node。在部署在物理机上的集群中,管理员需要手动删除Node(比如kubectldeletenode。Node恢复正常。Kubelet会再次与kube-apiserver通信,确认这些Pod的预期状态,然后决定删除或者继续运行这些Pod。用户强制删除。用户可以执行kubectldeletepods--grace-period=0--force强制删除Pod。除非明确Pod确实处于停止状态(对于例如Node所在的VM或物理机已经关闭),不推荐这种方式。特别是StatefulSet管理的Pod,强行删除很容易导致脑裂或者数据丢失等问题。参考文章:https://feisky.gitbooks.io/kubernetes/content/troubleshooting/pod.htmlhttps://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