配置多个调度器

Kubernetes 默认自带一个调度器,这里对此进行了描述。如果默认调度器不适合你的需求,你可以实现自己的调度器。此外,你甚至可以在默认调度器之外同时运行多个调度器,并指导 Kubernetes 对你的每个 Pod 使用哪个调度器。让我们通过一个例子学习如何在 Kubernetes 中运行多个调度器。

如何实现调度器的详细描述不在本文档范围之内。请参阅 Kubernetes 源码目录中 pkg/scheduler 下的 kube-scheduler 实现,以获取一个规范示例。

开始之前

你需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具已配置为与你的集群通信。建议在至少有两个节点且不充当控制平面主机的集群上运行本教程。如果你还没有集群,可以使用 minikube 创建一个,或者使用这些 Kubernetes 实验场之一:

要检查版本,请输入 kubectl version

打包调度器

将你的调度器二进制文件打包成容器镜像。为了本示例的目的,你可以使用默认调度器 (kube-scheduler) 作为你的第二个调度器。从 GitHub 克隆 Kubernetes 源代码 并构建源文件。

git clone https://github.com/kubernetes/kubernetes.git
cd kubernetes
make

创建一个包含 kube-scheduler 二进制文件的容器镜像。这是用于构建该镜像的 Dockerfile

FROM busybox
ADD ./_output/local/bin/linux/amd64/kube-scheduler /usr/local/bin/kube-scheduler

将文件保存为 Dockerfile,构建镜像并将其推送到注册表。本示例将镜像推送到 Google Container Registry (GCR)。有关更多详细信息,请阅读 GCR 文档。另外,你也可以使用 Docker Hub。有关更多详细信息,请参阅 Docker Hub 文档

docker build -t gcr.io/my-gcp-project/my-kube-scheduler:1.0 .     # The image name and the repository
gcloud docker -- push gcr.io/my-gcp-project/my-kube-scheduler:1.0 # used in here is just an example

为调度器定义 Kubernetes Deployment

现在你的调度器已打包成容器镜像,为其创建一个 Pod 配置并在 Kubernetes 集群中运行它。但对于本示例,你可以使用 Deployment 而非直接在集群中创建 Pod。 Deployment 管理 ReplicaSet,后者又管理 Pods,从而使调度器具有故障弹性。这是 Deployment 配置。将其保存为 my-scheduler.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-scheduler
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: my-scheduler-as-kube-scheduler
subjects:
- kind: ServiceAccount
  name: my-scheduler
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: system:kube-scheduler
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: my-scheduler-as-volume-scheduler
subjects:
- kind: ServiceAccount
  name: my-scheduler
  namespace: kube-system
roleRef:
  kind: ClusterRole
  name: system:volume-scheduler
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: my-scheduler-extension-apiserver-authentication-reader
  namespace: kube-system
roleRef:
  kind: Role
  name: extension-apiserver-authentication-reader
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
  name: my-scheduler
  namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-scheduler-config
  namespace: kube-system
data:
  my-scheduler-config.yaml: |
    apiVersion: kubescheduler.config.k8s.io/v1beta2
    kind: KubeSchedulerConfiguration
    profiles:
      - schedulerName: my-scheduler
    leaderElection:
      leaderElect: false    
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    component: scheduler
    tier: control-plane
  name: my-scheduler
  namespace: kube-system
spec:
  selector:
    matchLabels:
      component: scheduler
      tier: control-plane
  replicas: 1
  template:
    metadata:
      labels:
        component: scheduler
        tier: control-plane
        version: second
    spec:
      serviceAccountName: my-scheduler
      containers:
      - command:
        - /usr/local/bin/kube-scheduler
        - --config=/etc/kubernetes/my-scheduler/my-scheduler-config.yaml
        image: gcr.io/my-gcp-project/my-kube-scheduler:1.0
        livenessProbe:
          httpGet:
            path: /healthz
            port: 10259
            scheme: HTTPS
          initialDelaySeconds: 15
        name: kube-second-scheduler
        readinessProbe:
          httpGet:
            path: /healthz
            port: 10259
            scheme: HTTPS
        resources:
          requests:
            cpu: '0.1'
        securityContext:
          privileged: false
        volumeMounts:
          - name: config-volume
            mountPath: /etc/kubernetes/my-scheduler
      hostNetwork: false
      hostPID: false
      volumes:
        - name: config-volume
          configMap:
            name: my-scheduler-config

在上述清单中,你使用 KubeSchedulerConfiguration 来定制调度器实现的特性。此配置通过 --config 选项在初始化时传递给 kube-schedulermy-scheduler-config ConfigMap 存储了配置文件。 my-scheduler Deployment 的 Pod 将 my-scheduler-config ConfigMap 挂载为卷。

在上述调度器配置中,你的调度器实现通过一个 KubeSchedulerProfile 来表示。

另外,请注意,你创建了一个专用的 ServiceAccount my-scheduler 并将 ClusterRole system:kube-scheduler 绑定到它,以便它能够获得与 kube-scheduler 相同的权限。

