因为研究的需要,需要对K8S的调度器进行扩展,本文主要讲解了k8s调度器扩展的一个流程,其中包含源码修改、源码编译、调度器配置以及部署和本人所踩的一些坑,使用的k8s的版本为1.23.1
git clone --branch v1.23.1 https://github.com/kubernetes/kubernetes.git
调度器的源码在kubernetes/pkg/scheduler
和kubernetes/cmd/kube-scheduler
目录下,第一个路径是调度器的主要功能实现的代码,第二个是调度器的main文件所在位置
调度器中的调度框架定义了一组扩展点,用户可以实现扩展点定义的接口来实现自己的调度逻辑,并将扩展注册到扩展点上。
如下图所示,有下面几个扩展点:
主要讲解几个重要的扩展点:
QueueSort:
该扩展点用于对调度队列中的Pod进行排序,以决定优先调度哪个Pod
Filter:
对节点进行过滤,排除那些不满足Pod运行的节点,比如Pod需要2G的内存,如果节点的可用内存小于2G那么就会被过滤掉。
Score:
对过滤后的节点进行打分,最终选择一个得分最高的节点来运行该Pod
Bind:
用于将Pod绑定到选择的节点上。
k8s调度器的调度框架是基于预选
和优选
两个阶段的框架。
- 预选阶段:
在该阶段,调度器将根据Pod的资源需求和节点的资源容量等信息,筛选出所有可行的节点,将它们标记为“可调度节点”。该阶段中,调度器将对各个节点依次执行注册的各个Filter插件
(Filter插件可以有很多),只要有一个Filter插件不满足,就会排除掉该节点。
优选阶段:
在该阶段,调度器将针对每个“可调度节点”,计算出一个得分,选取最高得分的节点,并将Pod绑定到该节点上。该阶段中,调度器将对经过过滤的各个阶段执行注册的各个Score插件
(Score插件可以有很多)来对节点进行打分,最终对各个插件的打分进行聚合,最终选择得分最高的节点来运行Pod。
整个调度框架是可以扩展的,可以在预选和优选阶段之间添加过滤器和扩展器,以定制化地满足用户的需求。
源码分析:
代码在kubernetes/pkg/scheduler/schedule_one.go
中
预选阶段:
如下图为QueueSortPlugin、FilterPlugin以及ScorePlugin,还有很多其它的插件,在这里就不介绍了
k8s scheduler为我们提供了很多调度插件扩展点,我们可以实现这些插件来实现特定的一些功能。
如果要自己实现一个打分插件,那么需要下面的步骤:
kubernetes/pkg/scheduler/framework/plugins
目录下创建自己的目录,编写代码实现插件定义的接口,比如Filter插件需要实现Filter这个方法,Score插件需要实现一个打分的方法Scorekubernetes/cmd/kube-scheduler/scheduler.go
中注册插件:k8s官方已经为我们提供了编译脚本,也就是Makefile
下面的命令可以将源码编译成二进制程序(执行下面的命令前需要在kubernetes目录下
):
make all WHAT=cmd/kube-scheduler GOFLAGS=-v
使用make all
可以编译所有的二进制程序,使用WHAT
可以选择单独编译某个程序,GOFLAGS
用于给go编译器传递参数,-v
表示在编译时显示正在编译的文件
编译成功后的二进制程序在_output/bin/
文件夹下:
使用下面的命令可以将程序编译为镜像:
注意:编译为docker镜像时依赖其它的镜像,比如kube-cross镜像,这些镜像需要科学上网才能下载,而且镜像很大,kube-cross有六点几个G
make quick-release-images
使用该命令只会编译linux/amd64平台下的docker镜像,而且无法指定单独编译某个组件
编译完成后的镜像在_output/release-images/amd64
目录下,为tar包
将tar包导入docker镜像:
docker load < kube-scheduler.tar
我们对调度器的源码进行了扩展,实现了里面规定的插件,并且注册了插件。
调度器会生成一个默认的调度方案,名称为default-scheduler
,这个调度方案中包含了一系列的原生插件,
通过下面的配置文件我们可以对各个扩展点进行启用或禁用,profiles是一个切片,一个profile就是一个调度器,可以通过profiles来指定多个包含不同扩展点的调度器。
apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
leaderElection:
leaderElect: true
profiles:
- schedulerName: my-scheduler
plugins:
score:
enabled:
- name: MyScorePlugin
disabled:
- name: "*"
这个配置文件对应的go结构体在kubernetes/pkg/scheduler/apis/config/types.go中
注意:
默认情况下,调度器会生成一个default-scheduler的profile,如果使用我们自己编写的config文件,如果没有配置default-scheduler,默认的好像是不会生成的。但是看别人的博客中说是会自己生成的,但是我的就没有生成,在没有指定调度器的时候,提交的pod不会被调度。
5.1节中生成的配置需要在scheduler启动的时候使用--config
参数来指定,因此需要重启scheduler。
注意:
kubeadm搭建的集群中,kubernetes中的组件(除了kubelet)是以静态Pod的方式运行的。
注意:
不能通过kubectl edit kube-scheduler-master的方式来修改,因为scheduler是静态pod,无法通过ApiServer来进行管理
静态Pod是由kubelet进行管理并且仅存于特点Node上的Pod,因此它们不能通过ApiServer进行管理。
静态Pod的配置文件可以从kubelet的配置文件中获取:
kubelet的配置文件为/var/lib/kubelet/config.yaml
,查看配置文件:
可以获取到静态Pod所在的配置文件路径为/etc/kubernetes/manifests
在/etc/kubernetes/manifests有一些Pod资源的yaml文件,kubelet会负责启动该目录下的所有pod,当我们对这些配置文件进行了修改后,kubelet会重新启动修改的pod。
要重启scheduler,并且指定配置文件,我们需要修改/etc/kubernetes/manifests
目录下的kube-scheduler.yaml
文件,我们只需要修改kube-scheduler.yaml,kubelet就会根据这个文件重启scheduler,
注意:经过测试,如果镜像没有修改,scheduler应该是不会重启的
我们需要将调度器的配置文件挂载到Pod中,并且指定Pod启动的命令行参数,同时指定我们构建的镜像的名称
修改kube-scheduler.yaml
文件,需要修改的地方有四个,分别是:scheduler启动参数
;镜像名
;volumes
;volumeMounts
1添加--config参数指定配置文件 2.修改镜像:
3 挂载配置文件到/etc/kubernetes/目录下:
之后kubelet会重启scheduler,如果没有可以尝试重启主机
部署了修改的调度器后,就需要对我们的调度器进行一个测试,可以在集群中部署Pod来测试调度情况。
在部署Pod的时候,需要指定调度器的名称,比如我们在配置文件中的profiles
下配置了一个名称为my-scheduler
的调度器,因此在创建pod类的资源时要指定调度器名称:
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
schedulerName: my-scheduler
containers:
- image: nginx:1.17.1
imagePullPolicy: IfNotPresent
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
在windows上克隆了源码,对scheduler进行了扩展,然后将代码上传到了Linux上进行编译,出现下面的错误:
后来才发现,kubernetes根目录下的Makefile
是一个链接文件
,链接到buidl/root/Makefile
,但是克隆到windows上
之后,这个链接就没有
了,上传到Linux后,没有这个链接
,因此无法编译
。
windows上克隆后上传到linux的文件:缺少链接
要将k8s中的组件编译为镜像,需要依赖其它的镜像,比如:kube-cross、go-runner等,
这些镜像需要去外网下载,而且镜像文件很大,导致频繁失败。
解决方案1
:科学上网解决方案2
:如果没有科学上网,可以租赁阿里云国外服务器(按量使用),使用docker拉下来,再下载到本地。但是镜像很大,下载速度很慢解决方案3
:租赁阿里云国外服务器,直接在服务器上编译,编译完成后再下载到本地,但是如果代码出了问题,修改后就需要重新编译,很麻烦
安装完集群并且安装了网络插件后,依然显示所有节点NotReady:
查看日志:
发现日志出现 failed to find plugin “flannel” in path [/opt/cni/bin]
[root@node2 ~]# journalctl -f -u kubelet
-- Logs begin at 四 2023-04-06 15:59:04 CST. --
4月 06 16:25:31 node2 kubelet[2046]: I0406 16:25:31.045873 2046 cni.go:240] "Unable to update cni config" err="no valid networks found in /etc/cni/net.d"
4月 06 16:25:34 node2 kubelet[2046]: E0406 16:25:34.788414 2046 kubelet.go:2347] "Container runtime network not ready" networkReady="NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized"
4月 06 16:25:36 node2 kubelet[2046]: I0406 16:25:36.049721 2046 cni.go:205] "Error validating CNI config list" configList="{\n \"name\": \"cbr0\",\n \"cniVersion\": \"0.3.1\",\n \"plugins\": [\n {\n \"type\": \"flannel\",\n \"delegate\": {\n \"hairpinMode\": true,\n \"isDefaultGateway\": true\n }\n },\n {\n \"type\": \"portmap\",\n \"capabilities\": {\n \"portMappings\": true\n }\n }\n ]\n}\n" err="[failed to find plugin \"flannel\" in path [/opt/cni/bin]]"
4月 06 16:25:36 node2 kubelet[2046]: I0406 16:25:36.049741 2046 cni.go:240] "Unable to update cni config" err="no valid networks found in /etc/cni/net.d"
4月 06 16:25:39 node2 kubelet[2046]: E0406 16:25:39.804414 2046 kubelet.go:2347] "Container runtime network not ready" networkReady="NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized"
4月 06 16:25:41 node2 kubelet[2046]: I0406 16:25:41.053032 2046 cni.go:205] "Error validating CNI config list" configList="{\n \"name\": \"cbr0\",\n \"cniVersion\": \"0.3.1\",\n \"plugins\": [\n {\n \"type\": \"flannel\",\n \"delegate\": {\n \"hairpinMode\": true,\n \"isDefaultGateway\": true\n }\n },\n {\n \"type\": \"portmap\",\n \"capabilities\": {\n \"portMappings\": true\n }\n }\n ]\n}\n" err="[failed to find plugin \"flannel\" in path [/opt/cni/bin]]"
查看/opt/cni/bin 缺少 flannel
解决方案:
下载CNI插件:https://github.com/containernetworking/plugins/releases/tag/v0.8.6
解压后放入/opt/cni/bin/
目录下:
tar zxvf cni-plugins-linux-amd64-v0.8.6.tgz
cp flannel /opt/cni/bin/