LabHub
学习 学习路径 课程

GPU Operator 与时间片

资源关卡 — 由上报、请求规则、污点分出来的 Pending

在 LabHub 中继续学习

目标

完整经历 GPU Pod 必须通过的两道 gate 中的资源侧。让节点宣告资源,观察 extended resource 的两条 request 规则如何真正阻止错误配置,并学会区分同样是 Pending 的状态究竟源于 taint 还是资源。

为什么重要

收到“GPU Pod 无法启动”的报告后,如果最初 3 分钟无法确定排查分支,就会在错误方向上浪费数小时。若 Pod 甚至未被分配到节点,问题就在 scheduler 阶段;scheduler 看到的不是设备,而是 node status 中记录的数字。device plugin 负责写入这个数字。若数字不存在,即使设备完好地插在机器上,Pod 也会永远等待。

extended resource 还有两条规则:requests 与 limits 必须相等,且值必须是整数。因为它不能 overcommit,也不能切分设备。这两条规则都由 apiserver 验证,违反时 Pod 根本不会被创建。最后,GPU 节点通常带有 taint;即使资源充足,没有 toleration 也无法调度。这三项就是资源 gate 的全部内容。

环境

此 Pod 中没有 GPU 或 device plugin。取而代之的是 kwok 启动的真实 control plane:只要在 node status 中写入数字,真实 scheduler 就会据此判断;extended resource 的验证也由真实 apiserver 完成。工作目录为 /root/gpuwhy,manifest 放在 k8s/,产物放在 out/

步骤

  1. 创建 gpu-why namespace,并为 lab-node-0 添加四个 discovery label。
  2. 在该节点的 status 中宣告 nvidia.com/gpu: 2,并保存到 out/advertised.txt
  3. 创建两个违反规则的 manifest,并将拒绝消息保存到 out/rejected.txt
  4. 不加任何额外条件创建 trainer Pod,观察资源如何独自决定节点。
  5. 为节点添加 taint,并把 cpu-only 等待的原因保存到 out/tainted.txt
  6. 使用带 toleration 的 trainer2 同时通过两道 gate。
  7. trainer3 占用超出剩余容量的位置,观察等待原因转为资源不足,并保存到 out/exhausted.txt
  8. out/gate-report.txt 中用七行总结。

参考

为 GPU 节点添加 discovery label

创建 gpu-why namespace,并为 lab-node-0 添加四个 label:nvidia.com/gpu.present=truenvidia.com/gpu.product=NVIDIA-A100-SXM4-40GBnvidia.com/gpu.count=2nvidia.com/gpu.deploy.device-plugin=true。不要添加到另外两个节点。

在真实集群中,gpu-feature-discovery DaemonSet 会读取设备并添加这些 label。operator 通过 nvidia.com/gpu.deploy.* label 控制哪些节点运行哪些 DaemonSet。本步骤只手工制造该结果。使用 kubectl label node <이름> <키>=<값> --overwrite,再用 kubectl get node --show-labels 检查。

在 node status 中宣告 extended resource

lab-node-0 的 status 中把 nvidia.com/gpu 写为 2。必须同时写入 capacity 和 allocatable。然后将三个节点的宣告量保存到 /root/gpuwhy/out/advertised.txt

extended resource 不是 kubelet 自行统计的值,而是 device plugin 告知后写入 node status 的数字;这里直接写入该位置。status 是独立 subresource,因此使用 kubectl patch node <이름> --subresource=status --type=json -p '[...]' 修改。JSON pointer 中的斜线必须转义为 ~1,所以路径是 /status/capacity/nvidia.com~1gpu。若只写 capacity,scheduler 不会获得可用容量。

确认 extended resource 的两条规则会阻止错误

创建两个会被拒绝的 Pod manifest。/root/gpuwhy/k8s/bad-mismatch.yaml 中让 nvidia.com/gpu 的 requests 与 limits 不同;/root/gpuwhy/k8s/bad-fraction.yaml 中使用小数 request。尝试应用二者,并将拒绝消息保存到 /root/gpuwhy/out/rejected.txt

extended resource 不允许 overcommit,因此 requests 与 limits 必须相等,并且值必须为整数,因为设备按不可切分的单位分配。两条规则都由 apiserver 验证,所以 Pod 根本不会被创建。命令失败才是正确结果;消息写到标准错误,因此需要用 2>&1 捕获后才能保存到文件。

即使不指定条件,资源也会决定节点

使用 /root/gpuwhy/k8s/trainer.yamlgpu-why namespace 创建 Pod trainer。把 nvidia.com/gpu 只在 limits 中写为 1,不要添加 nodeSelector

extended resource 只写 limits 时,requests 会自动填充为相同值。创建后,用 kubectl -n gpu-why get pod trainer -o yaml 亲自确认 requests 已填充。没有指定节点却会去宣告 GPU 的节点,这正是本步骤的重点:scheduler 看到的是数字,而不是设备。

即使资源充足,taint 也会阻止调度

lab-node-0 上添加 nvidia.com/gpu=present:NoSchedule taint,并使用 /root/gpuwhy/k8s/cpu-only.yaml 创建不使用 GPU 的 Pod cpu-only。该 Pod 必须通过 nvidia.com/gpu.present: "true" nodeSelector 指向 GPU 节点。进入等待状态后,将 scheduling condition 的消息保存到 /root/gpuwhy/out/tainted.txt

GPU 节点成本高昂;若普通工作负载占满 CPU 容量,真正的 GPU Pod 就没有位置。taint 用于把该节点保留给 GPU 工作负载。使用 kubectl taint node <이름> <키>=<값>:<효과> 添加;等待原因可通过 kubectl -n gpu-why get pod cpu-only -o jsonpath='{.status.conditions[?(@.type=="PodScheduled")].message}' 提取。还要观察已经运行的 Pod 不会被 NoSchedule 驱逐。

添加 toleration,同时通过两道 gate

使用 /root/gpuwhy/k8s/trainer2.yaml 创建 Pod trainer2。同时加入可容忍前述 taint 的 toleration,以及 1 张 nvidia.com/gpu request。不要删除 taint。

toleration 的 keyvalueeffect 必须与节点 taint 匹配。检查点是:在同一节点上,cpu-only 继续等待,只有 trainer2 能进入。若删除 taint 来通过,就失去了保留 GPU 节点的意义。

容量耗尽后,相同 Pod 也会等待

使用 /root/gpuwhy/k8s/trainer3.yaml 再创建一个与 trainer2 完全相同的 Pod trainer3。进入等待状态后,将 scheduling condition 的消息保存到 /root/gpuwhy/out/exhausted.txt

节点宣告了 2 个位置,前两个 Pod 已全部占用。必须保留 toleration,才能表明本次等待原因不是 taint,而是资源。确认 scheduler 消息变为 Insufficient nvidia.com/gpu。同样是 Pending,原因不同,排查位置也不同。

用数字和一个单词总结 gate

/root/gpuwhy/out/gate-report.txt 中写七行:ALLOCATABLEUSEDPENDINGTAINTREQUESTS_EQUAL_LIMITSFRACTIONALSCHEDULER_SEES

三个数字必须从集群统计,不要猜测。USED 是分配到 lab-node-0 的 Pod 所有 nvidia.com/gpu request 之和;PENDINGgpu-why namespace 中处于等待状态的 Pod 数。TAINT키=값:효과 形式填写第 5 步添加的内容;其余三项用一个单词写出第 3、4 步确认的规则。SCHEDULER_SEES 要回答 scheduler 看到的是设备还是数字。