调度器配置
官方文档地址:
- 调度器配置 https://kubernetes.io/docs/reference/scheduling/config/
- 配置多个调度器:https://kubernetes.io/docs/tasks/extend-kubernetes/configure-multiple-schedulers/
- kube-scheduler 参考:https://kubernetes.io/zh-cn/docs/reference/command-line-tools-reference/kube-scheduler/
- kube-scheduler 配置 (v1):https://kubernetes.io/zh-cn/docs/reference/config-api/kube-scheduler-config.v1/
- kube-scheduler 配置 (v1beta3):https://kubernetes.io/zh-cn/docs/reference/config-api/kube-scheduler-config.v1beta3/
调度器配置
当前配置
当前控制平面信息如下
> kubectl get nodes
Alias tip: kgno
NAME STATUS ROLES AGE VERSION
devmaster1 Ready control-plane 96d v1.25.4
devmaster2 Ready control-plane 96d v1.25.4
devmaster3 Ready control-plane 96d v1.25.4
当前 scheduler 配置如下:cat /etc/kubernetes/manifests/kube-scheduler.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
component: kube-scheduler
tier: control-plane
name: kube-scheduler
namespace: kube-system
spec:
containers:
- command:
- kube-scheduler
- --authentication-kubeconfig=/etc/kubernetes/scheduler.conf
- --authorization-kubeconfig=/etc/kubernetes/scheduler.conf
- --bind-address=127.0.0.1
- --kubeconfig=/etc/kubernetes/scheduler.conf
- --leader-elect=true
image: k8s.gcr.io/kube-scheduler:v1.24.4
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 8
httpGet:
host: 127.0.0.1
path: /healthz
port: 10259
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 15
name: kube-scheduler
resources:
requests:
cpu: 100m
startupProbe:
failureThreshold: 24
httpGet:
host: 127.0.0.1
path: /healthz
port: 10259
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 15
volumeMounts:
- mountPath: /etc/kubernetes/scheduler.conf
name: kubeconfig
readOnly: true
hostNetwork: true
priorityClassName: system-node-critical
securityContext:
seccompProfile:
type: RuntimeDefault
volumes:
- hostPath:
path: /etc/kubernetes/scheduler.conf
type: FileOrCreate
name: kubeconfig
status: {}
当期 kube-scheduler 进程信息
> ps -p $(pidof kube-scheduler) -o cmd -www | sed -e 's/--/\n--/g'
kube-scheduler
--authentication-kubeconfig=/etc/kubernetes/scheduler.conf
--authorization-kubeconfig=/etc/kubernetes/scheduler.conf
--bind-address=127.0.0.1
--kubeconfig=/etc/kubernetes/scheduler.conf
--leader-elect=true
应用最简单的配置
可以通过编写配置文件,并将其路径传给 kube-scheduler
的命令行参数,定制 kube-scheduler
的行为
调度模板(Profile)允许你配置 kube-scheduler 中的不同调度阶段。每个阶段都暴露于某个扩展点中。插件通过实现一个或多个扩展点来提供调度行为。
通过运行 kube-scheduler --config <filename>
来设置调度模板
在每个 master 节点上,新建文件夹存放配置
新建一个文件 /etc/kubernetes/custom-config/kube-scheduler/scheduler.conf
apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
clientConnection:
kubeconfig: /etc/kubernetes/scheduler.conf
leaderElection:
leaderElect: true
修改 /etc/kubernetes/manifests/kube-scheduler.yaml
文件中的启动命令和挂载为:
spec:
containers:
- command:
- kube-scheduler
- --config=/etc/kubernetes/custom-config/kube-scheduler/scheduler.conf
- --authorization-kubeconfig=/etc/kubernetes/scheduler.conf
- --authentication-kubeconfig=/etc/kubernetes/scheduler.conf
...
volumeMounts:
- mountPath: /etc/kubernetes/scheduler.conf
name: kubeconfig
readOnly: true
- mountPath: /etc/kubernetes/custom-config/kube-scheduler
name: kube-custom-config
readOnly: true
...
volumes:
- hostPath:
path: /etc/kubernetes/scheduler.conf
type: FileOrCreate
name: kubeconfig
- hostPath:
path: /etc/kubernetes/custom-config/kube-scheduler
type: DirectoryOrCreate
name: kube-custom-config
重启后的进程如下:
> ps -p $(pidof kube-scheduler) -o cmd -hww | sed -e 's/--/\n--/g'
kube-scheduler
--config=/etc/kubernetes/custom-config/kube-scheduler/scheduler.conf
--authentication-kubeconfig=/etc/kubernetes/scheduler.conf
--authorization-kubeconfig=/etc/kubernetes/scheduler.conf
配置文件
扩展点
调度行为发生在一系列阶段中,这些阶段是通过以下扩展点公开的:
queueSort
:这些插件对调度队列中的悬决的 Pod 排序。 一次只能启用一个队列排序插件。preFilter
:这些插件用于在过滤之前预处理或检查 Pod 或集群的信息。 它们可以将 Pod 标记为不可调度。filter
:这些插件相当于调度策略中的断言(Predicates),用于过滤不能运行 Pod 的节点。 过滤器的调用顺序是可配置的。 如果没有一个节点通过所有过滤器的筛选,Pod 将会被标记为不可调度。postFilter
:当无法为 Pod 找到可用节点时,按照这些插件的配置顺序调用他们。 如果任何postFilter
插件将 Pod 标记为**可调度**,则不会调用其余插件。preScore
:这是一个信息扩展点,可用于预打分工作。score
:这些插件给通过筛选阶段的节点打分。调度器会选择得分最高的节点。reserve
:这是一个信息扩展点,当资源已经预留给 Pod 时,会通知插件。 这些插件还实现了Unreserve
接口,在Reserve
期间或之后出现故障时调用。permit
:这些插件可以阻止或延迟 Pod 绑定。preBind
:这些插件在 Pod 绑定节点之前执行。bind
:这个插件将 Pod 与节点绑定。bind
插件是按顺序调用的,只要有一个插件完成了绑定,其余插件都会跳过。bind
插件至少需要一个。postBind
:这是一个信息扩展点,在 Pod 绑定了节点之后调用。multiPoint
:这是一个仅配置字段,允许同时为所有适用的扩展点启用或禁用插件。
对每个扩展点,你可以禁用默认插件或者是启用自己的插件,例如:
apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
profiles:
- plugins:
score:
disabled:
- name: PodTopologySpread
enabled:
- name: MyCustomPluginA
weight: 2
- name: MyCustomPluginB
weight: 1
调度插件
下面默认启用的插件实现了一个或多个扩展点:
ImageLocality
:选择已经存在 Pod 运行所需容器镜像的节点。
实现的扩展点:score
。
TaintToleration
:实现了污点和容忍。
实现的扩展点:filter
、preScore
、score
。
NodeName
:检查 Pod 指定的节点名称与当前节点是否匹配。
实现的扩展点:filter
。
NodePorts
:检查 Pod 请求的端口在节点上是否可用。
实现的扩展点:preFilter
、filter
。
实现的扩展点:filter
、score
。
PodTopologySpread
:实现了 Pod 拓扑分布。
实现的扩展点:preFilter
、filter
、preScore
、score
。
NodeUnschedulable
:过滤.spec.unschedulable
值为 true 的节点。
实现的扩展点:filter
。
NodeResourcesFit
:检查节点是否拥有 Pod 请求的所有资源。 得分可以使用以下三种策略之一:LeastAllocated
(默认)、MostAllocated
和RequestedToCapacityRatio
。
实现的扩展点:preFilter
、filter
、score
。
NodeResourcesBalancedAllocation
:调度 Pod 时,选择资源使用更为均衡的节点。
实现的扩展点:score
。
VolumeBinding
:检查节点是否有请求的卷,或是否可以绑定请求的卷。 实现的扩展点:preFilter
、filter
、reserve
、preBind
和score
。
**说明:**当 VolumeCapacityPriority
特性被启用时,score
扩展点也被启用。 它优先考虑可以满足所需卷大小的最小 PV。
VolumeRestrictions
:检查挂载到节点上的卷是否满足卷提供程序的限制。
实现的扩展点:filter
。
VolumeZone
:检查请求的卷是否在任何区域都满足。
实现的扩展点:filter
。
NodeVolumeLimits
:检查该节点是否满足 CSI 卷限制。
实现的扩展点:filter
。
EBSLimits
:检查节点是否满足 AWS EBS 卷限制。
实现的扩展点:filter
。
GCEPDLimits
:检查该节点是否满足 GCP-PD 卷限制。
实现的扩展点:filter
。
AzureDiskLimits
:检查该节点是否满足 Azure 卷限制。
实现的扩展点:filter
。
InterPodAffinity
:实现 Pod 间亲和性与反亲和性。
实现的扩展点:preFilter
、filter
、preScore
、score
。
PrioritySort
:提供默认的基于优先级的排序。
实现的扩展点:queueSort
。
DefaultBinder
:提供默认的绑定机制。
实现的扩展点:bind
。
DefaultPreemption
:提供默认的抢占机制。
实现的扩展点:postFilter
。
多配置文件
可以配置 kube-scheduler
运行多个配置文件。 每个配置文件都有一个关联的调度器名称,并且可以在其扩展点中配置一组不同的插件。
使用下面的配置样例,调度器将运行两个配置文件:一个使用默认插件,另一个禁用所有打分插件。
apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
- schedulerName: no-scoring-scheduler
plugins:
preScore:
disabled:
- name: '*'
score:
disabled:
- name: '*'
对于那些希望根据特定配置文件来进行调度的 Pod,可以在 .spec.schedulerName
字段指定相应的调度器名称。
默认情况下,将创建一个调度器名为 default-scheduler
的配置文件。 这个配置文件包括上面描述的所有默认插件。 声明多个配置文件时,每个配置文件中调度器名称必须唯一。
如果 Pod 未指定调度器名称,kube-apiserver 将会把调度器名设置为 default-scheduler
。 因此,应该存在一个调度器名为 default-scheduler
的配置文件来调度这些 Pod。
应用于多个扩展点的插件
从 kubescheduler.config.k8s.io/v1beta3
开始,配置文件配置中有一个附加字段 multiPoint
,它允许跨多个扩展点轻松启用或禁用插件。 multiPoint
配置的目的是简化用户和管理员在使用自定义配置文件时所需的配置。
示例
在示例中,希望 ImagePullPolicy 成为唯一的调度影响因素
修改 /etc/kubernetes/custom-config/kube-scheduler/pull-policy-only-scheduler.conf
内容如下:
apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
clientConnection:
kubeconfig: /etc/kubernetes/scheduler.conf
leaderElection:
leaderElect: true
profiles:
- schedulerName: default-scheduler
# If a QueueSort plugin is specified
# the same QueueSort Plugin and PluginConfig must be specified for all profiles.
plugins:
queueSort:
disabled:
- name: '*'
enabled:
- name: PrioritySort
- schedulerName: pull-policy-only-scheduler
plugins:
queueSort:
disabled:
- name: '*'
enabled:
- name: PrioritySort
preFilter:
disabled:
- name: '*'
filter:
disabled:
- name: '*'
enabled:
- name: TaintToleration
postFilter:
disabled:
- name: '*'
preScore:
disabled:
- name: '*'
score:
disabled:
- name: '*'
enabled:
- name: ImageLocality
weight: 100
reserve:
disabled:
- name: '*'
permit:
disabled:
- name: '*'
preBind:
disabled:
- name: '*'
bind:
disabled:
- name: '*'
enabled:
- name: DefaultBinder
postBind:
disabled:
- name: '*'
修改 /etc/kubernetes/manifests/kube-scheduler.yaml
指定使用新的配置文件(注意不改文件名有可能不生效,暂时不知道为什么)
在 devmaster2 节点拉取镜像
在 devmaster3 节点拉取镜像
创建命名空间
切换到对应的命名空间
分别创建 10 个容器
kubectl get pods -o=custom-columns=NAME:.metadata.name,Namespace:.metadata.namespace,NODE:.spec.nodeName,LABELS:.metadata.labels | sort
for i in {0..20..1}
do
kubectl apply -f - << EOF
apiVersion: v1
kind: Pod
metadata:
name: nx1-10-$i
labels:
name: nx1-10-$i
spec:
schedulerName: pull-policy-only-scheduler
containers:
- name: nx
image: nginx:1.10
EOF
kubectl apply -f - << EOF
apiVersion: v1
kind: Pod
metadata:
name: nx1-20-$i
labels:
name: nx1-20-$i
spec:
schedulerName: pull-policy-only-scheduler
containers:
- name: nx
image: nginx:1.20
EOF
sleep 1
done
kubectl get pods -o=custom-columns=NAME:.metadata.name,Namespace:.metadata.namespace,NODE:.spec.nodeName,LABELS:.metadata.labels | sort
注意:这个调度器还和镜像大小有关
代码:https://github.com/chendave/kubernetes/blob/master/pkg/scheduler/framework/plugins/imagelocality/image_locality.go
// calculatePriority returns the priority of a node. Given the sumScores of requested images on the node, the node's
// priority is obtained by scaling the maximum priority value with a ratio proportional to the sumScores.
func calculatePriority(sumScores int64, numContainers int) int64 {
maxThreshold := maxContainerThreshold * int64(numContainers)
if sumScores < minThreshold {
sumScores = minThreshold
} else if sumScores > maxThreshold {
sumScores = maxThreshold
}
return int64(framework.MaxNodeScore) * (sumScores - minThreshold) / (maxThreshold - minThreshold)
}
// sumImageScores returns the sum of image scores of all the containers that are already on the node.
// Each image receives a raw score of its size, scaled by scaledImageScore. The raw scores are later used to calculate
// the final score. Note that the init containers are not considered for it's rare for users to deploy huge init containers.
func sumImageScores(nodeInfo *framework.NodeInfo, containers []v1.Container, totalNumNodes int) int64 {
var sum int64
for _, container := range containers {
if state, ok := nodeInfo.ImageStates[normalizedImageName(container.Image)]; ok {
sum += scaledImageScore(state, totalNumNodes)
}
}
return sum
}
// scaledImageScore returns an adaptively scaled score for the given state of an image.
// The size of the image is used as the base score, scaled by a factor which considers how much nodes the image has "spread" to.
// This heuristic aims to mitigate the undesirable "node heating problem", i.e., pods get assigned to the same or
// a few nodes due to image locality.
func scaledImageScore(imageState *framework.ImageStateSummary, totalNumNodes int) int64 {
spread := float64(imageState.NumNodes) / float64(totalNumNodes)
return int64(float64(imageState.Size) * spread)
}
// normalizedImageName returns the CRI compliant name for a given image.
// TODO: cover the corner cases of missed matches, e.g,
// 1. Using Docker as runtime and docker.io/library/test:tag in pod spec, but only test:tag will present in node status
// 2. Using the implicit registry, i.e., test:tag or library/test:tag in pod spec but only docker.io/library/test:tag
// in node status; note that if users consistently use one registry format, this should not happen.
func normalizedImageName(name string) string {
if strings.LastIndex(name, ":") <= strings.LastIndex(name, "/") {
name = name + ":latest"
}
return name
}