K8s 源码分析 —— scheduler(一)
基于 release-1.25 分支的代码分析
一,疑问
Question:调度器挑选节点时,是根据节点当前的真实压力,还是根据 pod 的 request 值?
Answer:是根据 pod 的 request 来判断的,但有些插件在尝试根据节点真实压力来判断。
具体代码位于:pkg/scheduler/framework/plugins/noderesources/fit.go#L253
二,代码分析
1,概括
AdmissionCheck() ——> Fits() ——> fitsRequest(computePodResourceRequest(pod), nodeInfo, nil, nil)
2,局部逻辑
2.1 computePodResourceRequest(pod):
计算 pod 的资源申请量:pod 里面分 init 容器和普通容器,由于 init 容器是顺序运行的,所以取最大值(initMax) 就行,而普通容器取申请值总和(sum),然后再取 max(initMax,sum) 作为最后的结果。
Example:
Pod:
InitContainers
IC1:
CPU: 2
Memory: 1G
IC2:
CPU: 2
Memory: 3G
Containers
C1:
CPU: 2
Memory: 1G
C2:
CPU: 1
Memory: 1G
Result: CPU: 3, Memory: 3G
大致逻辑:
1,遍历普通容器,把对应资源进行相加操作;
2,遍历 init 容器,对比 1 里的结果和当前 init 容器每一项资源大小,取大值;
3,如果存在 pod.Spec.Overhead,把 2 的结果再加上 pod.Spec.Overhead 返回。
2.2 fitsRequest():
根据 2.1 里得到的 pod 所需资源与当前节点上剩余资源相对比,判断各个资源是否够用,记录容量不足的资源返回上一层函数。
大致逻辑:
1,定义一个变量 insufficientResources,里面用来存放当前节点资源不足的信息;
2,如果当前节点上所运行着的 pod 数量 +1 > 此节点所允许运行的 pod 数量,则记录:
"Too many pods" >> insufficientResources
3,如果此 pod 申请的各种资源大小全为 0,到此直接返回 insufficientResources;
4,如果此 pod 申请的 cpu > (此节点可分配的 cpu - 此节点上所有 pod 的 cpu 申请量总和),则记录:
"Insufficient cpu" >> insufficientResources
5,如果此 pod 申请的 memory > (此节点可分配的 memory - 此节点上所有 pod 的 memory 申请量总和),则记录:
"Insufficient memory" >> insufficientResources
6,如果此 pod 申请的临时存储容量 > (此节点可分配的临时存储容量 - 此节点上所有 pod 的临时存储容申请量总和),则记录:
"Insufficient ephemeral-storage" >> insufficientResources
7,遍历 ScalarResources,如果其中某一项资源申请量 > (此节点可分配 - 此节点上所有 pod 申请总和),则记录:
("Insufficient %v", rName) >> insufficientResources
8,返回 insufficientResources 。
2.3 AdmissionCheck():
检查当前节点是否可调度当前 pod,从资源,节点亲和性等各个方面进行判断。
大致逻辑:
1,定义一个变量 admissionResults,用来存放当前节点是否符合调度 pod 的结果;
2,获取到 Fits() 返回的 insufficientResources;
3,判断 insufficientResources 是否为空,不为空则遍历 insufficientResources,追加到 admissionResults;
4,判断 pod 的 nodeSelector 和 nodeAffinity 是否和节点相匹配,不匹配就把相关信息追加到 admissionResults;
"node(s) didn't match Pod's node affinity/selector"
5,判断 pod.Spec.NodeName 是否和当前节点名称匹配,不匹配就把相关信息追加到 admissionResults;
"node(s) didn't match the requested node name"
6,判断 pod 里所有容器申请的端口信息与当前节点已使用的端口有冲突,如果有就把相关信息追加到 admissionResults;
"node(s) didn't have free ports for the requested pod ports"
7,返回 admissionResults,在 3~6 中,如果当前不要求返回所有不匹配信息(includeAllFailures==false),在遇到第一个不匹配信息就会直接返回 admissionResults。
三,总结
Scheduler 调度 pod 时,是根据资源申请值来判断当前节点是否可选择的。
Q1:如果 Pod 的资源申请值是 1C,限制值是 2C,那么是否会调度到资源还有 1.5C 的节点上?
A1:会。因为 Scheduler 只考虑了申请值,会出现容器使用的资源量达不到限制值的情况。
// 有一些 out-of-tree plugin 考虑了限制值,比如:
https://github.com/kubernetes-sigs/scheduler-plugins/tree/master/pkg/trimaran/lowriskovercommitment
Q2:如果一个 Pod 需要 1C1G,并且根据申请值判断出某节点上还剩 1C1G,但此节点上已运行的 Pod 使用的资源是大于其申请量的,所以该节点剩余资源并没有 1C1G, 那么是否会调度这个新 Pod 到这个节点上呢?