请参阅 kube-scheduler 文档 获取其他命令行参数的详细说明,并参阅 调度器配置参考 获取其他可定制的 kube-scheduler 配置的详细说明。

在集群中运行第二个调度器

为了在 Kubernetes 集群中运行你的调度器,请在 Kubernetes 集群中创建上述配置中指定的 Deployment

kubectl create -f my-scheduler.yaml

验证调度器 Pod 正在运行

kubectl get pods --namespace=kube-system
NAME                                           READY     STATUS    RESTARTS   AGE
....
my-scheduler-lnf4s-4744f                       1/1       Running   0          2m
...

在此列表中,除了默认的 kube-scheduler Pod 之外,你应该看到一个状态为 “Running” 的 my-scheduler Pod。

启用领导者选举

要运行启用领导者选举的多调度器,必须执行以下操作:

更新 YAML 文件中 my-scheduler-config ConfigMap 中 KubeSchedulerConfiguration 的以下字段:

  • leaderElection.leaderElect 设置为 true
  • leaderElection.resourceNamespace 设置为 <lock-object-namespace>
  • leaderElection.resourceName 设置为 <lock-object-name>

如果你的集群上启用了 RBAC,则必须更新 system:kube-scheduler 集群角色。将你的调度器名称添加到应用于 endpointsleases 资源的规则的 resourceNames 中,如下例所示:

kubectl edit clusterrole system:kube-scheduler
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-scheduler
rules:
  - apiGroups:
      - coordination.k8s.io
    resources:
      - leases
    verbs:
      - create
  - apiGroups:
      - coordination.k8s.io
    resourceNames:
      - kube-scheduler
      - my-scheduler
    resources:
      - leases
    verbs:
      - get
      - update
  - apiGroups:
      - ""
    resourceNames:
      - kube-scheduler
      - my-scheduler
    resources:
      - endpoints
    verbs:
      - delete
      - get
      - patch
      - update

为 Pod 指定调度器

现在你的第二个调度器正在运行,创建一些 Pods,并指示它们由默认调度器或你部署的调度器进行调度。为了使用特定调度器调度给定的 Pod,请在该 Pod 的规约 (spec) 中指定调度器的名称。让我们看三个例子。

  • 没有指定调度器名称的 Pod 规约

    apiVersion: v1
    kind: Pod
    metadata:
      name: no-annotation
      labels:
        name: multischeduler-example
    spec:
      containers:
      - name: pod-with-no-annotation-container
        image: registry.k8s.io/pause:3.8

    当未提供调度器名称时,Pod 会自动使用默认调度器进行调度。

    将此文件保存为 pod1.yaml 并提交到 Kubernetes 集群。

    kubectl create -f pod1.yaml
    
  • 指定 default-scheduler 的 Pod 规约

    apiVersion: v1
    kind: Pod
    metadata:
      name: annotation-default-scheduler
      labels:
        name: multischeduler-example
    spec:
      schedulerName: default-scheduler
      containers:
      - name: pod-with-default-annotation-container
        image: registry.k8s.io/pause:3.8
    

    通过将调度器名称作为值提供给 spec.schedulerName 来指定调度器。在本例中,我们提供了默认调度器的名称 default-scheduler

    将此文件保存为 pod2.yaml 并提交到 Kubernetes 集群。

    kubectl create -f pod2.yaml
    
  • 指定 my-scheduler 的 Pod 规约

    apiVersion: v1
    kind: Pod
    metadata:
      name: annotation-second-scheduler
      labels:
        name: multischeduler-example
    spec:
      schedulerName: my-scheduler
      containers:
      - name: pod-with-second-annotation-container
        image: registry.k8s.io/pause:3.8
    

    在本例中,我们指定此 Pod 应使用我们部署的调度器 my-scheduler 进行调度。请注意, spec.schedulerName 的值应与 KubeSchedulerProfile 映射中为调度器提供的 schedulerName 字段的名称匹配。

    将此文件保存为 pod3.yaml 并提交到 Kubernetes 集群。

    kubectl create -f pod3.yaml
    

    验证所有三个 Pods 正在运行。

    kubectl get pods
    

验证 Pods 是否使用了期望的调度器进行调度

为了便于理解这些示例,我们并未验证 Pods 是否实际使用了期望的调度器进行调度。我们可以通过更改上述 Pod 和 Deployment 配置的提交顺序来进行验证。如果在提交调度器 Deployment 配置之前将所有 Pod 配置提交到 Kubernetes 集群,我们会看到 Pod annotation-second-scheduler 永远处于“Pending”状态,而其他两个 Pod 则被调度。一旦我们提交了调度器 Deployment 配置并且我们的新调度器开始运行, annotation-second-scheduler Pod 也会被调度。

或者,你可以查看事件日志中的“Scheduled”条目,以验证 Pods 是否由期望的调度器进行调度。

kubectl get events

你还可以通过修改相关控制平面节点上的静态 Pod 清单,为集群的主调度器使用 自定义调度器配置 或自定义容器镜像。

最后修改于 2023 年 12 月 14 日太平洋标准时间上午 9:24: 更新配置 API 的引用 (2fe79a7c28)