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
- 初始时没有其他运行的工作负载
我们的步骤是:
- 用低优先级 Pod 填充两个节点,消耗所有 CPU。
- 提交一个无法容纳的高优先级 Gang 作业。
- 观察 Koordinator 如何驱逐低优先级 Pod 以腾出空间。
定义 PriorityClass
- 您必须定义优先级类以启用抢占逻辑。
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. kubectl apply -f priorityclasses.yaml
- 验证
1. kubectl get priorityclass
1. NAME VALUE GLOBAL-DEFAULT AGE
2. high-priority 1000000 false 1m
3. low-priority 1000 false 1m
部署低优先级 Pod 以消耗资源
- 创建 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. kubectl apply -f low-priority-pods.yaml
- 检查
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 作业以触发抢占
- 现在提交一个 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. kubectl apply -f high-priority-job.yaml
几秒钟后,Koordinator 将驱逐每个节点上的一个 Pod 以释放资源。
验证抢占结果
- 检查受害 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. 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. 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>
