如何排查K8s Scheduler在调度pod过程中遗漏部分节点的问题

86次阅读
没有评论

共计 4126 个字符,预计需要花费 11 分钟才能阅读完成。

本篇内容主要讲解“如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让丸趣 TV 小编来带大家学习“如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题”吧!

问题现象

在 TKE 控制台上新建版本为 v1.18.4(详细版本号 v1.18.4-tke.5)的独立集群,其中,集群的节点信息如下:

有 3 个 master node 和 1 个 worker node,并且 worker 和 master 在不同的可用区。

node 角色 label 信息 ss-stg-ma-01masterlabel[failure-domain.beta.kubernetes.io/region=sh,failure-domain.beta.kubernetes.io/zone=200002]ss-stg-ma-02masterlabel[failure-domain.beta.kubernetes.io/region=sh,failure-domain.beta.kubernetes.io/zone=200002]ss-stg-ma-03masterlabel[failure-domain.beta.kubernetes.io/region=sh,failure-domain.beta.kubernetes.io/zone=200002]ss-stg-test-01workerlabel[failure-domain.beta.kubernetes.io/region=sh,failure-domain.beta.kubernetes.io/zone=200004]

待集群创建好之后,再创建出一个 daemonset 对象,会出现 daemonset 的某个 pod 一直卡住 pending 状态的现象。现象如下:

$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE NODE 
debug-4m8lc 1/1 Running 1 89m ss-stg-ma-01
debug-dn47c 0/1 Pending 0 89m  none 
debug-lkmfs 1/1 Running 1 89m ss-stg-ma-02
debug-qwdbc 1/1 Running 1 89m ss-stg-test-01

(补充:TKE 当前支持的最新版本号为 v1.18.4-tke.8,新建集群默认使用最新版本)

问题结论

k8s 的调度器在调度某个 pod 时,会从调度器的内部 cache 中同步一份快照(snapshot),其中保存了 pod 可以调度的 node 信息。上面问题(daemonset 的某个 pod 实例卡在 pending 状态)的原因就是同步的过程发生了部分 node 信息丢失,导致了 daemonset 的部分 pod 实例无法调度到指定的节点上,卡在了 pending 状态。

接下来是详细的排查过程。

日志排查

截图中出现的节点信息(来自客户线上集群):k8s master 节点:ss-stg-ma-01、ss-stg-ma-02、ss-stg-ma-03 k8s worker 节点:ss-stg-test-01

1、获取调度器的日志 这里首先是通过动态调大调度器的日志级别,比如,直接调大到 V(10),尝试获取一些相关日志。当日志级别调大之后,有抓取到一些关键信息,信息如下:如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题

解释一下,当调度某个 pod 时,有可能会进入到调度器的抢占 preempt 环节,而上面的日志就是出自于抢占环节。集群中有 4 个节点(3 个 master node 和 1 个 worker node),但是日志中只显示了 3 个节点,缺少了一个 master 节点。所以,这里暂时怀疑下是调度器内部缓存 cache 中少了 node info。

2、获取调度器内部 cache 信息 k8s v1.18 已经支持打印调度器内部的缓存 cache 信息。打印出来的调度器内部缓存 cache 信息如下:如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题

如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题

如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题

如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题

可以看出,调度器的内部缓存 cache 中的 node info 是完整的(3 个 master node 和 1 个 worker node)。通过分析日志,可以得到一个初步结论:调度器内部缓存 cache 中的 node info 是完整的,但是当调度 pod 时,缓存 cache 中又会缺少部分 node 信息。

问题根因

在进一步分析之前,我们先一起再熟悉下调度器调度 pod 的流程(部分展示)和 nodeTree 数据结构。

pod 调度流程(部分展示)

如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题

