拓冰建站拓冰建站
首页 / 资讯中心 / 正文

K8s接入海光DCU跑DeepSeek:整卡直通与vDCU共享调度实践

手里正好有一批海光 DCU 的卡要给算法团队在 Kubernetes 里跑 DeepSeek一开始以为装个驱动、装个 device plugin 就能像 NVIDIA 那样直接用。真上手之后才发现国产加速卡接入云原生平台的链路里有不少坑尤其是整卡能用、共享就废这个问题几乎绕不开。CubeStudio 这层平台帮我省了不少事但理解它背后怎么调度 DCU、怎么把一张卡切成多份才是真正能落地部署 DeepSeek 的关键。这篇文章就把我这次的适配过程完整捋一遍K8s 里为什么不能照搬 GPU 方案、海光 DCU 的软件栈长什么样、整卡接入怎么做、两种 vDCU 虚拟化的原理和差异以及最后怎么在共享卡上把 DeepSeek 推理服务跑起来。如果你也在折腾国产 DCU 上云或者想把已有 DCU 集群按多租户方式开放给团队使用这篇应该能让你少走不少弯路。内容偏实操命令、配置、YAML 我都会贴出来直接照着改就能用。1. 先把需求理清楚DCU 进 K8s 到底要解决哪几件事很多同学一上来就翻 K8s 官方文档找 device plugin 怎么写然后照着 GPU 的套路抄一份。结果卡在第一步NVIDIA 的 device plugin 根本认不到 DCU因为两边的驱动栈、设备文件、健康检查接口完全不是一回事。所以在动手之前我建议先把DCU 要解决哪几件事列清楚。这次对接 CubeStudio本质上要解决三个层面问题。第一层是资源暴露。K8s 调度器本身不知道 DCU 是什么它只知道 CPU 和内存。要让 DCU 被调度必须通过 device plugin 机制把卡上报成一种可量化的扩展资源比如hygon.com/dcu并且告诉 kubelet 这张卡在节点上的设备路径。这个路径通常是/dev/dri/renderD*和/dev/kfd因为海光 DCU 的驱动栈兼容 ROCm设备节点也和 AMD 类似。如果 device plugin 没有把这两个路径都上报并映射进容器应用层连卡都打开不了。第二层是资源调度策略。整卡调度只是最基础的能力把一张卡当作一个不可分割的单元分配给一个 Pod。问题是实际业务里不可能每任务都吃满整卡尤其跑 DeepSeek 推理时一些小模型用 7B、14B 参数规模的蒸馏版本一张卡能塞两三个实例还有富余。如果只能整卡分配卡闲着也是闲着集群利用率上不去这在多租户 AI 平台上是最要命的事。所以必须引入共享调度也就是后面要讲的 vDCU 虚拟化。第三层是平台层的封装。算法工程师不想管什么 device plugin、什么调度策略他们只想要我提交一个任务指定要 0.5 张卡、16GB 显存平台自动给我安排。这个能力单靠 K8s 原生机制做不出来得在平台层通过 CRD、配额、调度器扩展等方式实现。CubeStudio 在这里的位置就是帮你把DCU 资源池管理 任务调度 多租户隔离 推理服务发布整套串起来但底层它调度的还是 K8s 的设备扩展资源。想清楚这三层后面的技术选型就不会乱。整卡接入解决第一层vDCU 共享解决第二层CubeStudio 的工作是让第二层的能力以平台化的方式暴露给用户。我这次实操的目标就是把这套链路完整打通。2. 接入前的环境核对驱动、设备节点和容器运行时缺一不可2.1 DTK 软件栈与驱动版本的事实核对海光 DCU 的软件栈不叫 CUDA叫 DTK全称是 DCU Toolkit。它往下兼容 ROCm 的编程模型也就是说你用 HIP 写的代码理论上能在 DCU 上编译运行。这个理论俩字背后有很多坑后面细说。总之在接入 K8s 之前物理机上必须装好 DTK 和与之匹配的内核驱动。我这次用的环境给大家做个参考操作系统openEuler 22.03 LTS SP3内核 5.10.0 系列DCU 驱动版本DTK 24.04.1具体小版本记不太清了但一定要和 DTK 版本配套宿主机设备节点/dev/kfd、/dev/dri/renderD128到renderD135容器运行时containerd 1.7.xK8s 1.28有一个非常容易踩的坑驱动版本和 DTK 版本不匹配时/dev/kfd虽然存在但调用 HIP API 时要么报HSA_STATUS_ERROR_...要么直接卡死。所以装完驱动后别急着接 K8s先在宿主机上跑一个rocminfo命令检查卡是否被正确识别。注意这里不是指 NVIDIA 的nvidia-smi海光 DCU 对应的是hy-smi或 ROCm 生态的rocm-smi。2.2 容器运行时必须要做的两件事DCU 能正常出设备节点只是第一步容器里要真正用上卡容器运行时必须满足两个条件。第一设备节点要通过 CDI 或者 privileged 容器注入。我建议用 CDI不要用 privileged。原因是安全性vDCU 虚拟化的隔离性本来就比整卡弱如果容器还带 privileged 权限失控风险会成倍放大。CDI 的配置方式是用nvidia-ctk类似的工具生成一个 JSON 文件海光这边有配套的 DCU CDI 生成工具你执行完之后会在/etc/cdi/目录下生成对应设备的声明文件。第二运行时需要挂载 HIP 相关的库目录。很多同学以为自己写的程序是静态编译不需要挂库实际上调用 HIP 运行时库libamdhip64.so的应用非常普遍。如果你的镜像里没有把/opt/dtk下的库打进去容器启动后就会报找不到.so文件的错误。最省事的做法是把宿主机 DTK 安装目录挂载进容器路径一般类似这样/opt/dtk-24.04:/opt/dtk-24.04:ro然后再通过LD_LIBRARY_PATH环境变量指向它。注意这一步必须做因为 K8s 的 device plugin 只管设备文件不管软件库。提示如果发现容器能识别/dev/kfd但程序还是报错先用这个命令验证一下ldd /your/binary | grep amdhip。只要 libamdhip64 没有被正确加载后面跑 DeepSeek 必挂。2.3 CubeStudio 在接入链路里的角色CubeStudio 这一层我把它理解为AI 平台控制面 调度器扩展。它本身不是替代 K8s而是把 K8s 能力包装成更适合算法团队使用的形态。在整卡模式下CubeStudio 做的事情主要是给节点打标签、部署 DCU 的 device plugin、配置资源配额。在共享模式下它会部署 vDCU 相关的调度组件和虚拟化组件。我这次实际用的版本中CubeStudio 提供了一个叫dcu-scheduler-extender的组件它和 K8s 默认调度器配合工作负责处理 vDCU 资源的分配决策。理解 CubeStudio 的架构对排障特别重要。因为当你提交一个需要 vDCU 的任务时请求链路上先走 K8s 默认调度器再走 scheduler extender再回到 kubelet 绑定 Pod最后才是 device plugin 给容器注入设备。任何一个环节出问题现象都是 Pod Pending但根源可能完全不同。3. 整卡直通用一个 device plugin 把 DCU 变成 K8s 可调度资源3.1 设备上报为什么不能照搬 NVIDIA 的方案NVIDIA 的 device plugin 围绕nvidia.com/gpu这套资源名工作它假设的设备文件是/dev/nvidia0、/dev/nvidiactl等。DCU 完全不一样它依赖的是/dev/kfd和/dev/dri/renderD128这类节点。如果直接拿 NVIDIA 的插件改成资源名即便上报成功容器里拿到的设备路径也是错的。所以海光这边的 device plugin 必须做两件事一是扫描/dev/dri/renderD*判断有几张 DCU 卡二是把每张卡映射成一个资源名上报给 kubelet。实际上报的资源名各家叫法不一样我这边用的是hygon.com/dcu这在 K8s 里就是一个 Extended Resource只要名字合法、数值为整数调度器就能处理。Device plugin 部署形态一般做成 DaemonSet每个 DCU 节点上跑一个 Pod。它的核心逻辑是定时检查设备健康状态如果某张卡坏掉比如温度异常、显存错误就把这个设备的资源数量减一防止调度器继续往坏卡上调度任务。3.2 整卡模式下的 Deployment YAML下面这个 YAML 是整卡模式下的标准写法。算法团队申请一张卡调度器看到节点上报了hygon.com/dcu: 8就会把 Pod 调度到有卡且剩余可分配数量 ≥1 的节点上。apiVersion: v1 kind: Pod metadata: name: dcu-integer-test spec: restartPolicy: OnFailure containers: - name: dcu-test image: your-registry/dtk-base:24.04 command: [/bin/bash, -c] args: - | rocm-smi --showmeminfo vram sleep 3600 resources: limits: hygon.com/dcu: 1 env: - name: LD_LIBRARY_PATH value: /opt/dtk/lib:/opt/dtk/lib64 volumeMounts: - name: dtk mountPath: /opt/dtk securityContext: capabilities: add: [SYS_ADMIN] volumes: - name: dtk hostPath: path: /opt/dtk-24.04这里有两个细节需要注意。第一个是securityContext。虽然我前面说别用 privileged但整卡模式下如果没有正确的 rlimit 设置容器内调用 HIP 初始化时会因为无法锁定内存而失败。建议在 Pod 层面增加securityContext中的sysctl配置或者给容器加SYS_ADMINcapability并确保宿主机上/etc/security/limits.conf中 memlock 限制不要设得太低。如果你的集群启用了 PodSecurity Admission可能需要单独为 DCU 工作负载放行一个策略。第二个是资源配额。整卡模式下hygon.com/dcu只能写整数 1、2、3不能写 0.5。如果你尝试写 0.5K8s 调度器直接报错因为 Extended Resource 只支持整数。这就是我前面说的整卡解决不了共享问题。要支持小份额必须走 vDCU。3.3 整卡到共享之间差了一个显存解耦实测下来整卡模式最大的痛点是资源利用率非常难看。我拿 DCU 跑 DeepSeek-R1-Distill-Qwen-7BBF16 精度下模型权重约占 14GB 显存一张 64GB 的卡推理时 KV cache 和激活值全部加起来也就占 30GB 出头剩下 30 多 GB 完全是闲置的。如果每个任务都整卡分配两张卡的利用率加起来可能不到 50%。我当时的想法是想办法把显存切分让 7B 模型和 14B 模型在两张卡上交错部署尽量把显存压满。但在不做虚拟化的情况下两个容器共享一张物理卡会出现一个问题谁先启动谁就占用了所有显存后启动的容器拿到设备节点后显存申请失败直接崩溃。如果没有 vDCU 这层中间件做资源仲裁共享调度就是一句空话。4. 两种 vDCU 虚拟化的实现逻辑与差异对比4.1 vDCU 解决的不是虚拟化而是资源分割先说个容易混淆的概念。vDCU 不是一个类似虚拟机的隔离方案它更接近资源切片代理。它的核心思想是在容器和物理 DCU 之间加一层代理由代理统一管理显存分配、算力调度和任务排队。这个代理怎么实现不同厂商做法不同。我这边接触到 CubeStudio 的实现方式是宿主机上跑一个名为vdcu-proxy的守护进程它通过控制 DTK 为用户态程序提供的显存管理接口拦截容器的显存申请请求按预分配额度做限制。算力方面则是通过时间片轮转和优先级队列实现。这里要强调一个事实vDCU 不是硬件 MIG 那种硬隔离它依赖的是软件层面的管理。所以它的隔离性不如硬件虚拟化但在多租户 AI 平台场景下软件虚拟化带来的灵活性优势非常大——你可以按显存 GB 数任意切分而不是像 MIG 那样只能按固定档位切。4.2 第一种虚拟化显存硬切分型 vDCU这种模式类似给每张卡划出几块固定大小的显存分区。我习惯叫它显存硬切分型。每个 vDCU 实例会独占一个显存区间边界清晰互不干扰。具体到我的环境里CubeStudio 会把一张 64GB 的卡划分成四个 16GB 的 vDCU。容器申请hygon.com/vdcu: 1时实际拿到的是 1 个 16GB 的 vDCU 份额显存上限就是 16GB超出就报 OOM。这种模式的优点是显存隔离强一个任务 OOM 不会影响同卡上的其他任务资源规格清晰配额管理简单缺点也很明显算力没有隔离如果同一个物理卡上有一个任务在疯狂执行大矩阵乘其他任务的算子执行会被挤得很慢显存碎片问题如果切分粒度太大小任务不好分配4.3 第二种虚拟化算力调度型 vDCU算力调度型 vDCU 解决的是显存硬切分模式下算力打架的问题。它的做法是不限制显存上限而是限制任务的算力份额。在实际实现中vdcu-proxy 会把物理卡的全部算力定义为一个总配额然后按比例分配给不同的容器。比如一个任务占用 25% 算力那它拿到的执行名额和调度权重就只有四分之一其他时间要等待。显存方面可以设置软上限只要物理显存够临时借用一部分也是允许的超过整体显存会触发回收或排队。用我自己的话总结两种模式的体验对比项显存硬切分型 vDCU算力调度型 vDCU资源单元按显存 GB 数切分按百分比切分算力显存隔离强隔离边界硬性限制弱隔离允许突发借用算力隔离无同卡任务互相影响有按权重分配时间片适用场景多模型隔离部署、明确显存需求的推理任务交互式开发、训练调试、负载波动明显的场景复杂度低底层实现简单高需要代理层对算子执行做注入和调度4.4 两种 vDCU 怎么选我的判断标准我的建议是如果是生产环境的推理服务优先选显存硬切分型如果是开发测试或训练调参选算力调度型。原因不复杂推理服务对稳定性要求高交到一个任务手里的显存必须绝对有保证否则别人的任务 OOM 把你的推理进程也带崩这个事故谁都不想背。而训练调试任务的特点是显存使用波动大一个 batch 可能直接用满下一个 batch 又释放很多如果卡在固定显存分片里经常出现明明总内存够但当前 slice 不够的尴尬反而降低 GPU 利用率。CubeStudio 里这两种 vDCU 是可以共存的。我在平台上创建了两个资源池一个叫vdcu-mem-pool使用显存硬切分型一个叫vdcu-sched-pool使用算力调度型。用户提交任务时按需求选择资源池就行。这一点对平台运营方来说非常实用不用为所有业务统一一种策略。5. 用 vDCU 给 DeepSeek 做推理从显存规划到工作负载 YAML5.1 模型选型与显存规划这次要部署的是 DeepSeek 系列蒸馏模型。我选了 DeepSeek-R1-Distill-Qwen-14B精度用 BF16推理框架是 vLLM海光 DCU 有对应的 vLLM-DTK 适配版本。先做一道简单的显存估算题这块建议每个做部署的人都自己算一遍不要盲目照抄别人的配置。模型参数14B 个参数精度BF16每个参数占 2 字节权重显存 14 × 10^9 × 2 / 1024^3 ≈ 26.08 GB算上运行时内部表示的额外开销大概 28-30 GBKV cache 大小取决于并发数和序列长度。假设并发 8单序列上下文长度 819214B 模型每层的 KV cache 大约为 2 × 层数 × 注意力头维度 × 8 × 8192 × 2 字节不同模型架构差异较大粗略估算需要 8-12 GB激活值与其他临时缓冲区2-4 GB总计大约是 40-44 GB。一张 64GB 的物理卡分成两个 30GB 的 vDCU 后单卡只放一个 14B 推理实例比较富余但如果你把它切成两个 30GB 的 vDCU第二个 vDCU 还有足够空间放一个量化到 4bit 的 7B 模型。这就是共享调度带来的实际收益。5.2 在共享池上创建 Deployment下面是一个申请算力调度型 vDCU 的 Deployment 示例。我在资源请求里写hygon.com/vdcu-sched: 25意思是申请 25% 的算力份额并且它的显存上限是 0由 vDCU 池默认值决定这个需要根据 CubeStudio 实际定义的资源名调整。apiVersion: apps/v1 kind: Deployment metadata: name: deepseek-r1-distill-14b namespace: ai-inference labels: app: deepseek-inference spec: replicas: 1 selector: matchLabels: app: deepseek-inference template: metadata: labels: app: deepseek-inference spec: schedulerName: cube-scheduler containers: - name: vllm image: your-registry/vllm-dtk:0.6.3 command: [python3, -m, vllm.entrypoints.openai.api_server] args: - --model/models/deepseek-r1-distill-qwen-14b - --served-model-namedeepseek-qwen-14b - --tensor-parallel-size1 - --max-model-len8192 - --gpu-memory-utilization0.9 - --enforce-eager ports: - containerPort: 8000 resources: limits: hygon.com/vdcu-sched: 25 memory: 40Gi requests: hygon.com/vdcu-sched: 25 cpu: 8 memory: 40Gi volumeMounts: - name: dcu-libs mountPath: /opt/dtk - name: models mountPath: /models env: - name: LD_LIBRARY_PATH value: /opt/dtk/lib:/opt/dtk/lib64 - name: HIP_VISIBLE_DEVICES value: 0 volumes: - name: dcu-libs hostPath: path: /opt/dtk-24.04 - name: models hostPath: path: /data/models说几个容易出错的地方。第一schedulerName必须写成cube-scheduler否则 Pod 会被 K8s 默认调度器接管而默认调度器根本不认识hygon.com/vdcu-sched这种资源Pod 永远 Pending。这一点是最容易忽略的而且报错信息非常隐蔽写着0/8 nodes available: 8 Insufficient hygon.com.vdcu-sched其实节点上明明有很多空闲资源。第二HIP_VISIBLE_DEVICES0这个环境变量在整卡模式下是错的但在 vDCU 模式下每个容器只能看到一个被代理映射出来的虚拟设备设置为 0 是正确的。vdcu-proxy 会在容器内屏蔽物理设备序号所有虚拟设备统一从 0 开始编号。第三--gpu-memory-utilization0.9这个参数指的是 vLLM 允许使用的显存占可见显存的比例。因为 vDCU 已经把显存限制在一个档位里0.9 表示 vLLM 最多用掉 90% 的共享显存剩 10% 给 CUDA context 和碎片。如果你的 vDCU 档位是按 30GB 切的0.9 就是 27GB对 14B 推理是够的。5.3 多模型共享一张卡的验证方法配好之后我在同一个物理节点上发了两个 Deployment一个是上面的 14B 模型另一个是 DeepSeek-R1-Distill-Qwen-7B7B 的份额申请的是hygon.com/vdcu-sched: 15。两个 Pod 都起来后用hy-smi在宿主机上查显存rootdcu-node03:~# hy-smi GPU Memory Usage 0 45678MiB / 65536MiB可以看到一张卡的显存被两个任务吃掉了差不多 70%比整卡模式下一卡一顿的利用率高多了。再观察任务的 p99 时延7B 模型因为算力份额只给了 15%单次推理的响应时间比独占时要慢 20%-30%但完全在可接受范围内。对推理服务来说只要 p99 不突破 SLO慢一点没关系关键是卡的利用率上去了整体吞吐反而更高。6. vDCU 环境下的真实踩坑记录与调优思路6.1 Pod Pending 排查链路被误判的资源名有一次 Pod 一直 Pending我用kubectl describe pod看到事件是Insufficient hygon.com.vdcu-sched下意识以为是资源池满了去查节点剩余容量发现明明标注了 100% 可分配。后来才发现是资源名写错了。CubeStudio 的共享池资源名里面带了池子 ID比如hygon.com/vdcu-sched-pool-a3f8而我直接在 YAML 里写了hygon.com/vdcu-sched。调度器 extender 按照精确名字匹配去查池子找不到就返回不可调度。排查这种问题有个通用方法论先看 scheduler extender 的日志再看 device plugin 上报的资源全名最后确认 Pod 的schedulerName是不是设置对了。千万别一上来就怀疑资源池配置资源池配置写错反而是小概率事件。我在生产环境还遇到过一次同名资源被两个不同 device plugin 重复上报导致 kubelet 直接报错拒绝启动。6.2 vDCU 隔离边界能保证的与不能保证的我刚开始用 vDCU 时以为它和硬件 MIG 一样能硬隔离故障域。后来压测时发现一个任务因为显存越界导致驱动异常同一物理卡上的所有 vDCU 任务全部报错重启。原因很简单vDCU 是用户态代理层面做的资源切分驱动层面其实还是共享同一个物理设备驱动一旦崩溃整个物理卡上跑的所有虚拟实例全完。这个事让我调整了平台侧的配置策略高危任务和生产任务不要混在同一张卡上可以把高危任务固定到一个独立的开发测试池生产推理放到另一个池子物理卡之间不要交叉共享。平台侧做不了硬隔离但至少能在调度策略上避免故障扩散。另一个 vDCU 的弱点是显存回收不够及时。算力调度型 vDCU 允许任务突发借用显存但如果一个容器退出时没有正常释放显存或者 vLLM 退出时有残留的 HIP context代理层需要额外轮询才能把显存收回。我在运维时碰到过容器退出后显存仍被占用十几分钟的情况。这个问题的临时解法是写一个巡检脚本定时检查异常退出 Pod 并把相关进程 kill 掉同时定期在节点上执行echo 1 /sys/.../gpu_reset之类的重置操作不过不同驱动版本的 reset 接口差异很大建议还是以重启异常容器为主。6.3 从整卡到 vDCU 的平滑迁移经验最后分享一个迁移方面的经验。如果你的平台上已经有一批业务在用整卡调度的 DCU想把它们平滑迁移到 vDCU 共享池千万别一次性把所有节点翻成虚拟化模式会导致有状态任务重建。我的做法是双池并存先用两三个节点搭一个 vDCU 池把新任务和可无损重启的推理任务迁过去跑一周观察显存回收和时延表现确认稳定后再逐步在每个物理节点上增加 vDCU 代理的部署。整卡池保留给少数确实需要独占算力的训练任务。这样做的迁移代价非常小而且你可以用真实业务数据去验证两种模式的资源收益而不是靠估算。这套链路跑通之后算法团队再提交 DeepSeek 推理任务时已经感受不到底层是物理卡还是虚拟卡了。后端看板显示整个 DCU 集群的显存利用率从原来的 40% 左右提升到了 70% 以上这对花了大价钱采购算力的组织来说省下来的资源都是真金白银。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门