资源关卡 — 由上报、请求规则、污点分出来的 Pending
目标
完整经历 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/。
步骤
- 创建
gpu-whynamespace,并为lab-node-0添加四个 discovery label。 - 在该节点的 status 中宣告
nvidia.com/gpu: 2,并保存到out/advertised.txt。 - 创建两个违反规则的 manifest,并将拒绝消息保存到
out/rejected.txt。 - 不加任何额外条件创建
trainerPod,观察资源如何独自决定节点。 - 为节点添加 taint,并把
cpu-only等待的原因保存到out/tainted.txt。 - 使用带 toleration 的
trainer2同时通过两道 gate。 - 用
trainer3占用超出剩余容量的位置,观察等待原因转为资源不足,并保存到out/exhausted.txt。 - 在
out/gate-report.txt中用七行总结。
参考
- status 是 subresource。使用
kubectl patch node <이름> --subresource=status,并在 JSON pointer 中把斜线转义为~1。 - 如果只填写
capacity而遗漏allocatable,scheduler 不会获得可用容量。这是常见错误;若第 3 步之后 Pod 仍持续等待,请先检查这里。 - 第 5 步和第 7 步都处于 Pending。请并排比较两个文件中的消息差异;这一区别会直接决定下一步查看哪里。
- 不要通过删除 taint 来让 Pod 通过,否则保留 GPU 节点的初衷就消失了。
为 GPU 节点添加 discovery label
创建 gpu-why namespace,并为 lab-node-0 添加四个 label:nvidia.com/gpu.present=true、nvidia.com/gpu.product=NVIDIA-A100-SXM4-40GB、nvidia.com/gpu.count=2、nvidia.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.yaml 在 gpu-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 的 key、value、effect 必须与节点 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 中写七行:ALLOCATABLE、USED、PENDING、TAINT、REQUESTS_EQUAL_LIMITS、FRACTIONAL、SCHEDULER_SEES。
三个数字必须从集群统计,不要猜测。USED 是分配到 lab-node-0 的 Pod 所有 nvidia.com/gpu request 之和;PENDING 是 gpu-why namespace 中处于等待状态的 Pod 数。TAINT 按 키=값:효과 形式填写第 5 步添加的内容;其余三项用一个单词写出第 3、4 步确认的规则。SCHEDULER_SEES 要回答 scheduler 看到的是设备还是数字。