Job 级别抢占

简介

在大规模集群环境中,高优先级作业(例如关键的 AI 训练任务)在可用资源不足时,通常需要从低优先级工作负载中抢占资源。然而,Kubernetes 中传统的 Pod 级别抢占无法保证分布式作业的所有成员 Pod 一起抢占资源,导致无效抢占。

为了解决这个问题,Koordinator 提供了 Job 级别抢占,它确保:

  • 抢占在作业(GangGroup)级别触发。
  • 只有当所有成员 Pod 在驱逐后都可以共同调度时,才会发生抢占。
  • 通过 nominatedNode 为所有成员预留资源,以维护调度一致性。

此功能与 PodGroup/GangGroup 语义无缝协作。

前置条件

  • Kubernetes >= 1.18
  • Koordinator >= 1.7.0

验证抢占是否已启用

虽然从 Koordinator >= 1.7.0 开始,Job 级别抢占默认启用,但建议确认 Coscheduling 插件配置。

检查调度器配置

检索当前的 koord-scheduler-config:

1. kubectl -n koordinator-system get cm koord-scheduler-config -o yaml

确保 Coscheduling 插件具有 enablePreemption: true:

1. pluginConfig:
2. - name: Coscheduling
3. args:
4. apiVersion: kubescheduler.config.k8s.io/v1
5. kind: CoschedulingArgs
6. enablePreemption: true

如果进行了更改,请重启 koord-scheduler Pod 以应用它们。

使用示例

环境设置

为了演示 Job 级别抢占,我们将模拟一个资源受限的环境,并从高优先级作业触发抢占。假设集群有 2 个工作节点,每个节点具有:

  • CPU: 4 核心
  • 内存: 16 GiB
  • 初始时没有其他运行的工作负载

我们的步骤是:

  1. 用低优先级 Pod 填充两个节点,消耗所有 CPU。
  2. 提交一个无法容纳的高优先级 Gang 作业。
  3. 观察 Koordinator 如何驱逐低优先级 Pod 以腾出空间。

定义 PriorityClass

  1. 您必须定义优先级类以启用抢占逻辑。
1. # 高优先级类(用于抢占者)
2. apiVersion: scheduling.k8s.io/v1
3. kind: PriorityClass
4. metadata:
5. name: high-priority
6. value: 1000000
7. preemptionPolicy: PreemptLowerPriority
8. description: "用于可以抢占其他作业的关键 AI 训练作业。"
1. # 低优先级类(用于受害者)
2. apiVersion: scheduling.k8s.io/v1
3. kind: PriorityClass
4. metadata:
5. name: low-priority
6. value: 1000
7. preemptionPolicy: PreemptLowerPriority
8. globalDefault: false
9. description: "用于可以被抢占的非关键作业。"
  1. 应用它们
1. kubectl apply -f priorityclasses.yaml
  1. 验证
1. kubectl get priorityclass
1. NAME              VALUE        GLOBAL-DEFAULT   AGE
2. high-priority     1000000      false            1m
3. low-priority      1000         false            1m

部署低优先级 Pod 以消耗资源

  1. 创建 2 个低优先级 Pod(每个节点 1 个),每个请求 4 个 CPU 核心 → 完全占用两个节点。
1. apiVersion: v1
2. kind: Pod
3. metadata:
4. name: lp-pod-1
5. namespace: default
6. spec:
7. schedulerName: koord-scheduler
8. priorityClassName: low-priority
9. containers:
10. - command:
11. - sleep
12. - 365d
13. image: busybox
14. imagePullPolicy: IfNotPresent
15. name: curlimage
16. resources:
17. limits:
18. cpu: 4
19. memory: 40Mi
20. requests:
21. cpu: 4
22. memory: 40Mi
23. terminationMessagePath: /dev/termination-log
24. terminationMessagePolicy: File
25. restartPolicy: Always
26. ---
27. apiVersion: v1
28. kind: Pod
29. metadata:
30. name: lp-pod-2
31. namespace: default
32. spec:
33. schedulerName: koord-scheduler
34. priorityClassName: low-priority
35. containers:
36. - command:
37. - sleep
38. - 365d
39. image: busybox
40. imagePullPolicy: IfNotPresent
41. name: curlimage
42. resources:
43. limits:
44. cpu: 4
45. memory: 40Mi
46. requests:
47. cpu: 4
48. memory: 40Mi
49. terminationMessagePath: /dev/termination-log
50. terminationMessagePolicy: File
51. restartPolicy: Always
  1. 应用它们
