专栏首页K8S疑难杂症Pod疑难杂症(2):K8S Scheduler在调度pod过程中遗漏部分节点
原创

Pod疑难杂症(2):K8S Scheduler在调度pod过程中遗漏部分节点

问题背景

新建一个如下的k8s集群,有3个master node和1个worker node(worker 和 master在不同的可用区),node信息如下:

node

label信息

master-01

label[failure-domain.beta.kubernetes.io/region=sh,failure-domain.beta.kubernetes.io/zone=200002]

master-02

label[failure-domain.beta.kubernetes.io/region=sh,failure-domain.beta.kubernetes.io/zone=200002]

master-03

label[failure-domain.beta.kubernetes.io/region=sh,failure-domain.beta.kubernetes.io/zone=200002]

worker-node-01

label[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  master-01
debug-dn47c 0/1   Pending 0        89m  <none>
debug-lkmfs 1/1   Running 1        89m   master-02
debug-qwdbc 1/1   Running 1        89m  worker-node-01

问题结论

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),尝试获取一些相关日志。 当日志级别调大之后,有抓取到一些关键信息,信息如下:

error log01

解释一下,当调度某个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调度流程(部分展示)

scheduler cycle

结合上图,一次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)到snapshot.nodeInfoList的同步过程丢失了某个节点信息。

nodeTree数据结构

(方便理解,本文使用了链表来展示)

nodeTree

重现问题,定位根因

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

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

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

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

同步过程如下:

1、 zoneIndex=1, nodeArray[sh:200004].lastIndex=0, we get worker-node-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 master-01.

5、 zoneIndex=1, nodeArray[sh:200004].lastIndex=0, we get worker-node-01.

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

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

[
    worker-node-01,
    master-01,
    worker-node-01,
    master-02,
]

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

解决方案

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

相关issue:https://github.com/kubernetes/kubernetes/issues/97120 相关pr(k8s v1.18): https://github.com/kubernetes/kubernetes/pull/93387

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Kube-scheduler 源码分析(一):调度器设计

    我们先整体了解一下 Scheduler 的设计原理,然后再看这些过程是如何用代码实现的。关于调度器的设计在官网有介绍,我下面结合官网给的说明,简化掉不影响理解的...

    米开朗基杨
  • 云巢揭秘:数据库产品 PaaS On IaaS 实践分享

    作者:kevinyfsun(孙勇福)  腾讯CSIG云产品研发工程师 导语| 随着2B行业的蓬勃发展,数据库产品中心原有烟囱式发展模式,在资源调度和统一管控上...

    腾讯大讲堂
  • 容器 & 服务:K8s 与 Docker 应用集群 (二)

    容器 & 服务:K8s 与 Docker 应用集群 (一)中,我们通过解决之前的一个遗留问题,初步了解了k8s的一些基础命令,做了一个应用部署。本篇将继续介绍k...

    程序员架构进阶
  • 容器 & 服务:K8s 与 Docker 应用集群 (二)

    容器 & 服务:K8s 与 Docker 应用集群 (一)中,我们通过解决之前的一个遗留问题,初步了解了k8s的一些基础命令,做了一个应用部署。本篇将继续介绍k...

    程序员架构进阶
  • 基于Win10单机部署kubernetes应用

    鸽了好久了,终于又一次克服了拖延症,决心写点啥,起因也是因为最近刚好重做了系统,把win10从home版升级到了专业版,可以愉快的安装docker destop...

    麒思妙想
  • vivo AI 计算平台的K8s填坑指南

    在 2018 年底,vivo AI 研究院为了解决统一的高性能训练环境、大规模的分布式训练、计算资源的高效利用调度等痛点,着手建设 AI 计算平台。白驹过隙,将...

    深度学习与Python
  • 开工必备!50+篇超实用云原生技术干货合集

    kai 开 gong 工 da 大 ji 吉 新年新气象,更要1G棒 2020年没写完的代码,现在还有思路吗? 2021年开始使用云原生技术了吗? 一开工就遇...

    腾讯云原生
  • 白话K8S核心组件概念

    容器是利用了集装箱的思想,把可运行程序打包成可运行、自包含、标准化的镜像。通过K8S能够管理和编排我们打的镜像,举例来说,如果你想运行两个副本,直接在编排文件中...

    用户5166556
  • 看焱融云CSI动态感知如何扩展Kubernetes Scheduler

    Kubernetes Scheduler 的作用是将待调度的 Pod 按照一定的调度算法和策略绑定到集群中一个合适的 Worker Node(以下简称 Node...

    焱融科技
  • 转发有礼 | 50篇+云原生系列干货文章汇总,请查收!

    云原生技术干货文章合集,来咯~ ? 2020 年,要说咱们技术圈子里什么最火? 云原生肯定是那 NO.1 ? 截止目前,我们不难看出,K8s 容器、服务...

    腾讯云原生
  • 这应该是最全的K8s-Pod调度策略了

    API Server接受客户端提交Pod对象创建请求后的操作过程中,有一个重要的步骤就是由调度器程序kube-scheduler从当前集群中选择一个可用的最佳节...

    小小科
  • K8s 调度系统由浅入深系列:简介

    从CNCF基金会的成立,到Kubernetes社区蓬勃发展,历经6载,17年异军突起,在mesos、swarm等项目角逐中,拔得头筹,继而一统容器编排,其成功的...

    CNCF
  • Kubernetes 之集群调度

    我们现在有这样一个需求,就是集群中多台服务的配置是不一致的。这就导致资源分配并不是均匀的,比如我们需要有些服务节点用来运行计算密集型的服务,而有些服务节点来运行...

    民工哥
  • 带着问题学 Kubernetes 架构!

    打开这篇文章的同学,想必对 docker 都不会陌生。docker 是一种虚拟容器技术,它上手比较简单,只需在宿主机上起一个 docker engine,然后就...

    Java技术栈
  • 【K8s源码品读】008:Phase 1 - kube-scheduler - 初探调度的启动流程与算法

    junedayday
  • K8S Pod调度策略

    kube-scheduler是K8S集群默认的调度器,如果你愿意,也可以自己写一个调度组件来替代kube-scheduler,在实际应用中,kube-sched...

    用户2020431
  • kuberneter调度由浅入深:框架

    1. Kubernetes社区GB代表选举结束 Paris Pittman当选

    zouyee
  • Kubernetes K8S之固定节点nodeName和nodeSelector调度详解 当nodeName指定节点存在当nodeName指定节点不存在添加label

    nodeName是节点选择约束的最简单形式,但是由于其限制,通常很少使用它。nodeName是PodSpec的领域。

    踏歌行
  • Kubernetes监控实践(1):K8s的工作原理与监控实践

    Kubernetes(K8s)是一个开源平台,能够有效简化应用管理、应用部署和应用扩展环节的手动操作流程,让用户更加灵活地部署管理云端应用。

    宜信技术学院

扫码关注云+社区

领取腾讯云代金券