Kubernetes的原生调度器(kube-scheduler)功能强大,但在面对特定业务场景,例如强制将某类Pod调度到拥有特定硬件(如FPGA/GPU)的节点组,或者实现复杂的租户隔离计分逻辑时,标准调度策略可能无法满足需求。
解决这类复杂需求的最有效方法是利用Kubernetes的Scheduler Framework机制,编写自定义的调度插件(Plugin)。本文将指导您如何创建一个自定义的计分(Score)插件,以实现节点偏好调度。
1. Scheduler Framework 简介
Scheduler Framework允许开发者通过实现特定的接口,将自定义逻辑注入到调度流程的各个阶段(如QueueSort, Filter, Score, Reserve, Bind等)。我们以最常用的Score Plugin为例,它决定了哪些合格的节点应该被优先选择。
插件目标
我们将创建一个名为 LabelPreference 的计分插件,该插件会检查节点的标签 scheduler-preference: high。如果节点拥有此标签,则给予最高分数,确保该类Pod优先被调度到这些节点上。
2. 编写自定义 Score 插件(Go 语言)
自定义调度器通常需要基于Kubernetes的调度器代码库进行开发。以下是实现核心逻辑的代码片段,您需要使用Go语言环境并引入 k8s.io/kubernetes/pkg/scheduler/framework 包。
package labelpreference
import (
"context"
"fmt"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/scheduler/framework"
)
const (
// PluginName 是插件的唯一标识符
PluginName = "LabelPreference"
// PreferenceLabel 是我们关注的节点标签键
PreferenceLabel = "scheduler-preference"
// HighPreferenceValue 是高优先级的标签值
HighPreferenceValue = "high"
)
// LabelPreference 结构体实现了 ScorePlugin 接口
type LabelPreference struct {
handle framework.Handle
}
// New 初始化插件实例
func New(_ context.Context, obj runtime.Object, handle framework.Handle) (framework.Plugin, error) {
// 可以在这里处理配置参数 (obj)
klog.Info("Initializing LabelPreference plugin")
return &LabelPreference{handle: handle}, nil
}
// Name 返回插件名称
func (lp *LabelPreference) Name() string {
return PluginName
}
// Score 实现了节点的计分逻辑
func (lp *LabelPreference) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
// 从缓存中获取节点信息
nodeInfo, err := lp.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
if err != nil {
return 0, framework.NewStatus(framework.Error, fmt.Sprintf("failed to get node info: %v", err))
}
// 检查节点是否含有我们设定的高优先级标签
if nodeInfo.Node().Labels[PreferenceLabel] == HighPreferenceValue {
klog.V(4).Infof("Node %s has high preference label. Assigning max score.", nodeName)
// 赋予最高分数 (0-framework.MaxNodeScore, 即 10)
return framework.MaxNodeScore, nil
}
// 普通节点得分较低
return 1, nil
}
// ScoreExtensions 必须实现,用于后续的 NormalizeScore 逻辑(这里简化处理)
func (lp *LabelPreference) ScoreExtensions() framework.ScoreExtensions {
return lp
}
// NormalizeScore 可以在所有节点得分后进行归一化处理(可选)
func (lp *LabelPreference) NormalizeScore(
ctx context.Context,
state *framework.CycleState,
pod *v1.Pod,
scores framework.NodeScoreList,
) *framework.Status {
return nil
}
3. 部署自定义调度器
编译上述Go代码并将其打包成一个可执行文件(例如 custom-scheduler)。接下来,需要通过ConfigMap来配置 Kubernetes 告诉调度器加载该插件,并将其作为新的调度配置文件。
步骤 3.1:创建调度器配置 ConfigMap
这个配置将覆盖默认的 Score 阶段,启用我们的 LabelPreference 插件,并禁用某些默认的计分策略(例如 NodeResourcesBalancedAllocation,以确保我们的策略占主导地位)。
apiVersion: v1
kind: ConfigMap
metadata:
name: custom-scheduler-config
namespace: kube-system
labels:
app: custom-scheduler
data:
scheduler-config.yaml: |
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: custom-scheduler
plugins:
score:
# 启用自定义插件
enabled:
- name: LabelPreference
weight: 3 # 赋予更高的权重,确保其影响力更大
# 禁用某些默认插件,避免干扰
disabled:
- name: NodeResourcesBalancedAllocation
- name: ImageLocality
# 如果插件需要参数,可以在这里配置
pluginConfig:
- name: LabelPreference
args: {}
步骤 3.2:部署自定义调度器
将自定义的可执行文件部署为一个Deployment,并使用上一步创建的ConfigMap作为其启动配置。
apiVersion: apps/v1
kind: Deployment
metadata:
name: custom-scheduler
namespace: kube-system
labels:
app: custom-scheduler
spec:
replicas: 1
selector:
matchLabels:
app: custom-scheduler
template:
metadata:
labels:
app: custom-scheduler
spec:
serviceAccountName: kube-scheduler
containers:
- name: scheduler
# 替换为您的自定义镜像路径
image: your-registry.com/custom-scheduler:v1.0.0
args:
- --config=/etc/kubernetes/scheduler-config.yaml
volumeMounts:
- name: scheduler-config
mountPath: /etc/kubernetes
volumes:
- name: scheduler-config
configMap:
name: custom-scheduler-config
步骤 3.3:使用自定义调度器
现在,当您创建一个Pod时,只需要在Pod定义中指定 schedulerName 即可使用您新的调度逻辑:
apiVersion: v1
kind: Pod
metadata:
name: highly-preferred-pod
spec:
schedulerName: custom-scheduler # 关键!
containers:
- name: my-app
image: nginx
通过这种方式,您可以将复杂的业务逻辑(如地理位置偏好、特定租户隔离、动态负载均衡调整)集成到 Kubernetes 调度循环中,实现对集群资源分配的深度控制。
汤不热吧