以下代码分析基于
kubernetes v1.12.0
版本。
本文主要分析优选策略
逻辑,即从预选的节点中选择出最优的节点。优选策略的具体实现函数为PrioritizeNodes
。PrioritizeNodes
最终返回是一个记录了各个节点分数的列表。
1. 调用入口
genericScheduler.Schedule
中对PrioritizeNodes
的调用过程如下:
此部分代码位于pkg/scheduler/core/generic_scheduler.go
1 | func (g *genericScheduler) Schedule(pod *v1.Pod, nodeLister algorithm.NodeLister) (string, error) { |
核心代码:
1 | // 基于预选节点filteredNodes进一步筛选优选的节点,返回记录各个节点分数的列表 |
2. PrioritizeNodes
优选,从满足的节点中选择出最优的节点。PrioritizeNodes
最终返回是一个记录了各个节点分数的列表。
具体操作如下:
- PrioritizeNodes通过并行运行各个优先级函数来对节点进行优先级排序。
- 每个优先级函数会给节点打分,打分范围为0-10分。
- 0 表示优先级最低的节点,10表示优先级最高的节点。
- 每个优先级函数也有各自的权重。
- 优先级函数返回的节点分数乘以权重以获得加权分数。
- 最后组合(添加)所有分数以获得所有节点的总加权分数。
PrioritizeNodes主要流程如下:
- 如果没有设置优选函数和拓展函数,则全部节点设置相同的分数,直接返回。
- 依次给node执行map函数进行打分。
- 再对上述map函数的执行结果执行reduce函数计算最终得分。
- 最后根据不同优先级函数的权重对得分取加权平均数。
入参:
- pod
- nodeNameToInfo
- meta interface{},
- priorityConfigs
- nodes
- extenders
出参:
- HostPriorityList:记录节点分数的列表。
HostPriority
定义如下:
1 | // HostPriority represents the priority of scheduling to a particular host, higher priority is better. |
PrioritizeNodes
完整代码如下:
此部分代码位于pkg/scheduler/core/generic_scheduler.go
1 | // PrioritizeNodes prioritizes the nodes by running the individual priority functions in parallel. |
以下对PrioritizeNodes
分段进行分析。
3. EqualPriorityMap
如果没有提供优选函数和拓展函数,则将所有的节点设置为相同的优先级,即节点的score都为1,然后直接返回结果。(但一般情况下优选函数列表都不为空)
1 | // If no priority configs are provided, then the EqualPriority function is applied |
EqualPriorityMap具体实现如下:
1 | // EqualPriorityMap is a prioritizer function that gives an equal weight of one to all nodes |
4. processNode
processNode
就是基于index拿出node的信息,调用之前注册的各种优选函数(此处是mapFunction
),通过优选函数对node和pod进行处理,最后返回一个记录node分数的列表result
。processNode
同样也使用workqueue.Parallelize
来进行并行处理。(processNode
类似于预选逻辑findNodesThatFit
中使用到的checkNode
的作用)
其中优选函数是通过priorityConfigs
来记录,每类优选函数包括PriorityMapFunction
和PriorityReduceFunction
两种函数。优选函数的注册部分可参考registerAlgorithmProvider。
1 | processNode := func(index int) { |
priorityConfigs
定义如下:
核心属性:
- Map :PriorityMapFunction
- Reduce:PriorityReduceFunction
1 | // PriorityConfig is a config used for a priority function. |
具体的优选函数处理逻辑待下文分析,本文会以NewSelectorSpreadPriority
函数为例。
5. PriorityMapFunction
PriorityMapFunction
是一个计算给定节点的每个节点结果的函数。
PriorityMapFunction
定义如下:
1 | // PriorityMapFunction is a function that computes per-node results for a given node. |
PriorityMapFunction是在processNode
中调用的,代码如下:
1 | results[i][index], err = priorityConfigs[i].Map(pod, meta, nodeInfo) |
下文会分析NewSelectorSpreadPriority
在的map函数CalculateSpreadPriorityMap
。
6. PriorityReduceFunction
PriorityReduceFunction
是一个聚合每个节点结果并计算所有节点的最终得分的函数。
PriorityReduceFunction
定义如下:
1 | // PriorityReduceFunction is a function that aggregated per-node results and computes |
PrioritizeNodes中对reduce函数调用部分如下:
1 | for i, priorityConfig := range priorityConfigs { |
下文会分析NewSelectorSpreadPriority
在的reduce函数CalculateSpreadPriorityReduce
。
7. Summarize all scores
先等待计算完成再计算加权平均数。
1 | // Wait for all computations to be finished. |
计算所有节点的加权平均数。
1 | // Summarize all scores. |
当设置了拓展的计算方式,则增加拓展计算方式的加权平均数。
1 | if len(extenders) != 0 && nodes != nil { |
8. NewSelectorSpreadPriority
以下以NewSelectorSpreadPriority
这个优选函数来做分析,其他重要的优选函数待后续专门分析。
NewSelectorSpreadPriority
主要的功能是将属于相同service和rs下的pod尽量分布在不同的node上。
该函数的注册代码如下:
此部分代码位于pkg/scheduler/algorithmprovider/defaults/defaults.go
1 | // ServiceSpreadingPriority is a priority config factory that spreads pods by minimizing |
NewSelectorSpreadPriority
的具体实现如下:
此部分代码位于pkg/scheduler/algorithm/priorities/selector_spreading.go
1 | // NewSelectorSpreadPriority creates a SelectorSpread. |
NewSelectorSpreadPriority
主要包括map和reduce两种函数,分别对应CalculateSpreadPriorityMap
,CalculateSpreadPriorityReduce
。
8.1. CalculateSpreadPriorityMap
CalculateSpreadPriorityMap
的主要作用是将相同service、RC、RS或statefulset的pod分布在不同的节点上。当调度一个pod的时候,先寻找与该pod匹配的service、RS、RC或statefulset,然后寻找与其selector匹配的已存在的pod,寻找存在这类pod最少的节点。
基本流程如下:
- 寻找与该pod对应的service、RS、RC、statefulset匹配的selector。
- 遍历当前节点的所有pod,将该节点上已存在的selector匹配到的pod的个数作为该节点的分数(此时,分数大的表示匹配到的pod越多,越不符合被调度的条件,该分数在reduce阶段会被按10分制处理成分数大的越符合被调度的条件)。
此部分代码位于pkg/scheduler/algorithm/priorities/selector_spreading.go
1 | // CalculateSpreadPriorityMap spreads pods across hosts, considering pods |
以下分段分析:
先获得selector。
1 | selectors = getSelectors(pod, s.serviceLister, s.controllerLister, s.replicaSetLister, s.statefulSetLister) |
计算节点上匹配selector的pod的个数,作为该节点分数,该分数并不是最终节点的分数,只是中间过渡的记录状态。
1 | count := int(0) |
8.2. CalculateSpreadPriorityReduce
CalculateSpreadPriorityReduce
根据节点上现有匹配pod的数量计算每个节点的十分制的分数,具有较少现有匹配pod的节点的分数越高,表示节点越可能被调度到。
基本流程如下:
- 记录所有节点中匹配到pod个数最多的节点的分数(即匹配到的pod最多的个数)。
- 遍历所有的节点,按比例取十分制的得分,计算方式为:(节点中最多匹配pod的个数-当前节点pod的个数)/节点中最多匹配pod的个数。此时,分数越高表示该节点上匹配到的pod的个数越少,越可能被调度到,即满足把相同selector的pod分散到不同节点的需求。
此部分代码位于pkg/scheduler/algorithm/priorities/selector_spreading.go
1 | // CalculateSpreadPriorityReduce calculates the source of each node |
以下分段分析:
先获取所有节点中匹配到的pod最多的个数。
1 | for i := range result { |
遍历所有的节点,按比例取十分制的得分。
1 | for i := range result { |
9. 总结
优选,从满足的节点中选择出最优的节点。PrioritizeNodes
最终返回是一个记录了各个节点分数的列表。
9.1. PrioritizeNodes
主要流程如下:
- 如果没有设置优选函数和拓展函数,则全部节点设置相同的分数,直接返回。
- 依次给node执行map函数进行打分。
- 再对上述map函数的执行结果执行reduce函数计算最终得分。
- 最后根据不同优先级函数的权重对得分取加权平均数。
其中每类优选函数会包含map函数和reduce函数两种。
9.2. NewSelectorSpreadPriority
其中以NewSelectorSpreadPriority
这个优选函数为例作分析,该函数的功能是将相同service、RS、RC或statefulset下pod尽量分散到不同的节点上。包括map函数和reduce函数两部分,具体如下。
9.2.1. CalculateSpreadPriorityMap
基本流程如下:
- 寻找与该pod对应的service、RS、RC、statefulset匹配的selector。
- 遍历当前节点的所有pod,将该节点上已存在的selector匹配到的pod的个数作为该节点的分数(此时,分数大的表示匹配到的pod越多,越不符合被调度的条件,该分数在reduce阶段会被按10分制处理成分数大的越符合被调度的条件)。
9.2.2. CalculateSpreadPriorityReduce
基本流程如下:
- 记录所有节点中匹配到pod个数最多的节点的分数(即匹配到的pod最多的个数)。
- 遍历所有的节点,按比例取十分制的得分,计算方式为:(节点中最多匹配pod的个数-当前节点pod的个数)/节点中最多匹配pod的个数。此时,分数越高表示该节点上匹配到的pod的个数越少,越可能被调度到,即满足把相同selector的pod分散到不同节点的需求。
参考:
赞赏一下