之前分享过一例集群节点NotReady的问题。在那个问题中,我们的排查路劲,从K8S集群到容器运行时,再到sdbus和systemd,不可谓不复杂。那个问题目前已经在systemd中做了修复,所以基本上能看到那个问题的几率是越来越低了。
但是,集群节点就绪问题还是有的,然而原因却有所不同。
今天这篇文章,跟大家分享另外一例集群节点NotReady的问题。这个问题和之前那个问题相比,排查路劲完全不同。作为姊妹篇分享给大家。
问题现象
这个问题的现象,也是集群节点会变成NotReady状态。问题可以通过重启节点暂时解决,但是在经过大概20天左右之后,问题会再次出现。
问题出现之后,如果我们重启节点上kubelet,则节点会变成Ready状态,但这种状态只会持续三分钟。这是一个特别的情况。
大逻辑
在具体分析这个问题之前,我们先来看一下集群节点就绪状态背后的大逻辑。K8S集群中,与节点就绪状态有关的组件,主要有四个,分别是集群的核心数据库etcd,集群的入口API Server,节点控制器以及驻守在集群节点上,直接管理节点的kubelet。
一方面,kubelet扮演的是集群控制器的角色,它定期从API Server获取Pod等相关资源的信息,并依照这些信息,控制运行在节点上Pod的执行;另外一方面,kubelet作为节点状况的监视器,它获取节点信息,并以集群客户端的角色,把这些状况同步到API Server。
在这个问题中,kubelet扮演的是第二种角色。
Kubelet会使用上图中的NodeStatus机制,定期检查集群节点状况,并把节点状况同步到API Server。而NodeStatus判断节点就绪状况的一个主要依据,就是PLEG。
PLEG是Pod Lifecycle Events Generator的缩写,基本上它的执行逻辑,是定期检查节点上Pod运行情况,如果发现感兴趣的变化,PLEG就会把这种变化包装成Event发送给Kubelet的主同步机制syncLoop去处理。但是,在PLEG的Pod检查机制不能定期执行的时候,NodeStatus机制就会认为,这个节点的状况是不对的,从而把这种状况同步到API Server。
而最终把kubelet上报的节点状况,落实到节点状态的是节点控制这个组件。这里我故意区分了kubelet上报的节点状况,和节点的最终状态。因为前者,其实是我们describe node时看到的Condition,而后者是真正节点列表里的NotReady状态。
就绪三分钟
在问题发生之后,我们重启kubelet,节点三分钟之后才会变成NotReady状态。这个现象是问题的一个关键切入点。
在解释它之前,请大家看一下官方这张PLEG示意图。这个图片主要展示了两个过程。一方面,kubelet作为集群控制器,从API Server处获取pod spec changes,然后通过创建worker线程来创建或结束掉pod;另外一方面,PLEG定期检查容器状态,然后把状态,以事件的形式反馈给kubelet。
在这里,PLEG有两个关键的时间参数,一个是检查的执行间隔,另外一个是检查的超时时间。以默认情况为准,PLEG检查会间隔一秒,换句话说,每一次检查过程执行之后,PLEG会等待一秒钟,然后进行下一次检查;而每一次检查的超时时间是三分钟,如果一次PLEG检查操作不能在三分钟内完成,那么这个状况,会被上一节提到的NodeStatus机制,当做集群节点NotReady的凭据,同步给API Server。
而我们之所以观察到节点会在重启kubelet之后就绪三分钟,是因为kubelet重启之后,第一次PLEG检查操作就没有顺利结束。节点就绪状态,直到三分钟超时之后,才被同