问题景象
在TKE管制台上新建版本为v1.18.4(具体版本号 < v1.18.4-tke.5)的独立集群,其中,集群的节点信息如下:
有3个master node和1个worker node,并且worker 和 master在不同的可用区。
node | 角色 | label信息 |
---|---|---|
ss-stg-ma-01 | master | label[failure-domain.beta.kubernetes.io/region=sh,failure-domain.beta.kubernetes.io/zone=200002] |
ss-stg-ma-02 | master | label[failure-domain.beta.kubernetes.io/region=sh,failure-domain.beta.kubernetes.io/zone=200002] |
ss-stg-ma-03 | master | label[failure-domain.beta.kubernetes.io/region=sh,failure-domain.beta.kubernetes.io/zone=200002] |
ss-stg-test-01 | worker | label[failure-domain.beta.kubernetes.io/region=sh,failure-domain.beta.kubernetes.io/zone=200004] |
待集群创立好之后,再创立出一个daemonset对象,会呈现daemonset的某个pod始终卡住pending状态的景象。
景象如下:
$ kubectl get pod -o wideNAME READY STATUS RESTARTS AGE NODE debug-4m8lc 1/1 Running 1 89m ss-stg-ma-01debug-dn47c 0/1 Pending 0 89m <none>debug-lkmfs 1/1 Running 1 89m ss-stg-ma-02debug-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)
,尝试获取一些相干日志。
当日志级别调大之后,有抓取到一些要害信息,信息如下:
解释一下,当调度某个pod时,有可能会进入到调度器的抢占preempt
环节,而下面的日志就是出自于抢占环节。
集群中有4个节点(3个master node和1个worker node),然而日志中只显示了3个节点,短少了一个master节点。
所以,这里临时狐疑下是调度器外部缓存cache中少了node info
。
2、获取调度器外部cache信息
k8s v1.18曾经反对打印调度器外部的缓存cache信息。打印进去的调度器外部缓存cache信息如下:
能够看出,调度器的外部缓存cache中的node info
是残缺的(3个master node和1个worker node)。
通过剖析日志,能够失去一个初步论断:调度器外部缓存cache中的node info
是残缺的,然而当调度pod时,缓存cache中又会短少局部node信息。
问题根因
在进一步剖析之前,咱们先一起再相熟下调度器调度pod的流程(局部展现)和nodeTree数据结构。
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数据结构
(不便了解,本文应用了链表来展现)
在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.
2、 zoneIndex=2 >= len(zones); zoneIndex=0, nodeArray[sh:200002].lastIndex=3, return.
3、 zoneIndex=1, nodeArray[sh:200004].lastIndex=1, return.
4、 zoneIndex=0, nodeArray[sh:200002].lastIndex=0, we get ss-stg-ma-01.
5、 zoneIndex=1, nodeArray[sh:200004].lastIndex=0, we get ss-stg-test-01.
6、 zoneIndex=2 >= len(zones); zoneIndex=0, nodeArray[sh:200002].lastIndex=1, we get ss-stg-ma-02.
同步实现之后,调度器的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)。
相干issue:https://github.com/kubernetes...
相干pr(k8s v1.18): https://github.com/kubernetes...
TKE修复版本:v1.18.4-tke.5
【腾讯云原生】云说新品、云研新术、云游新活、云赏资讯,扫码关注同名公众号,及时获取更多干货!!