
“DCU 装上海光驱动Kubernetes 是不是就能直接调度了”上周有朋友问我这个问题我觉得他大概率把事情想简单了。机器上明明有四张 DCUkubectl get nodes却看不到任何 DCU 资源Pod 想申请一张卡根本无从谈起。DCU 要接入 Kubernetes 并最终在 CubeStudio 这类 AI 平台上跑起 DeepSeek中间隔着驱动、运行时、设备插件、调度器、平台五层每一层掉链子卡就只能白白躺在机器里。这篇文章就是我完整走完这条链路后的实操记录覆盖整卡调度、两种 vDCU 虚拟化方式、CubeStudio 平台适配以及最后把 DeepSeek 跑起来的全过程。整篇文章会写得比较细适合正在做 DCU 算力纳管、或者准备在信创环境里做 AI 平台的同学参考。1. 纳管 DCU 前先搞清楚资源模型和软件栈边界1.1 DCU 不是“换了个名字的 GPU”软件栈有三层海光 DCUDeep Computing Unit是海光推出的深度学习计算单元硬件架构上兼容类 GCN/CDNA 的通用计算体系软件生态上与 ROCm/HIP 体系有很深的渊源。这带来的直接好处是大量为 AMD ROCm 生态编写的推理框架、训练框架可以相对容易地迁移到 DCU 上坏处也很明确——如果你脑子里只有 NVIDIA 的那套“CUDA 一条龙”经验会在接入过程中反复碰壁。具体到 K8s 接入场景DCU 的软件栈至少分三层内核驱动层负责把 DCU 硬件注册到系统里生成/dev/dri/renderD*这类设备节点让上层用户态程序能访问 GPU 设备。用户态运行时层海光 DTKDCU Toolkit提供的 HIP 运行时、数学库、通信库类似 RCCL等容器里的 PyTorch、vLLM 靠它们跟硬件打交道。资源管理层K8s Device Plugin 负责探测设备、上报资源、分配设备把“物理卡”抽象成 K8s 可调度的扩展资源。这三层缺一不可。很多人上来就在 K8s 里部署一堆工作负载结果容器里连/dev/dri都看不到回头查半天才发现是设备插件和驱动版本对不上这类问题我在后面专门写一节。1.2 K8s 扩展资源链路一个 DCU 请求是怎么落到卡上的K8s 调度器并不认识“DCU”是什么它只认识整数资源量。DCU 要被调度得走完整的扩展资源链路节点上的 Device Plugin 通过驱动接口比如dcu-smi采集本机 DCU 数量和显存信息。Device Plugin 向 kubelet 注册并上报扩展资源例如hygon.com/dcu4。用户在 Pod 里声明limits.hygon.com/dcu: 1。kubelet 收到 Pod 创建请求后如果本节点有足够资源就调用 Device Plugin 的Allocate接口拿到设备路径、环境变量、cgroup 设备规则等分配信息。容器运行时根据这些信息把/dev/dri/renderD*等设备挂进容器同时注入HIP_VISIBLE_DEVICES之类的环境变量。这条链路里任何一个环节错位都会出问题。比如设备插件上报了 4 张卡但Allocate阶段返回的设备号和实际驱动不匹配Pod 可能显示 Running进去却发现卡不可用。所以我一直强调从节点往上一层层验证而不是直接跳到平台层操作。1.3 整卡、共享、vDCU 三选一先看业务再谈方案海光 DCU 接入 K8s 时常见的有三种资源使用方式方式资源隔离程度适用场景主要问题整卡强隔离一张卡只归一个 Pod大模型训练、对性能有硬性要求的推理显存和算力利用率可能很低时间片共享型 vDCU算力时分复用显存共享开发调试、多个轻量推理服务、Notebook显存不隔离大负载可能互相影响显存隔离型 vDCU显存、算力双隔离多租户场景、训练推理混部配置稍复杂需要平台配套支持这仨没有绝对好坏。整卡模式最稳但很多场景下一张 32G 的卡只跑一个 7B 模型推理显存浪费一半以上共享模式利用率高但隔离弱显存隔离型是折中方案接近“虚拟化实例”的效果。我的建议是训练任务多的集群优先考虑整卡或显存隔离型 vDCU推理和 Notebook 为主的集群可以把时间片共享型作为补充但一定要配好显存监控。2. 整卡接入Device Plugin 部署与调度验证2.1 节点侧准备驱动、DTK、容器运行时配置整卡接入看起来最简单但准备工作反而最容易忽视。先装驱动。不同型号的 DCU 卡对应不同的驱动版本和 DTK 版本这个组合关系以海光官方发布的兼容性矩阵为准。我踩过的坑是机器上有两张不同代际的 DCU 卡驱动装的是新卡要求的版本老卡直接识别不到dcu-smi只显示两张卡。很多机器其实混插了不同型号的卡装完驱动后务必逐卡确认。驱动装好后验证以下内容# 查看 DCU 设备是否被系统识别 ls -l /dev/dri/ # 查看 DCU 驱动信息和设备列表 dcu-smi list dcu-smi info # 确认模块加载状态 lsmod | grep -i dcu接着配置容器运行时。K8s 节点一般用 containerd 或 dockerDCU 的 Device Plugin 在分配设备时依赖容器运行时支持设备挂载和 cgroup 限制。海光官方通常会提供配套的 runtime hook 或容器镜像需要在/etc/containerd/config.toml里注册对应的 runtime 类型保证 Pod 能被注入设备路径。这块配置网上资料零散建议直接参考官方安装包里的docs目录别自己瞎猜。2.2 部署 Device Plugin DaemonSet上报扩展资源节点侧就绪后部署 Device Plugin。Device Plugin 本质是一个 DaemonSet每个节点跑一个 Pod负责向 kubelet 注册本机 DCU 资源。apiVersion: apps/v1 kind: DaemonSet metadata: name: hygon-device-plugin namespace: kube-system spec: selector: matchLabels: name: hygon-device-plugin template: metadata: labels: name: hygon-device-plugin spec: priorityClassName: system-node-critical tolerations: - operator: Exists containers: - name: device-plugin image: 海光官方 device-plugin 镜像 imagePullPolicy: IfNotPresent securityContext: privileged: true volumeMounts: - name: device-plugin mountPath: /var/lib/kubelet/device-plugins - name: dev mountPath: /dev - name: sys mountPath: /sys volumes: - name: device-plugin hostPath: path: /var/lib/kubelet/device-plugins - name: dev hostPath: path: /dev - name: sys hostPath: path: /sys这个 YAML 里的镜像地址我故意留空了因为海光官方镜像仓库地址随版本变化直接用发行包里的安装脚本或 Helm Chart 是最稳的。部署完成后查看是否上报成功kubectl apply -f hygon-device-plugin.yaml kubectl -n kube-system get pods -o wide | grep hygon kubectl describe node node-name | grep -A 10 Allocatable如果看到类似hygon.com/dcu: 4出现在 Allocatable 和 Capacity 里说明 Device Plugin 已经成功注册。如果没有先看 Device Plugin Pod 日志和 kubelet 日志。2.3 创建一个冒烟测试 Pod验证整卡调度资源上报之后别急着往平台接先创建一个极简 Pod 验证调度和设备透传apiVersion: v1 kind: Pod metadata: name: dcu-smoke spec: restartPolicy: OnFailure containers: - name: main image: 带 DTK 运行时的测试镜像 command: [sh, -c, dcu-smi list sleep 3600] resources: limits: hygon.com/dcu: 1假设你的集群里只有一个节点有 DCU这个 Pod 应该被调度到该节点并正常运行。进入 Podkubectl exec -it dcu-smoke -- bash # 容器内查看 DCU 是否可见 dcu-smi list echo $HIP_VISIBLE_DEVICESHIP_VISIBLE_DEVICES是 Device Plugin 在Allocate阶段注入的环境变量它代表当前 Pod 被分配到的卡编号。冒烟测试通过后整卡链路就通了。2.4 用资源清单排查上报异常整卡环节最常遇到三类异常我按排查顺序整理一下节点没见过 DCU 资源优先确认 Device Plugin Pod 是否 Running日志里有没有报“driver not initialized”之类的错误然后确认/var/lib/kubelet/device-plugins/下有没有对应的 socket 文件。Pod 一直 Pending检查 Pod 有没有声明limits.hygon.com/dcu只写requests不写limits是不行的再看节点是否有足够剩余额度。容器内看到卡但执行报权限错误通常是 cgroup 设备规则没有正确写入确认 Device Plugin 有privileged权限且容器运行时版本支持设备注入。我的习惯是每完成一步就记录一次验证结果逐节点推进。K8s 集群如果有多个 GPU 节点绝不要在单个节点验证通过后直接上平台得保证所有节点的设备插件版本一致。3. 两种 vDCU 虚拟化方式的原理与实战配置3.1 时间片共享型 vDCU算力等分显存共享时间片共享型 vDCU 的思路是多个 Pod 共用一张物理卡调度器把卡的算力按时间片切分给不同任务。这样做的好处是资源利用率高尤其适合轻量推理、开发调试、Notebook 交互这类对延迟不敏感的场景。配置上通常是在 Device Plugin 里开启共享模式并设置单卡最多切分多少份。下面是一个示意配置具体参数名以你拿到的发行版为准{ share: true, defaultMemory: 0, vdcuPerCard: 4 }比如一张 32G 物理卡开启vdcuPerCard: 4后K8s 上报的资源额度翻 4 倍理论上可以同时跑 4 个各申请 0.25 张卡的 Pod。但时间片共享有个硬伤显存不隔离。4 个 Pod 同时跑如果某个模型的显存峰值超过了它“分到”的比例整个卡都会受影响甚至 OOM。我实测中遇到过两个推理服务同时跑一个显存冲到 28G另一个直接被 kill。所以这个模式我通常只建议用于内部开发环境生产环境多租户慎用。3.2 显存隔离型 vDCU一张卡切成多个独立实例显存隔离型 vDCU 的体验更接近“把一张物理卡切成 N 个虚拟卡”每个 vDCU 实例有独立的显存分区和计算资源配额Pod 只能看到自己分配到的那个实例显存互不干扰。配置方式上海光的设备管理器支持把物理卡按显存大小切出多个 vDCU。以 64G 卡为例可以切成 4 个 16G 的 vDCU 实例或者 2 个 32G 实例。Pod 申请资源时指定自己需要多少显存即可。resources: limits: hygon.com/vdcu: 1 requests: hygon.com/vdcu: 1显存隔离型最大的价值在于“稳定”。多租户平台上训练任务跑到一半被邻居挤挂是最头疼的事显存隔离后这种问题基本消失。代价是切分粒度受限——不能切出一个“1.5G”这种不规则大小分配不够灵活而且如果某个 Pod 宁可只要 8G 显存但需要全部算力这种模式也满足不了。3.3 两种模式的规格对比与选型维度时间片共享型 vDCU显存隔离型 vDCU算力隔离时分复用单任务峰值可用满整卡算力按实例配额限制算力上限稳定显存隔离不隔离共享物理卡显存独立显存地址空间支持大小按比例 1/N 切分按显存大小切分适合负载Notebook、轻量推理、调试多租户训练、正式推理服务配置复杂度低中典型问题显存 OOM 会波及同卡任务显存碎片、大模型放不下选型的时候别只看“能切几个”要看你客户的真实诉求。如果是给内部研发用共享型就够如果客户明确要求“不同业务部门之间的任务不能互相影响”直接上显存隔离型别在这上面省钱省事。3.4 多任务混部下的性能隔离实测感受我在测试环境里做了一组小实验一张物理卡共享型模式同时跑两个 7B 模型推理单路延迟从 40ms 左右涨到 80ms 以上吞吐没有明显提升说明算力被均分了但显存带宽成为瓶颈。换成显存隔离型两个实例各跑一个 7B 模型单路延迟基本稳定互不影响。由此得到的经验是共享型适合“穿插使用”的场景比如白天一堆人开 Notebook 调代码一旦要正式支撑业务还是得靠显存隔离型。平台层最好两种模式都能配让不同项目按需选择。4. CubeStudio 平台侧适配让 DCU 变成可分配的资源4.1 CubeStudio 对 K8s 集群的管理方式CubeStudio 这类 AI 平台本身不一定关心底层的卡是什么品牌——它建立在 Kubernetes API 之上只要 K8s 能把资源名暴露出来平台就能用。这也意味着一个关键点先确保 K8s 层 DCU 资源上报正常再谈平台适配。平台能看到什么资源完全取决于你 K8s 层面暴露了哪些资源名。在 CubeStudio 里通常需要做三件事在集群配置里指定要对接的 K8s 集群或通过 kubeconfig 注册。配置资源模板把hygon.com/dcu、hygon.com/vdcu等扩展资源暴露给平台用户。为不同项目设置资源配额限制 DCU 申请上限。4.2 配置 RuntimeClass 和资源配额如果集群里同时有多种运行时例如默认 containerd 和 DCU runtime建议在 K8s 里定义 RuntimeClass让平台侧创建 Pod 时指定使用 DCU runtimeapiVersion: node.k8s.io/v1 kind: RuntimeClass metadata: name: hygon-dcu handler: hygon然后在 CubeStudio 的资源配置里把该 RuntimeClass 关联到创建 Notebook、训练任务的模板中。资源配额方面平台一般按“项目/租户”设置额度例如apiVersion: v1 kind: ResourceQuota metadata: name: project-a-quota spec: hard: hygon.com/dcu: 8 requests.nvidia.com/gpu: 0这里有个容易忽略的地方很多平台默认只认nvidia.com/gpu如果不把 DCU 的资源名映射成平台能识别的资源类型用户界面里根本看不到 DCU 选项。需要在平台的资源定义里手动添加 DCU 资源类型并设置单位。4.3 通过平台创建 Notebook 与训练任务在 CubeStudio 上创建 Notebook 时镜像一定要选带 DTK 的版本。如果默认镜像列表里没有需要项目管理员预先导入。创建时声明资源资源类型选择hygon.com/dcu或hygon.com/vdcu。数量整卡场景填 1vDCU 场景按平台页面的规格填写。创建训练任务时K8s 层面的 Pod 模板和平台 UI 参数是一一对应的。以 PyTorchJob 为例apiVersion: kubeflow.org/v1 kind: PyTorchJob metadata: name: dcu-train-test spec: pytorchReplicaSpecs: Master: replicas: 1 template: spec: containers: - name: pytorch image: 带 DTK 和 PyTorch 的镜像 resources: limits: hygon.com/dcu: 1提交后平台会把任务转成 K8s 资源交给调度器。这里的重点是平台本身不负责选卡最终能不能调度成功取决于第 2 章的节点链路是否通畅。4.4 平台日志与节点负载的确认方法任务跑起来之后验证“平台 - K8s - 卡”三层是否一致。最直接的办法是在平台页面找到 Pod确认它调度到了预期的节点。登录节点执行dcu-smi看是否有对应进程占用卡。比对 Pod 里声明的HIP_VISIBLE_DEVICES和节点上 dcu-smi 的卡编号。这块看起来是常识但我在实际环境里遇到过平台页面显示任务成功的 Pod节点上的卡却一个进程都没有——原因是平台创建任务时资源声明写岔了Pod 根本没申请 DCU 资源。所以检查 Pod 实际 spec 里的 resources而不是只看平台 UI 的展示。5. DeepSeek 上卡在 DCU 上跑起来一个实际推理服务5.1 模型选型从 Distill 小模型到全量版怎么选DeepSeek 官方开源了 R1 系列及蒸馏版本。在 DCU 上部署先想清楚手上有多少显存。以 FP16/BF16 权重为例粗略估算1.5B约 3GB 权重小幅显存即可。7B/8B约 15GB 权重32G 卡能跑16G 卡要小心。14B约 28GB 权重32G 卡很紧张建议用量化版本。32B约 64GB 权重单卡基本没戏多卡或量化。671B 全量版需要大规模多卡集群不在单机范畴讨论。我的建议单张 32G DCU 卡优先跑 DeepSeek-R1-Distill-Qwen-7B/14B 这种量级卡多并且有并行条件再考虑 32B 以上。别被“要跑就跑 671B”的心态绑架先让业务在可用的资源上跑起来再谈规模。5.2 用 vLLM 的 HIP 后端启动 DCU 推理vLLM 是目前在 DCU 上部署 DeepSeek 性价比最高的推理框架。原因很简单vLLM 有相对成熟的 ROCm/HIP 后端海光 DCU 的 DTK 与 HIP 生态兼容社区适配成本低。在 DCU 上启动 vLLM大致命令如下HIP_VISIBLE_DEVICES0 \ HSA_OVERRIDE_GFX_VERSION具体 gfx 版本按 DTK 对应配置 \ vllm serve deepseek-ai/DeepSeek-R1-Distill-Qwen-14B \ --max-model-len 8192 \ --gpu-memory-utilization 0.9 \ --dtype bfloat16 \ --port 8000HSA_OVERRIDE_GFX_VERSION是 DCU 适配时常见的关键环境变量具体值取决于 DTK 版本和硬件代际直接看官方 release note 的说明这里不写死。如果手动编译过 vLLM注意它依赖的 hipBLAS、hipFFT 等库要和 DTK 版本对齐。版本错位时启动会报符号找不到的错处理起来很费时间。5.3 性能与显存验证一次请求从进入到返回服务起来后用 curl 测试推理链路curl -X POST http://node-ip:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: deepseek-ai/DeepSeek-R1-Distill-Qwen-14B, messages: [{role: user, content: 你好介绍一下你自己}], max_tokens: 512 }重点关注几个指标TTFT首个 Token 生成时间反映模型加载和预填充速度。decode 速度生成阶段每秒 token 数。显存占用用dcu-smi观察确认接近--gpu-memory-utilization设定值。我实测下来DeepSeek 这类大模型对显存带宽极其敏感并发高时 decode 速度下降明显。如果是多路并发推理建议控制并发数或者把单实例--max-num-seqs调低避免显存不够导致 OOM。6. 适配过程中翻车最多的地方踩坑实录6.1 容器里看不到 DCU 设备现象Pod 正常 Running但容器内ls /dev/dri/是空的跑训练报错找不到设备。排查链路检查 Device Plugin 是否正常注册kubectl -n kube-system logs device-plugin-pod。检查 Pod 是否真的有limits.hygon.com/dcu声明——只有 requests 没有 limits 时资源不会被分配。确认节点上/var/lib/kubelet/device-plugins/下有对应 socket。确认容器运行时配置了 DCU runtime。80% 的情况出在第 2 步平台创建任务时容易漏掉 limits 声明。6.2 驱动升级后 Device Plugin 匹配不上现象升级驱动和 DTK 后节点资源从hygon.com/dcu: 4变成了 0Pod 无法调度。原因大部分是 Device Plugin 编译时依赖的用户态库版本和驱动版本不匹配。排查时先用dcu-smi看主机侧是否识别卡主机侧正常说明问题出在 Device Plugin 和 kubelet 的交互层直接重启 Device Plugin Pod 或用官方新版镜像即可。这类问题最好的避免方法是升级驱动之前先在测试节点跑一遍完整验证确认dcu-smi、Device Plugin、Pod 三层都正常再批量推。6.3 vDCU 显存分配 OOM时间片共享型 vDCU 模式下显存不隔离多个 Pod 可能同时把整卡显存打满。表现是其中一个任务突然 OOM严重的整卡所有任务都挂掉。解决思路有三条换成显存隔离型 vDCU从根上隔离。在推理框架里设置--gpu-memory-utilization给每个 Pod 留足余量。平台层加上显存监控和告警接近阈值时提前介入。6.4 调度器抢占导致 vDCU 资源冲突当集群里同时有整卡和 vDCU 任务时某些调度器配置可能导致资源重复分配。比如节点上报了hygon.com/dcu: 4同时又上报了hygon.com/vdcu: 16调度器可能以为一个 Pod 申请 vDCU 时不影响整卡额度但实际上底层在抢同一块物理卡。解决方式是理清资源模型——要么节点只开一种模式要么在调度器层面把两种资源设置为互斥例如通过配额限制不让同一节点同时接受整卡和 vDCU 任务。这块属于平台和调度器配置的高级话题生产环境务必提前设计好别等出事了再调。最后再分享一个我个人的实操体会DCU 接入 K8s 这件事成功的关键是“顺序”和“分层验证”。先从dcu-smi确认驱动到/dev/dri设备节点再到 Device Plugin 资源上报然后是原生 Pod 调度最后才接到 CubeStudio 平台。任何一步跳过去直接试平台出了问题你都分不清是哪一层坏了。我之前就是先配平台配额再查节点来回折腾了一整天才定位到 Device Plugin 版本问题。按上面这套链路逐层走大部分坑都能提前避开。