今天聊一个我最近一直在折腾的问题:海光 DCU 怎么接入 Kubernetes,再统一放进 CubeStudio 里调度。公司里有一批海光 DCU 的机器,放了很久没人真正用起来,算法同事催着要跑 DeepSeek。DCU 本身兼容 HIP/ROCm 生态,跑主流推理框架没有太大障碍,但真正卡壳的地方在平台层:K8s 不认这张卡,CubeStudio 这类 AI 平台更不可能自动感知 DCU,得先做设备插件、扩展资源和虚拟化适配。这篇我按自己的实操顺序来写:先讲整卡接入,再讲两种 vDCU 虚拟化的原理和配置,然后是 CubeStudio 资源池怎么建,最后完整跑通一个 DeepSeek 推理服务。适合平台工程师、运维,以及想自己折腾异构算力调度的算法同学参考。
1. 为什么要把海光 DCU 接进 Kubernetes 和 CubeStudio
1.1 算力碎片化:真实存在的痛点
很多团队的算力池是怎么慢慢变成一锅粥的?最开始只有 NVIDIA GPU,大家用 nvidia-device-plugin 把卡接进 K8s,模型训练、推理都在这套体系里跑。后来因为成本、供货或者信创要求,机器里多了海光 DCU。DCU 算力不差,显存也够大,但算法同学一看调度平台里没有这个资源,就不会去用,于是卡一直闲着。
我这边的情况是:GPU 节点已经快排满了,DCU 节点利用率不到 5%。不是没人跑任务,是平台根本不暴露 DCU 资源。大家申请算力只能写“我要 2 卡 GPU”,DCU 再强也无感知。这种算力碎片化问题,靠人肉调度解决不长久,必须让 DCU 以标准资源的形式进入 K8s,再由 AI 平台统一暴露给用户。
1.2 CubeStudio 是什么,为什么选它
CubeStudio 本质上是跑在 Kubernetes 之上的一层 AI 研发平台,负责把底层算力封装成“资源池”,给用户提供数据集、Notebook、训练任务、模型推理等能力。它自己不会去认 DCU 的物理设备,而是依赖 K8s 的扩展资源机制:只要节点上出现了hygon.com/dcu这类资源,平台就能把它纳入资源池,用户提交任务时就能按“几张卡、多少显存”来申请。
选择 CubeStudio 的原因很直接:我们内部已经在用它管 GPU 任务,算法同学对这套操作界面很熟。如果 DCU 能通过同一套 K8s 资源模型暴露出去,用户侧完全无感知,不需要学新工具。这也是我这次适配的核心思路:不改造平台本身,只补设备层和调度层的能力。
1.3 接入前先想清楚:整卡、共享、虚拟化的边界
在动手之前,得先把三种使用方式的分界线理清楚,不然后面配置时会很混乱。
- 整卡模式:一个任务独占一张物理 DCU。优点是简单、稳定、性能最好,适合大模型训练、大尺寸推理。缺点是卡一旦分配出去,哪怕任务只用了一半显存,别人也用不了剩下的。
- 共享模式:多个任务共用一张物理卡。适合开发调试、小模型推理、批量离线任务,能明显提高卡的整体利用率。
- vDCU 虚拟化:共享的底层实现方式,一般分两种,一种是显存隔离型,一种是时间片复用型。这两种不是互斥的替代品,而是针对不同场景设计的调度手段。
先明白边界,后面选型的时候才不会纠结。
2. DCU 接入 K8s 的资源模型与设备插件
2.1 K8s 是怎么识别 DCU 的
K8s 原生不认识 GPU,也不认识 DCU。它只认识两类东西:CPU 和内存。要让集群感知到 DCU,标准做法是使用Extended Resource(扩展资源)和Device Plugin(设备插件)。
设备插件以 DaemonSet 方式跑在每个 DCU 节点上,启动时把节点上的 DCU 设备数量上报给 kubelet。kubelet 再把资源信息同步到 API Server,最终在kubectl describe node里看到类似:
Allocatable: cpu: 32 memory: 251Gi hygon.com/dcu: 4用户提交 Pod 时,在resources.limits里声明hygon.com/dcu: 1,调度器就知道这个 Pod 需要一张 DCU,把它调度到有足够 DCU 资源的节点上。之后设备插件会在容器启动前,把对应的设备节点和环境变量注入容器,任务就能在容器里直接调用 DCU。
这套机制的重点是:DCU 的驱动要装好,设备节点要能被设备插件发现,设备插件上报的资源名要和 Pod 请求的资源名一致。任何一环对不上,任务都起不来。
2.2 整卡模式:最稳妥的第一步
整卡模式是所有适配工作的起点。我的建议是,任何团队接入 DCU,先别搞虚拟化,先把整卡链路跑通。
整卡模式的资源声明非常简单:
resources: limits: hygon.com/dcu: 1不需要额外配置显存大小,也不需要关心卡内切分,一张卡就是最小调度单位。容器启动后,设备插件会设置HIP_VISIBLE_DEVICES之类的环境变量,应用只需要正常使用 HIP/ROCm 接口,就能看到对应的 DCU。
第一次验证时,可以跑一个最小的测试镜像,在容器里执行dcu-smi:
dcu-smi如果能看到卡的型号、显存、利用率,说明设备映射正常。这一步没通过,后面所有共享、vDCU 都不用谈。
2.3 共享模式的需求来源
整卡模式虽然稳,但利用率问题很真实。我这边很多算法同学跑的是数据处理、小模型微调、批量推理,显存占用往往只有几个 GB,却要整卡申请。一张 32GB 甚至 64GB 的 DCU,大部分时间在空转。
共享模式就是为这个场景设计的:把一张物理卡同时分配给多个任务。但共享不是白捡的,怎么共享、怎么隔离,决定了任务的稳定性和性能表现。这也是为什么要细究两种 vDCU 虚拟化方案。
2.4 第一种 vDCU:显存隔离型
显存隔离型 vDCU 的思路是把一张物理卡的显存切成若干份,每个 vDCU 独占固定大小的显存,计算单元也按一定方式隔离。你可以把它理解成物理卡里的“独立房间”,每个房间有固定的墙,房间之间互不打扰。
这种模式适合生产环境推理服务。比如一张 64GB 的卡,切成 4 个 16GB 的 vDCU,同时跑 4 个不同的推理服务,每个服务独占 16GB 显存,不会因为邻居任务吃满显存而 OOM。
配置时,资源请求通常长这样:
resources: limits: hygon.com/dcu-vmemory: 16具体资源名不同版本可能不一样,但语义都是“给我一个 16GB 的 vDCU”。这种模式的优点是隔离性好、性能可预期;缺点是显存切分后,如果某个 vDCU 实际只用了一半显存,剩下的也无法给别的任务用,依然存在一定浪费。
2.5 第二种 vDCU:时间片复用型
时间片复用型的思路完全不同。它不切割显存,而是让多个 vDCU 共享整张卡的显存和计算单元,通过调度器按时间片或优先级在任务之间切换。可以理解成多个人共用一张办公桌,谁用的时候谁在位,但桌面上的东西是大家共用的。
这种模式最直观的好处是超卖。比如一张卡可以创建 8 个 vDCU,同时跑 8 个开发调试任务,每个任务实际占用的显存都不大,计算资源通过时间片轮转,整体利用率会非常高。
配置时,资源请求可能长这样:
resources: limits: hygon.com/dcu-vslice: 1但风险也很明显:没有显存隔离,一个任务把显存吃满,会影响同卡上的所有其他任务。所以时间片复用型更合适开发环境、低优先级批任务、或者你完全清楚任务显存水位的情况。生产环境的推理服务,我一般不建议直接用时间片复用型。
2.6 两种 vDCU 的对比与选型
| 维度 | 显存隔离型 vDCU | 时间片复用型 vDCU |
|---|---|---|
| 显存隔离 | 有,每个 vDCU 独占显存 | 无,共享整卡显存 |
| 计算资源隔离 | 较好,按分区或权重分配 | 较弱,按时间片抢占 |
| 超卖能力 | 基本不支持 | 支持,可创建多个 vDCU |
| 典型场景 | 生产推理、稳定在线服务 | 开发调试、批处理、低优任务 |
| 风险点 | 显存切分后有浪费 | 显存冲突、邻居干扰 |
| 调度精度 | 高,可指定显存大小 | 低,通常按数量申请 |
选型建议很简单:在线服务优先显存隔离,离线任务优先时间片复用,两者可以同时启用。比如一张卡切出 2 个 16GB 的显存隔离型 vDCU 跑线上推理,再开出 4 个时间片型 vDCU 给开发同学做实验,充分利用剩余算力。
3. CubeStudio 海光 DCU 适配实操
3.1 环境准备:驱动、DTK、容器运行时
实操第一步,把节点底子打好。海光 DCU 跑 HIP/ROCm 生态,依赖两个关键组件:内核驱动和 DTK(DCU Toolkit)。驱动装好后,系统里会出现对应的设备节点,通常是/dev/kfd和/dev/dri/renderD*这类路径。用dcu-smi验证:
dcu-smi正常输出会列出卡号、显存总量、驱动版本、温度、利用率等信息。
接着确认容器运行时能识别设备。K8s 的设备插件虽然会在容器启动时注入设备节点,但如果容器运行时的配置文件里限制了设备挂载,任务起不来。用 containerd 时,建议先把运行时的基础配置检查一遍,确保没有白名单限制/dev/kfd和/dev/dri。
另外建议把dcu-smi也做进基础镜像里,后面排查问题会省很多时间。
3.2 部署 DCU 设备插件
设备插件是接入 K8s 的核心组件。一般以 DaemonSet 方式部署,一个节点一个 Pod。以我这边为例,设备插件的主要配置包括资源名hygon.com/dcu,以及是否开启共享、选择哪种虚拟化模式。
部署后立刻检查两个东西:
kubectl get pod -n kube-system -o wide | grep dcu kubectl describe node <dcu-node>期望结果是:插件 Pod 处于 Running 状态,节点描述里出现hygon.com/dcu的可分配数量,且数量和实际物理卡数量一致。
这一步常见的坑是插件 Pod 起来了,但节点不显示扩展资源。排查时优先看插件日志,八成是设备节点路径不对,或者权限不足读不到设备。
3.3 在 CubeStudio 里创建 DCU 资源池
设备插件把 DCU 上报到 K8s 后,CubeStudio 这边就相对简单了。在平台管理后台,找到资源池配置页面,新增一个 DCU 资源池。
关键配置点:
- 节点选择器:给 DCU 节点打上标签,比如
accelerator=hygon-dcu,资源池绑定这个标签,避免和 GPU 节点混在一起。 - 资源类型:添加
hygon.com/dcu,平台会读取节点上的可分配数量。 - 配额策略:给不同项目组设置 DCU 配额,比如某个算法组默认可以申请 2 张整卡或 8 个 vDCU,配额用完了需要申请扩容。
配置完成后,用户在平台界面创建 Notebook 或推理服务时,就能在资源选择里看到 DCU 选项。这里我一直强调:平台侧不需要改代码,它只是把 K8s 的资源模型翻译成界面选项。所以前期设备层的适配越规范,平台侧就越省事。
3.4 跑通第一个整卡任务
资源池建好后,先别急着提交大任务,跑一个最小的整卡验证任务。
我在 CubeStudio 里创建了一个测试 Notebook,镜像里预装了 DTK 和dcu-smi,资源申请选择1张 DCU 整卡。启动后打开终端:
dcu-smi能正常列卡,再跑一段 HIP 的向量计算代码,确认计算能力没问题。这一步的意义是验证从“平台界面”到“容器内部”的整条链路通了,后面再做 vDCU 才心里有底。
3.5 在 CubeStudio 里配置 vDCU 共享
共享能力一般在设备插件侧开启,然后在 CubeStudio 里把对应的 vDCU 资源类型暴露出来。
我这边开了两个共享模板:
- 模板 A(显存隔离型):每张卡切成 4 个 16GB 的 vDCU,主要用于在线推理服务。
- 模板 B(时间片复用型):每张卡创建 8 个 vDCU,数量可超卖,主要用于开发调试。
平台侧会把这两个模板映射成不同的资源请求。用户提交任务时,选了模板 A,后端生成的就是携带显存隔离型资源声明的 Pod;选了模板 B,后端生成的就是携带时间片型资源声明的 Pod。
这里必须提醒一句:虚拟化模式一定要和任务特征匹配。我有一次图省事,把一个线上推理服务跑在时间片复用型 vDCU 上,结果旁边开发同学的实验把显存吃满,线上服务直接 OOM,最后只能重启。后来所有生产推理服务统一走显存隔离型,再没出过这个问题。
4. DeepSeek 模型在 DCU 上跑通的全流程
4.1 模型选型与推理框架选择
DCU 接入平台和调度只是基础设施,最终还得看业务能不能跑起来。我们这次的目标是 DeepSeek。
DeepSeek 官方的大杯模型,比如 DeepSeek-R1 的 671B 版本,对显存和带宽的要求很高,一般团队没有多机多卡环境,不建议直接上。更实际的选择是蒸馏系列,比如DeepSeek-R1-Distill-Qwen-7B / 14B / 32B,模型尺寸适中,效果也不错,更适合在单机或双卡 DCU 节点上部署。
推理框架方面,vLLM 是目前兼容性最好、社区最活跃的选择之一。DCU 兼容 HIP/ROCm 生态,所以理论上只要 vLLM 能跑在 ROCm 环境,就能跑在 DCU 上。建议优先使用带 HIP 后端的 vLLM 镜像,或者直接用海光官方/厂商适配过的镜像,能少踩不少编译坑。
显存估算可以参考这个表:
| 模型 | 精度 | 显存估算 |
|---|---|---|
| DeepSeek-R1-Distill-Qwen-7B | FP16 | 约 16GB |
| DeepSeek-R1-Distill-Qwen-14B | FP16 | 约 30GB |
| DeepSeek-R1-Distill-Qwen-32B | FP16 | 约 65GB |
| DeepSeek-R1-Distill-Qwen-32B | INT8/INT4 量化 | 20GB~35GB |
我这边用的是 32B 版本,配合一张大显存的 DCU 整卡,效果和成本比较平衡。
4.2 构建推理镜像
镜像里需要包含 vLLM、DTK 运行库、dcu-smi工具,以及模型服务的启动脚本。Dockerfile 大致长这样:
FROM registry.example.com/dcu-base:latest RUN pip install vllm \ && pip install "transformers" "safetensors" "accelerate" ENV HIP_VISIBLE_DEVICES=0 WORKDIR /app CMD ["vllm", "serve", "/models/DeepSeek-R1-Distill-Qwen-32B", "--host", "0.0.0.0", "--port", "8000"]模型文件建议直接挂载到容器目录,不要放在镜像里,不然镜像体积几个 GB 起步,构建和拉取都痛苦。
4.3 编写 Kubernetes 部署清单
最终跑在 K8s 上的 Deployment 长这样:
apiVersion: apps/v1 kind: Deployment metadata: name: deepseek-r1-32b namespace: ai-platform spec: replicas: 1 selector: matchLabels: app: deepseek-r1-32b template: metadata: labels: app: deepseek-r1-32b spec: nodeSelector: accelerator: hygon-dcu containers: - name: vllm image: registry.example.com/dcu-vllm:latest command: - vllm - serve - /models/DeepSeek-R1-Distill-Qwen-32B - --host - 0.0.0.0 - --port - 8000 - --gpu-memory-utilization - "0.9" - --max-model-len - "32768" ports: - containerPort: 8000 resources: limits: hygon.com/dcu: 1 volumeMounts: - name: model-storage mountPath: /models livenessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 600 periodSeconds: 60 volumes: - name: model-storage persistentVolumeClaim: claimName: deepseek-model-pvc这里有两个细节要注意:
- 资源声明用的是
hygon.com/dcu: 1,也就是整卡模式。32B 模型需要完整显存,用整卡最稳。 livenessProbe的initialDelaySeconds要设大一点。vLLM 加载 32B 模型可能要几分钟,探针设太短会导致容器反复被重启。
如果部署 7B 模型,可以考虑使用显存隔离型 vDCU,比如申请一个 16GB 的 vDCU,多路复用一张物理卡,成本更低。
4.4 启动验证与并发调优
服务起来后,先看模型是否加载完成:
curl http://<pod-ip>:8000/v1/models能返回模型列表,说明服务已经就绪。接着发一个对话请求:
curl http://<pod-ip>:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "deepseek-r1-32b", "messages": [{"role": "user", "content": "用一句话介绍 Kubernetes"}], "max_tokens": 512 }'正常返回文本后,观察 DCU 资源的使用情况:
dcu-smi重点看显存利用率和计算利用率。如果显存已经吃满,计算利用率却很低,说明瓶颈在推理并发不够,可以调大--max-num-seqs;如果经常报显存不足,优先降低--max-model-len或调低--gpu-memory-utilization。
5. 常见问题与排查实录
5.1 高频故障速查表
| 问题 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 节点不显示 hygon.com/dcu 资源 | 设备插件没起来或上报失败 | kubectl logs -n kube-system <dcu-plugin-pod> | 检查驱动路径、设备节点权限 |
| Pod 一直 Pending | 节点 DCU 资源不足 | kubectl describe pod <name> | 减少资源申请,或等任务释放 |
| 容器里看不到 DCU 设备 | 设备插件未注入设备/环境变量 | 检查 Pod YAML 里的环境变量 | 重建设备插件,确认资源名一致 |
| vLLM 启动报找不到 HIP 设备 | HIP_VISIBLE_DEVICES 未设置或 set 错误 | env查看容器环境变量 | 手动指定设备和容器内实际设备号一致 |
| 共享模式下任务 OOM | 邻居任务占用显存 | 查看 dcu-smi 显存水位 | 换显存隔离型 vDCU |
| 模型加载很慢 | 权重文件从远端下载 | 检查磁盘 I/O 和网络 | 模型权重提前放本地 PVC 挂载 |
| 探针导致容器不断重启 | initialDelaySeconds 太短 | kubectl logs看启动日志 | 调大延迟时间 |
5.2 我踩过的几个坑
第一个坑是设备插件版本和驱动不匹配。最开始我随便拉了一个插件版本,结果节点上报的卡数量只有实际的一半,排查了半天才发现是插件读取设备列表的接口变了。后来统一了驱动和插件版本,问题消失。
第二个坑是共享模式下资源声明写错。vDCU 的资源名和整卡的资源名不一样,我在一个测试任务里用了整卡资源名,却想申请半个 vDCU,结果调度器直接拒绝。排错时先看资源名对不对,再看配额够不够。
第三个坑是容器内执行dcu-smi报权限错误。设备节点没挂进去,插件虽然注入了部分设备,但/dev/kfd权限不够。解决办法是在设备插件的配置里把需要挂载的设备节点列全,而不是只依赖默认规则。
第四个坑是 vLLM 启动时现场下载模型权重。K8s 节点从 Hugging Face 下载几十 GB 权重,慢到怀疑人生。后来我把模型文件放到内网对象存储,再通过 PVC 挂载,启动时间从半小时降到两分钟。
5.3 调优心得
跑稳定之后,我做了几轮并发压测,有几个经验可以分享:
- 推理服务的
--gpu-memory-utilization不要直接拉满到 0.99,留一点余量给临时张量和调度开销,0.9 是比较稳的值。 - 时间片复用型 vDCU 虽然能超卖,但一定要注意限制每个任务的最大显存占用。建议配合平台的资源限额功能,给共享任务设置显存上限。
- 如果同一个模型要服务多个业务线,优先用显存隔离型 vDCU 隔离部署,而不是复用同一个 vLLM 实例。虽然多占一些显存,但故障爆炸半径小很多。
6. 写在最后的个人心得
这轮海光 DCU 接入做下来,我最深的体会是:先把整卡链路打稳,再谈虚拟化,不要一上来就追求共享。整卡模式是地基,设备插件、资源上报、容器映射这些环节全部验证通过之后,再做 vDCU 就是水到渠成的事情。反过来的话,一旦出了问题,你根本分不清是驱动问题、调度问题还是虚拟化隔离问题。
另一个体会是,DCU 的生态现在确实比想象中好。HIP 兼容让 vLLM 这类主流框架可以直接跑,模型加载和推理效果都能到生产可用的水平,但这套东西的坑都藏在细节里:资源命名、设备节点权限、镜像版本、模型挂载方式,每一项都需要实际踩过才知道。如果你也在折腾类似的事情,建议按这个顺序推进:驱动到dcu-smi能识别,插件上报节点资源,整卡任务跑通,再逐步上共享和 vDCU,最后才部署 DeepSeek 推理服务。
后面我计划把 DCU 的训练任务也接进 CubeStudio,比如 LoRA 微调、SFT 这类场景。等跑完再更新一篇。大家如果有更稳的 vDCU 参数或共享调度策略,欢迎一起交流。