1. kubectl apply -f low-priority-pods.yaml
  1. 检查
1. kubectl get pods -o wide
1. NAME        READY   STATUS    RESTARTS   AGE     IP            NODE.          NOMINATED NODE   READINESS GATES
2. lp-pod-1    1/1     Running   0          2m      10.244.1.10   cn-beijing.1   <none>           <none>
3. lp-pod-2    1/1     Running   0          2m      10.244.1.11   cn-beijing.2   <none>           <none>

此时,两个节点上都没有可用的 CPU。

创建高优先级 Gang 作业以触发抢占

  1. 现在提交一个 2 Pod 的高优先级作业,每个 Pod 需要 3 个 CPU — 总需求超过当前容量。
1. apiVersion: scheduling.sigs.k8s.io/v1alpha1
2. kind: PodGroup
3. metadata:
4. name: hp-training-job
5. namespace: default
6. spec:
7. minMember: 2
8. scheduleTimeoutSeconds: 300
1. apiVersion: v1
2. kind: Pod
3. metadata:
4. name: hp-worker-1
5. namespace: default
6. labels:
7. pod-group.scheduling.sigs.k8s.io: hp-training-job
8. spec:
9. schedulerName: koord-scheduler
10. priorityClassName: high-priority
11. preemptionPolicy: PreemptLowerPriority
12. containers:
13. - command:
14. - sleep
15. - 365d
16. image: busybox
17. imagePullPolicy: IfNotPresent
18. name: curlimage
19. resources:
20. limits:
21. cpu: 3
22. memory: 40Mi
23. requests:
24. cpu: 3
25. memory: 40Mi
26. terminationMessagePath: /dev/termination-log
27. terminationMessagePolicy: File
28. restartPolicy: Always
29. ---
30. apiVersion: v1
31. kind: Pod
32. metadata:
33. name: hp-worker-2
34. namespace: default
35. labels:
36. pod-group.scheduling.sigs.k8s.io: hp-training-job
37. spec:
38. schedulerName: koord-scheduler
39. priorityClassName: high-priority
40. preemptionPolicy: PreemptLowerPriority
41. containers:
42. - command:
43. - sleep
44. - 365d
45. image: busybox
46. imagePullPolicy: IfNotPresent
47. name: curlimage
48. resources:
49. limits:
50. cpu: 3
51. memory: 40Mi
52. requests:
53. cpu: 3
54. memory: 40Mi
55. terminationMessagePath: /dev/termination-log
56. terminationMessagePolicy: File
57. restartPolicy: Always
  1. 应用它们
1. kubectl apply -f high-priority-job.yaml

几秒钟后,Koordinator 将驱逐每个节点上的一个 Pod 以释放资源。

验证抢占结果

  1. 检查受害 Pod 是否被驱逐
1. kubectl get pods -o wide
1. NAME           READY   STATUS        RESTARTS   AGE     IP            NODE           NOMINATED NODE   READINESS GATES
2. hp-worker-1    0/1     Pending       0          90s     <none>        <none>         cn-beijing.1     <none>
3. hp-worker-2    0/1     Pending       0          90s     <none>        <none>         cn-beijing.2     <none>
4. lp-pod-1       0/1     Terminating   0          5m      10.244.1.10   cn-beijing.1   <none>           <none>
5. lp-pod-2       1/1     Terminating   0          5m      10.244.1.11   cn-beijing.2   <none>           <none>

Pod lp-pod-1 和 lp-pod-2 正在被终止以腾出空间,高优先级 Pod 已被提名。

  1. 检查一个受害者:
1. kubectl get pod lp-pod-1 -o yaml
1. status:
2. conditions:
3. - type: DisruptionTarget
4. status: "True"
5. lastTransitionTime: "2025-09-17T08:41:35Z"
6. message: 'koord-scheduler: preempting to accommodate higher priority pods, preemptor:
7. default/hp-training-job, triggerpod: default/hp-worker-1'
8. reason: PreemptionByScheduler
  1. 确认终止后的绑定
1. kubectl get pods -o wide
1. NAME           READY   STATUS    RESTARTS   AGE     IP            NODE           NOMINATED NODE   READINESS GATES
2. hp-worker-1    1/1     Running   0          3m      10.244.1.14   cn-beijing.1   <none>           <none>
3. hp-worker-2    1/1     Running   0          3m      10.244.2.15   cn-beijing.2   <none>           <none>