◈ GPU WORKBENCH 系统学习云计算
返回工作台 →

LEARN BY RUNNING

先运行现象,再解释原因

目标是教会你读懂、修改和排查完整系统。完成阅读不等于掌握;每课用实验和复述检验。

本机模式:真实 Kubernetes、真实 Koordinator、真实 CPU 计算;GPU 设备与显存限制为模拟。HAMi-core 的 CUDA 拦截、显存与算力隔离尚未实测。
01 · 三个账本,为什么不能混为一谈?

团队配额回答“你最多能申请多少”;设备分配回答“这张卡已经分给谁”;实际使用回答“进程此刻用了多少”。前两者是预算,第三个必须采集。

实验:提交 1024 MiB、25% 的任务。观察团队预留增加,再打开详情查看分配注解。页面的真实 GPU 使用量仍应为未知。

阅读:internal/workbench/store.go → Submit / Quota;kube.go → Observe。

检验:团队尚有 10 GiB 配额,为什么申请 9 GiB 仍可能无法运行?请分别从团队额度、单卡容量和碎片解释。

02 · 为什么先扣额度,再创建 Job?

两个请求同时读取剩余额度后再扣减,会同时认为容量足够。SQLite 的即时事务把额度检查、任务记录和 reservation 写入变成一个原子动作。

运行 scripts/go-local.sh test ./internal/workbench -run TestStoreAtomicQuota -v。30 个并发请求竞争只能容纳 2 个任务的额度,必须恰好接收 2 个。

幂等键标识一次提交:相同键、相同参数返回同一任务;同一键修改参数返回 409。

检验:客户端收到超时,可以直接换一个幂等键重试吗?用两次 HTTP 请求说明原因。

03 · 真实 Koordinator 如何分配模拟 GPU?

设备发布脚本写入两张 8 GiB 的 Device CR。koord-manager 将资源同步到 Node;koord-scheduler 的 DeviceShare 插件维护每卡分配,选出满足请求的节点和设备,再把结果写入 Pod 注解。

模拟 Device CR → Node 资源总量
Job → Pod → koord-scheduler / DeviceShare
→ scheduling.koordinator.sh/device-allocated
→ kubelet → 真实 CPU 工作负载

查看 kubectl get devices colima-gwb -o yaml 和 kubectl -n gpu-workbench get pods -o yaml,确认 schedulerName 与实际分配。

检验:为什么不能只看 Node 的 GPU 总余量?哪段数据能说明分到了第几张卡?

04 · HAMi-core 在哪里?模拟器替代了什么?

真实方案中,Koordinator 负责分配;koordlet 的运行时 Hook 根据分配注解与隔离标签注入设备、环境变量和 libvgpu.so。HAMi-core 在应用进程中拦截受支持的 CUDA 调用,执行相应限制。

本机没有 NVIDIA GPU。模拟 worker 从真实调度注解读取预算,以 Python 账本拒绝超额分配,并执行真实 CPU 矩阵运算。它没有运行 CUDA,也没有加载 HAMi-core。

实验:选择“显存超限实验”。worker 尝试申请“预算 + 128 MiB”,应以退出码 42 结束,日志明确记录 simulated_limit_rejected。

检验:为什么看到分配注解,甚至看到 LD_PRELOAD,都不能证明真实 GPU 隔离有效?真实环境还需要检查进程映射和超限调用结果。

05 · 创建超时、取消和重启为什么难?

HTTP 超时可能发生在后端已经成功创建之后。Controller 使用确定性 Job 名,先观察再创建,未知时保留额度。

取消先写入持久化意图,然后暂停 Job;只有确认关联 Pod 已停止才释放。暂停对象作为“取消标记”保留,阻止迟到的创建重试再次启动任务。

运行 scripts/go-local.sh test ./internal/workbench -run 'TestController|TestKubeCancel' -v。再提交长任务,重启平台进程,检查 Job UID 没有变化。

检验:Pod 显示 Terminating、集群 API 不可达、Job 已消失但 Pod 仍运行,这三种情况能否释放额度?

06 · 资源碎片:总量够,为什么仍然 Pending?

两张卡各有 8192 MiB。各安排一个 5120 MiB、60% 的任务后,每卡只剩 3072 MiB。总余量 6144 MiB,却没有任何一张卡能接受 4096 MiB 的单卡请求。

实验:提交两个 5120 MiB / 60% / 120 秒任务;等二者运行后,再提交一个 4096 MiB / 25% 任务。查看 Pending 原因;取消一个大任务后,观察等待任务进入运行。

该现象验证真实 DeviceShare 调度对模拟容量的处理,不提供真实 GPU 性能结论。

检验:增加团队配额能解决这个问题吗?改成多卡请求会不会改变应用所需的并行计算方式?

07 · 主机混部与 GPU 干扰分开看

LS / BE 描述 Koordinator 的主机资源治理。配套 ClusterColocationProfile 只匹配本实验命名空间中的 BE 任务,将资源模型转换为 batch CPU / memory,并设置 koord-batch 优先级。

实验:分别提交 LS 与 BE 任务,比较最终 Pod 的 requests、limits、priorityClassName 和 QoS 标签,再查看 NodeMetric 与 NodeSLO。

本机可验证真实 CPU/内存资源模型及任务运行;是否降低在线 P99,必须运行固定负载、采集原始延迟,再对照比较。模拟 GPU 的计算份额只是 CPU 占空比演示。

检验:给 Pod 加上 LS 标签,为什么不能保证同卡 GPU 上的 P99?

08 · 把现象变成证据

每次实验记录:模式、版本、参数、操作时间、任务 ID、Pod UID、分配注解、日志、最终状态和额度回收。证据保存在项目的 .runtime/evidence/。

依次完成:幂等提交 → 同卡共享 → 超限失败 → 容量不足 → 取消回收 → 重启恢复 → 碎片解除 → 设备健康变化。

完成标准:你能画出调用链,指出每层的源码位置,解释一个失败现场,并修改参数预测结果。我们会根据你的实际解释继续补齐薄弱点。

主要源码:store.go(事务)→ controller.go(协调)→ kube.go(执行协议)→ workloads/worker.py(负载与模拟约束)。