k8s特性02 —— 自定义调度器
/ / 点击本文基于k8s 1.18.2
自定义调度的方法
k8s中自定义调度一共有三种方法:
- 直接修改源码,重新编译。
- k8s自身实现了很多种调度算法,自定义如何使用这些算法
- 实现自己的调度算法,在原调度基础上,调度器调接口执行自定义调度
- 脱离原调度器,完全自己实现调度
- 第一种方法对源码侵入性比较大,正常情况下不适用。
- 第二种方法在官方实现的调度算法中,通过组合方式能够实现自己调度需求的则可用,否则不可用。
- 第三种使用场景更加广泛,可执行用户自己实现的调度策略。
- 第四种更自由,但是无法使用官方的调度算法
自定义调度
修改源码+定义调度算法:
最小入侵方案:
方案1:
在 pkg/scheduler 包下建立自己的代码包,实验代码结构如下:
1
2
3
4
5--outoft
|-- outoft.go
|-- plugins
|-- fakeplugin
|-- fakeplugin.go其中,plugins包为扩展调度包,其中包含用户自己调度逻辑。如果需要实现什么,在里边实现即可。此处给出fakePlugin代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package fakeplugin
import (
"context"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
)
const (
Name = "FakePlugin"
)
type FakePlugin struct {
handle framework.FrameworkHandle
}
func (f FakePlugin) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
return nil
}
func (f FakePlugin) Name() string {
return Name
}
func New(_ runtime.Object, handle framework.FrameworkHandle) (framework.Plugin, error) {
return &FakePlugin{
handle: handle,
}, nil
}注意1:plugin需要实现
pkg/scheduler/framework/v1alpha1/Plugin接口,用于注册该plugin。另外,如果需要在某个调度点实现自定义功能,实现指定的方法即可:此处实现了pkg/scheduler/framework/v1alpha1/FilterPlugin接口,可通过配置,在调度filter阶段执行完默认调度策略后来调用该方法。注册plugin的方法
此处给出 outoft.go文件代码,重点在 Register方法,该方法将用户自定义plugin注册到registry里。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package outoft
import (
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
"k8s.io/kubernetes/pkg/scheduler/outoft/plugins/fakeplugin"
)
func Register(registry framework.Registry) error {
for name, factory := range reg {
registry.Register(name, factory)
}
return nil
}
var reg = framework.Registry{
fakeplugin.Name: fakeplugin.New,
}配置调用注册plugin
修改scheduler的启动main函数。1
2
3
4
5
6
7
8package main
// ...
func main() {
// ...
command := app.NewSchedulerCommand()
// ...
}修改为
1
2
3
4
5
6
7
8package main
// ...
func main() {
// ...
command := app.NewSchedulerCommand(outoft.Register)
// ...
}如此,在创建调度器的过程中,会调用到注册plugin的方法,从而将用户自定义的调度plugin注册。
调用链:command.Run() -> runCommand() -> Steup() -> option(outOfTreeRegistry)注册成功 -> scheduler.New() -> registry.Merge() 将outOfTree plugin合并到registry。配置调度器文件
只注册了调度器是不会被调用的,需要用户自定义调度策略。配置调度器的config.yaml文件(启动调度器时需指定 –config为此文件),其中profiles里配置了自定义的调度策略,default-scheduler是默认,my-scheduler为自定义的调度策略。修改完配置文件启动,修改测试deployment.spec.template.spec.schedulerName值为my-scheduler,新建的pod调度过程即可调用到用户自定义的调度器。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37apiVersion: kubescheduler.config.k8s.io/v1beta1
clientConnection:
acceptContentTypes: ""
burst: 100
contentType: application/vnd.kubernetes.protobuf
kubeconfig: /etc/kubernetes/scheduler.conf
qps: 50
disablePreemption: false
enableContentionProfiling: true
enableProfiling: true
healthzBindAddress: 0.0.0.0:10251
kind: KubeSchedulerConfiguration
leaderElection:
leaderElect: true
leaseDuration: 15s
renewDeadline: 10s
resourceLock: endpointsleases
resourceName: kube-scheduler
resourceNamespace: kube-system
retryPeriod: 2s
metricsBindAddress: 0.0.0.0:10251
percentageOfNodesToScore: 0
podInitialBackoffSeconds: 1
podMaxBackoffSeconds: 10
profiles:
- pluginConfig:
- args:
apiVersion: kubescheduler.config.k8s.io/v1beta1
hardPodAffinityWeight: 1
kind:
name: InterPodAffinity
schedulerName: default-scheduler
- schedulerName: my-scheduler
plugins:
filter:
enabled:
- name: FakePlugin
方案2:
新建自己的项目,将kubernetes包放到vender下,或者用go mod导入,用户自定义的plugin、plugin注册代码同上,只需将kube-scheduler的启动包下的文件copy到当前用户自定义项目中,修改该启动方法,增加plugin注册参数,编译运行即可。(启动scheduler时,需指定 –config文件为上边的config.yaml文件)
在原调度基础上,扩展调度
用户可建立独立项目,通过http方式接入第三方调度方法(目前仅支持http,pkg/scheduler/framework/v1alpha1/Extender接口,目前仅有http实现,后续可能增加实现方法)
用户可实现的调度接入点:Filter、Prioritize、Bind、preempt,实现相应调度点自定义调度需要
- 用户提供http请求服务,处理调度请求
- 配置config.yaml配置文件
扩展对所有调度策略生效,如果用户在某些场景下不需要执行调度,需要用户在代码逻辑中自行判断
1 | # 配置文件内容大体同上边的 config.yaml,在上边文件最后追加内容: |
注意1:请求时,会将urlPrefix与 xxxVerb 拼在一起作为请求路径,另外,如果自定义逻辑里对node信息有做缓存,那么可以通过配置请求时只传nodeName,以减少数据传输
自定义调度策略服务需要接收 /prefix/filter和/prefix/prioritize的请求,并对body解析,执行相应调度策略,然后返回。API格式,可参照源码中test示例(test/integration/scheduler/extender_test.go Extender的方法:Filter\Prioritize\Bind):
1 | Filter(arg *v1.ExtenderArgs) (*v1.ExtenderFilterResult, error) |
脱离k8s原调度器,完全实现自己的调度
- 给需要使用这种调度策略的Pod指定一个原调度器中没有的调度策略,这样,原调度器就不会调度这个Pod。
- 此时Pod会一直处于Pending状态,用户需实现自己的调度器来ListPod、ListNode,发现未调度的Pod就调度(主要是指定nodeName)。