K8s Scheduler 调度 Pod

6 阅读2分钟

一句话先给你一个“不模糊”的定义

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