一句话先给你一个“不模糊”的定义
Kubernetes 中的“调度(Scheduling)”,指的是:
👉 为一个尚未绑定节点的 Pod,选择一个具体的 Node,并把这个选择结果写回 Pod 的 spec.nodeName 字段。
就这么朴素、具体。
二、调度前 Pod 处于什么状态?
当 KubeRay Operator 创建一个 Ray Worker Pod 时:
spec:
nodeName: ""
Pod 的状态是:
Pending(未绑定 Node)
👉 这一步Pod 还没落地到任何机器上。
三、Scheduler “干的第一件事”:发现一个未调度 Pod
1️⃣ Scheduler Watch Pod 事件
K8s Scheduler 一直在做:
watch 所有 nodeName 为空的 Pod
当它看到:
resources:
requests:
hami.io/gpu: 1
就知道:
这是一个需要 GPU 的 Pod
四、Scheduler 的核心工作流程(真正的“调度”)
2️⃣ 调度 = 两个阶段(非常关键)
Filtering(过滤)
↓
Scoring(打分)
我们分别看。
五、Filtering:哪些 Node “有资格”?
Scheduler 会遍历所有 Node,逐个问:
3️⃣ Node 必须同时满足这些条件
以你的 Pod 为例:
✅ 资源条件
Node.allocatable.hami.io/gpu ≥ 1
这是 HAMi Device Plugin 上报的。
✅ 节点状态
Ready = True
✅ 亲和 / 反亲和(如果有)
nodeSelector
nodeAffinity
✅ Taint / Toleration
GPU Node 通常有:
taint: gpu=true:NoSchedule
Pod 必须 toleration。
过滤完后,得到一个候选集:
Candidate Nodes = [Node A, Node C]
六、Scoring:选“最合适”的那一个
4️⃣ 对候选 Node 打分
Scheduler 会用一组打分插件,例如:
- LeastAllocated
- MostAllocated
- NodeResourcesFit
- 自定义插件(部分厂商)
比如:
Node A:GPU 剩余 3
Node C:GPU 剩余 1
可能会倾向:
- 让资源更均匀
- 或者优先填满某些节点
最终:
选中 Node A
七、调度真正“落地”的瞬间
5️⃣ Scheduler 写回 Pod.spec.nodeName
这是最关键的一步:
spec:
nodeName: node-a
⚠️ 到这里为止:
调度就已经完成了
八、调度完成 ≠ Pod 立刻运行
6️⃣ 接下来是 Kubelet 的事情
在 Node A 上:
kubelet watch 到:
有 Pod.spec.nodeName = 自己
于是:
- 创建 Container
- 调 Device Plugin(HAMi)
- 注入 CUDA_VISIBLE_DEVICES
- 拉镜像 / 启容器
Kubernetes Scheduler 在发现未绑定节点的 Ray Worker Pod 后,根据 Pod 的资源请求(如 hami.io/gpu)、节点可分配资源及调度策略,对集群内节点进行过滤和打分,最终选择一个满足条件的节点,并将该节点名称写入 Pod 的 spec.nodeName 字段,从而完成调度绑定。
九、把 Ray / HAMi / K8s 的角色再一次“对齐”
| 组件 | 做什么 |
|---|---|
| Ray Autoscaler | 决定“要不要更多 Worker” |
| KubeRay Operator | 创建 Pod 声明 |
| K8s Scheduler | 决定 Pod 跑在哪台 Node |
| HAMi | 决定 Pod 用哪块 GPU |
| Raylet | 把 GPU 汇报给 Ray |