Skip to content

Commit 802bb6a

Browse files
author
mcsos
committed
Add Algorithm Provider in kube-scheduler
1 parent 63fd4ce commit 802bb6a

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
- Kubernetes Scheduler 启动过程
1010

1111
- [参数初始化过程分析](./kube-scheduler/init/option.md)
12+
13+
- Kubernetes Scheduler 中的组件
14+
15+
- [Algorithm Provider](./kube-scheduler/component/algorithm-provider.md)
1216

1317
- 调度算法详解
1418

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Algorithm Provider #
2+
3+
kubernetes Scheduler 提供了一个名为 `--algorithm-provider string` 的参数,它的可选值包括 `DefaultProvider``ClusterAutoscalerProvider`,可以通过它来指定所使用的的调度插件列表。
4+
5+
注意这个参数在目前版本中已经被标记为过期,在未来的版本中会被废弃掉,即便如此,我们仍然会在这里分析一下它的内部逻辑,因为它对于整个 kubernetes Scheduler 的配置和使用至关重要。代码位于 `pkg/scheduler/algorithmprovider/registry.go`
6+
7+
在 kubernetes Scheduler 初始化过程中,会使用下面的代码片段。
8+
9+
``` go
10+
func (c *Configurator) createFromProvider(providerName string) (*Scheduler, error) {
11+
klog.V(2).Infof("Creating scheduler from algorithm provider '%v'", providerName)
12+
r := algorithmprovider.NewRegistry()
13+
defaultPlugins, exist := r[providerName]
14+
...
15+
}
16+
```
17+
18+
首先,会创建一个 registry 对象,然后使用配置的 `providerName` 来获取插件列表,接着会用这个插件列表对象进行后续的初始化操作。
19+
20+
这段代码其实就是 Algorithm Provider 的全部使用。下面来分析其具体实现。
21+
22+
``` go
23+
type Registry map[string]*schedulerapi.Plugins
24+
```
25+
26+
首先,Registry 对象是一个 map,key 为 Algorithm Provider 名称,value 为其对应的调度插件列表。
27+
28+
``` go
29+
func NewRegistry() Registry {
30+
defaultConfig := getDefaultConfig()
31+
applyFeatureGates(defaultConfig)
32+
33+
caConfig := getClusterAutoscalerConfig()
34+
applyFeatureGates(caConfig)
35+
36+
return Registry{
37+
schedulerapi.SchedulerDefaultProviderName: defaultConfig,
38+
ClusterAutoscalerProvider: caConfig,
39+
}
40+
}
41+
```
42+
43+
这里返回的 Registry 的 key 就是命令行选项中的 `DefaultProvider``ClusterAutoscalerProvider`,接下来看它们的 value。
44+
45+
`DefaultProvider` 对应的 value 是使用 `getDefaultConfig()` 返回的,内容就是按照不同的插入点分类的不同的插件名称,代码如下:
46+
47+
``` go
48+
func getDefaultConfig() *schedulerapi.Plugins {
49+
return &schedulerapi.Plugins{
50+
QueueSort: &schedulerapi.PluginSet{
51+
Enabled: []schedulerapi.Plugin{
52+
{Name: queuesort.Name},
53+
},
54+
},
55+
PreFilter: &schedulerapi.PluginSet{
56+
Enabled: []schedulerapi.Plugin{
57+
{Name: noderesources.FitName},
58+
{Name: nodeports.Name},
59+
{Name: interpodaffinity.Name},
60+
},
61+
},
62+
Filter: &schedulerapi.PluginSet{
63+
Enabled: []schedulerapi.Plugin{
64+
{Name: nodeunschedulable.Name},
65+
{Name: noderesources.FitName},
66+
{Name: nodename.Name},
67+
{Name: nodeports.Name},
68+
{Name: nodeaffinity.Name},
69+
{Name: volumerestrictions.Name},
70+
{Name: tainttoleration.Name},
71+
{Name: nodevolumelimits.EBSName},
72+
{Name: nodevolumelimits.GCEPDName},
73+
{Name: nodevolumelimits.CSIName},
74+
{Name: nodevolumelimits.AzureDiskName},
75+
{Name: volumebinding.Name},
76+
{Name: volumezone.Name},
77+
{Name: interpodaffinity.Name},
78+
},
79+
},
80+
PreScore: &schedulerapi.PluginSet{
81+
Enabled: []schedulerapi.Plugin{
82+
{Name: interpodaffinity.Name},
83+
{Name: defaultpodtopologyspread.Name},
84+
{Name: tainttoleration.Name},
85+
},
86+
},
87+
Score: &schedulerapi.PluginSet{
88+
Enabled: []schedulerapi.Plugin{
89+
{Name: noderesources.BalancedAllocationName, Weight: 1},
90+
{Name: imagelocality.Name, Weight: 1},
91+
{Name: interpodaffinity.Name, Weight: 1},
92+
{Name: noderesources.LeastAllocatedName, Weight: 1},
93+
{Name: nodeaffinity.Name, Weight: 1},
94+
{Name: nodepreferavoidpods.Name, Weight: 10000},
95+
{Name: defaultpodtopologyspread.Name, Weight: 1},
96+
{Name: tainttoleration.Name, Weight: 1},
97+
},
98+
},
99+
Bind: &schedulerapi.PluginSet{
100+
Enabled: []schedulerapi.Plugin{
101+
{Name: defaultbinder.Name},
102+
},
103+
},
104+
}
105+
}
106+
```
107+
108+
需要注意的是,有可能同一个插件实现了多个插入点,因此会出现多次。
109+
110+
`ClusterAutoscalerProvider` 对应的 value:
111+
112+
``` go
113+
func getClusterAutoscalerConfig() *schedulerapi.Plugins {
114+
caConfig := getDefaultConfig()
115+
// Replace least with most requested.
116+
for i := range caConfig.Score.Enabled {
117+
if caConfig.Score.Enabled[i].Name == noderesources.LeastAllocatedName {
118+
caConfig.Score.Enabled[i].Name = noderesources.MostAllocatedName
119+
}
120+
}
121+
return caConfig
122+
}
123+
```
124+
125+
可以看出它的插件列表和 `DefaultProvider` 的插件列表几乎是一样的,只是将 Score 插入点的 `NodeResourcesLeastAllocated` 插件替换为 `NodeResourcesMostAllocated`
126+
127+
在获取了插件列表后,还会使用 `applyFeatureGates()` 做一些额外的修改。
128+
129+
``` go
130+
func applyFeatureGates(config *schedulerapi.Plugins) {
131+
if utilfeature.DefaultFeatureGate.Enabled(features.EvenPodsSpread) {
132+
klog.Infof("Registering EvenPodsSpread predicate and priority function")
133+
f := schedulerapi.Plugin{Name: podtopologyspread.Name}
134+
config.PreFilter.Enabled = append(config.PreFilter.Enabled, f)
135+
config.Filter.Enabled = append(config.Filter.Enabled, f)
136+
config.PreScore.Enabled = append(config.PreScore.Enabled, f)
137+
s := schedulerapi.Plugin{Name: podtopologyspread.Name, Weight: 1}
138+
config.Score.Enabled = append(config.Score.Enabled, s)
139+
}
140+
141+
if utilfeature.DefaultFeatureGate.Enabled(features.ResourceLimitsPriorityFunction) {
142+
klog.Infof("Registering resourcelimits priority function")
143+
s := schedulerapi.Plugin{Name: noderesources.ResourceLimitsName}
144+
config.PreScore.Enabled = append(config.PreScore.Enabled, s)
145+
s = schedulerapi.Plugin{Name: noderesources.ResourceLimitsName, Weight: 1}
146+
config.Score.Enabled = append(config.Score.Enabled, s)
147+
}
148+
}
149+
```
150+
151+
这里的内容是根据是否开启了 `EvenPodsSpread``ResourceLimitsPriorityFunction` 特性来决定是否加入额外的插件。
152+
153+
调用 `applyFeatureGates()` 修改完插件列表后,`NewRegistry()` 返回 Registry 对象。
154+
155+
现在回到本文开头的代码中。
156+
157+
``` go
158+
func (c *Configurator) createFromProvider(providerName string) (*Scheduler, error) {
159+
klog.V(2).Infof("Creating scheduler from algorithm provider '%v'", providerName)
160+
r := algorithmprovider.NewRegistry()
161+
defaultPlugins, exist := r[providerName]
162+
...
163+
}
164+
```
165+
166+
这里的参数 `providerName` 在初始化的时候已经被定义为 `DefaultProvider`,相关的初始化结果存储在 `defaultSchedulerOptions` 中。
167+
168+
``` go
169+
var defaultSchedulerOptions = schedulerOptions{
170+
...
171+
schedulerAlgorithmSource: schedulerapi.SchedulerAlgorithmSource{
172+
Provider: defaultAlgorithmSourceProviderName(),
173+
},
174+
...
175+
}
176+
```
177+
178+
其中的 `defaultAlgorithmSourceProviderName()`
179+
180+
``` go
181+
func defaultAlgorithmSourceProviderName() *string {
182+
provider := schedulerapi.SchedulerDefaultProviderName
183+
return &provider
184+
}
185+
```
186+
187+
``` go
188+
const (
189+
...
190+
191+
// SchedulerDefaultProviderName defines the default provider names
192+
SchedulerDefaultProviderName = "DefaultProvider"
193+
)
194+
```
195+
196+
至此,已经全部分析完了关于 kubernetes Scheduler 中 Algorithm Provider 的内容。
197+
198+
kubernetes Scheduler 初始化时定义了 Provider 名称为 `DefaultProvider`(如果没有在命令行使用参数指定其它值),接着使用 `algorithmprovider.NewRegistry()` 返回 Registry 对象,然后使用 `DefaultProvider` 返回其中对应的插件列表。

0 commit comments

Comments
 (0)