结合上图,一次 pod 的调度过程就是 一次 Scheduler Cycle。在这个 Cycle 开始时,第一步就是 update snapshot。snapshot 我们可以理解为 cycle 内的 cache,其中保存了 pod 调度时所需的 node info,而 update snapshot,就是一次 nodeTree(调度器内部 cache 中保存的 node 信息)到 snapshot 的同步过程。而同步过程主要是通过 nodeTree.next() 函数来实现,函数逻辑如下:

// next returns the name of the next node. NodeTree iterates over zones and in each zone iterates
// over nodes in a round robin fashion.
func (nt *nodeTree) next() string {if len(nt.zones) == 0 {
 return  
 numExhaustedZones := 0
 for {if nt.zoneIndex  = len(nt.zones) {
 nt.zoneIndex = 0
 zone := nt.zones[nt.zoneIndex]
 nt.zoneIndex++
 // We do not check the exhausted zones before calling next() on the zone. This ensures
 // that if more nodes are added to a zone after it is exhausted, we iterate over the new nodes.
 nodeName, exhausted := nt.tree[zone].next()
 if exhausted {
 numExhaustedZones++
 if numExhaustedZones  = len(nt.zones) { // all zones are exhausted. we should reset.
 nt.resetExhausted()} else {return nodeName}

再结合上面排查过程得出的结论,我们可以再进一步缩小问题范围:nodeTree(调度器内部 cache)到的同步过程丢失了某个节点信息。

### nodeTree 数据结构(方便理解,本文使用了链表来展示)

如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题

在 nodeTree 数据结构中,有两个游标 zoneIndex 和 lastIndex(zone 级别),用来控制 nodeTree(调度器内部 cache)到 snapshot.nodeInfoList 的同步过程。并且,重要的一点是:上次同步后的游标值会被记录下来,用于下次同步过程的初始值。

### 重现问题,定位根因

创建 k8s 集群时,会先加入 master node,然后再加入 worker node(意思是 worker node 时间上会晚于 master node 加入集群的时间)。

第一轮同步:3 台 master node 创建好,然后发生 pod 调度(比如,cni 插件,以 daemonset 的方式部署在集群中),会触发一次 nodeTree(调度器内部 cache)到的同步。同步之后,nodeTree 的两个游标就变成了如下结果:

nodeTree.zoneIndex = 1,
nodeTree.nodeArray[sh:200002].lastIndex = 3,

第二轮同步:当 worker node 加入集群中后,然后新建一个 daemonset,就会触发第二轮的同步(nodeTree(调度器内部 cache)到的同步)。同步过程如下:

1、zoneIndex=1, nodeArray[sh:200004].lastIndex=0, we get ss-stg-test-01.

如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题

2、zoneIndex=2 = len(zones); zoneIndex=0, nodeArray[sh:200002].lastIndex=3, return.

如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题

3、zoneIndex=1, nodeArray[sh:200004].lastIndex=1, return.

如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题

4、zoneIndex=0, nodeArray[sh:200002].lastIndex=0, we get ss-stg-ma-01.

如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题

5、zoneIndex=1, nodeArray[sh:200004].lastIndex=0, we get ss-stg-test-01.

如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题

6、zoneIndex=2 = len(zones); zoneIndex=0, nodeArray[sh:200002].lastIndex=1, we get ss-stg-ma-02.

如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题

同步完成之后,调度器的 snapshot.nodeInfoList 得到如下的结果:

[
 ss-stg-test-01,
 ss-stg-ma-01,
 ss-stg-test-01,
 ss-stg-ma-02,
]

ss-stg-ma-03 去哪了?在第二轮同步的过程中丢了。

解决方案

从问题根因的分析中,可以看出,导致问题发生的原因,在于 nodeTree 数据结构中的游标 zoneIndex 和 lastIndex(zone 级别)值被保留了,所以,解决的方案就是在每次同步 SYNC 时,强制重置游标(归 0)。 

到此,相信大家对“如何排查 K8s Scheduler 在调度 pod 过程中遗漏部分节点的问题”有了更深的了解,不妨来实际操作一番吧!这里是丸趣 TV 网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-25发表,共计4126字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)