迁移现有应用
把通过 device plugin 申请整卡的工作负载迁移到 TensorFusion GPU 池,每次只迁几个副本
本文把申请 nvidia.com/gpu(或其他厂商 device plugin 资源)的工作负载迁移到 TensorFusion。稳妥的顺序是:先按整卡迁过来,每次只迁几个副本;确认没问题后,再缩到它实际用到的份额。
迁移有两条路径,每个工作负载选一条。
前置条件
- 已安装 TensorFusion,工作负载所在的 GPU 节点已纳入某个
GPUPool。用kubectl get gpupool,gpunode,gpu确认。 - 工作负载在
resources.limits里通过 device plugin 资源申请 GPU,例如nvidia.com/gpu: 1。 - 工作负载可以重启。迁移会修改 Pod 模板,Pod 会被重建。
逐个迁移
第 1 步:记录当前的资源使用
kubectl get pod <pod-name> -o jsonpath='{range .spec.containers[*]}{.name}{"\t"}{.resources.limits}{"\n"}{end}'
kubectl get gpu -o custom-columns='NAME:.metadata.name,MODEL:.status.gpuModel,CAPACITY:.status.capacity'记下每个 Pod 用几张卡、GPU 型号,以及这个型号的 TFLOPS 和显存容量。再从监控里记下工作负载真实的 GPU 利用率峰值和显存峰值,第 5 步要用。
有些框架会预先占一块显存池,GPU 上报的显存用量可能比模型实际需要的多。遇到这种情况,改用模型大小来估算显存。
第 2 步:按整卡交给 TensorFusion
在 Pod 模板上加标签和 isolation: shared 注解,原有的 nvidia.com/gpu limit 保持不动。
spec:
template:
metadata:
labels:
tensor-fusion.ai/enabled: "true" # 新增
annotations:
tensor-fusion.ai/isolation: "shared" # 新增
tensor-fusion.ai/enabled-replicas: "1" # 新增,见第 3 步
spec:
containers:
- name: app
resources:
limits:
nvidia.com/gpu: 1 # 不变isolation: shared 表示申请的每张 GPU 都整卡分配,不注入限流组件,工作负载的行为和迁移前一样。webhook 从 nvidia.com/gpu limit 读出 GPU 数量,把这个 limit 从容器上去掉,再由 TensorFusion 调度器分配具体的卡。
多容器 Pod 中,只有带 GPU limit 的容器会交给 TensorFusion。
第 3 步:先迁几个副本
tensor-fusion.ai/enabled-replicas 限制 TensorFusion 最多接管这个工作负载的多少个 Pod。其余 Pod 保持原样,继续走 device plugin。
kubectl apply -f <workload>.yaml
kubectl get pod -l <selector> -L tensor-fusion.ai/component被 TensorFusion 接管的 Pod,COMPONENT 一列有值;其余 Pod 这一列为空。
分阶段调大这个数字,例如先 1 个,再到副本数的 25%、50%、100%,每个阶段都检查一遍工作负载。每次修改这个注解都会改动 Pod 模板,因此会触发整个 Deployment 的滚动更新。
灰度期间,走 TensorFusion 的 Pod 和走 device plugin 的 Pod 不应该混用同一批 GPU 节点。把 Helm value controller.nvidiaOperatorProgressiveMigration 设为 true,TensorFusion 调度器就只会把仍然申请原生 GPU 的 Pod 放到没有 TensorFusion 工作负载的节点上。这个开关同样会让 webhook 检查每一个新建的 Pod,注意事项见自动迁移一节的警告。
enabled-replicas 只对有控制器(如 Deployment、StatefulSet)的 Pod 生效。标签 tensor-fusion.ai/enabled 不是 "true" 时,它不起作用。
第 4 步:验证已迁移的 Pod
POD=<a-migrated-pod>
kubectl get pod "$POD" -o jsonpath='{.metadata.annotations.tensor-fusion\.ai/gpu-ids}{"\n"}'
kubectl exec "$POD" -c app -- nvidia-smi
kubectl get gpu -o custom-columns='NAME:.metadata.name,AVAILABLE:.status.available'- Pod 上有
tensor-fusion.ai/gpu-ids注解,列出了分到的 GPU。 - 容器里
nvidia-smi看到的 GPU 数量和迁移前一致。 - 被分配的 GPU,可用 TFLOPS 和显存都为 0。
- 你自己的健康检查和性能测试通过。
全部副本迁移完成并且运行正常后,去掉 enabled-replicas 注解。
第 5 步:缩到实际需要的份额
每个 Pod 仍占一整张卡,还没有省下任何资源。用第 1 步记录的用量,把整卡申请换成 TFLOPS 和显存:
spec:
template:
metadata:
labels:
tensor-fusion.ai/enabled: "true"
annotations:
tensor-fusion.ai/tflops-request: "30"
tensor-fusion.ai/tflops-limit: "60"
tensor-fusion.ai/vram-request: "8Gi"
tensor-fusion.ai/vram-limit: "8Gi"
spec:
containers:
- name: app
resources: {} # 去掉 nvidia.com/gpu去掉 isolation: shared 后,工作负载使用默认的 soft 模式:limit 会被强制执行,多个 Pod 可以共用一张卡。这一步同样可以用 enabled-replicas 灰度。
没法手工估算时,加上 tensor-fusion.ai/autoscale: "true",让 TensorFusion 自己调整。见配置自动扩缩容。
回滚
去掉 tensor-fusion.ai/enabled 标签和所有 tensor-fusion.ai/* 注解;如果删过 nvidia.com/gpu limit,把它加回来。Deployment 会滚动回到走 device plugin 的 Pod。
自动迁移
打开自动迁移后,webhook 会转换每一个申请原生 GPU 资源的新 Pod,工作负载本身不用改。被转换的 Pod 会带上标签 tensor-fusion.ai/enabled: "true";如果它没有隔离模式注解,还会带上 tensor-fusion.ai/isolation: shared。效果与上面的第 2 步相同。
-
在 Helm values 里设置范围,然后升级 release:
dynamicConfig: autoMigration: enable: true scope: includes: namespaceNames: - ml-team-a excludes: podSelector: matchLabels: gpu-migration: skip -
重启要迁移的工作负载,例如
kubectl rollout restart。已有的 Pod 不受影响,只有新建的 Pod 会被转换。 -
按第 4 步验证。
范围的判断规则:
| 配置 | 效果 |
|---|---|
不写 scope | 所有申请原生 GPU 的 Pod 都迁移。 |
excludes | 最先判断。命中的 Pod 一定不迁移。 |
includes | 配置后,只迁移命中的 Pod。 |
includes、excludes 内部 | namespaceNames、namespaceSelector、podSelector 是“或”的关系,命中任意一个即算命中。 |
想让某个 Pod 不被迁移,给它加标签 tensor-fusion.ai/enabled: "false"。
打开自动迁移后,webhook 需要检查集群里创建的每一个 Pod,而不只是带 TensorFusion 标签的 Pod。在默认的 failurePolicy: Fail 下,controller 不可用期间,整个集群的 Pod 创建都会被拒绝。开启前请把 controller 部署为至少 2 个副本。
TensorFusion 文档