|
| 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