海光DCU接入Kubernetes实战:整卡共享与vDCU虚拟化全解析

海光DCU接入Kubernetes实战:整卡共享与vDCU虚拟化全解析 最近我这边有个比较硬核的活儿把一批海光 DCU 真正用起来——不是跑个 demo 截图发朋友圈而是把整卡调度、共享调度、虚拟化切分全部接进 Kubernetes再挂到自研的 AI 平台 CubeStudio 上最后让业务方在上面跑 DeepSeek 推理。整个过程踩了不少坑也摸出了一些门道。这个需求其实很典型硬件到了驱动装了dcu-smi也能看到卡了但 K8s 不认识这张卡AI 平台不认识这张卡业务方的训练脚本一申请nvidia.com/gpu就报“资源不足”。所以这篇文章重点解决一个问题海光 DCU 是怎么一步步接入 Kubernetes 和 AI 平台的包括整卡、共享、两种 vDCU 虚拟化模式下的配置方式以及最后怎么把 DeepSeek 跑起来。如果你是平台组、基础设施组的同学或者公司刚买了国产算力准备做适配这篇文章基本可以当一份踩坑复现手册来用。1. 整体思路拆解DCU 接入 K8s 到底要过几道坎1.1 先搞清楚 DCU 和 GPU 的“似像非像”海光 DCU 的硬件架构和 CUDA GPU 有点像但生态完全不是一套。它的软件栈走的是 ROCm/HIP 路线和 AMD 的 GPU 同源所以 K8s 侧不能直接拿 Nvidia 那一套 device plugin 和 runtime 来用必须找海光对应的组件。我第一次接触的时候也天真地以为“反正都是扩展资源改个名字就行”。实际操作下来发现至少有三层不一样的第一层是容器运行时怎么把 DCU 设备文件注入进 Pod第二层是 K8s 怎么感知 DCU 的数量和健康状态第三层是上层 AI 平台怎么根据资源需求把 Pod 调度到对应节点。这三层没有一层能靠简单替换nvidia字符串解决。拿容器运行时举例。Nvidia 的方案是nvidia-container-toolkitnvidia-container-runtime通过 prestart hook 把 GPU 设备挂进容器。海光这边对应的是 hygon 的 container runtime 插件机制类似但设备文件、驱动目录、权限控制都不一样。如果直接把 Nvidia 的 runtime 指过来容器启动时根本找不到/dev/dri/renderD128这类设备节点更别提驱动库映射了。理解这层差异之后后面所有配置就有方向了。整卡接入、共享调度、vDCU 虚拟化本质都是在解决“资源怎么被上层看见”这一个问题只是颗粒度不同。1.2 接入方案选型整卡、共享、vDCU 怎么选在接 K8s 之前先得想清楚业务要什么。我们当时有三种诉求混在一起训练任务要整卡独占交互式开发要共享模型推理服务希望用虚拟化把小卡切给多个 pod。所以不能只接一种模式得把三种都接进去。整卡模式最简单一张 DCU 只能被一个 Pod 用适合大模型训练、精调这类吃满算力和显存的场景。缺点也明显如果跑小模型或者交互调试整卡资源浪费很厉害一张 64G 显存的卡跑一个 7B 模型推理利用率可能不到 20%。共享模式是允许一个 Pod 和另一个 Pod 同时用一张卡但前提是二者能通过驱动级的并发机制共存。海光 DCU 原生支持多进程共享所以在 K8s 里只要不做设备隔离多个 Pod 可以同时拿着同一物理卡的不同设备节点跑任务。这种方式适合显存放得下、算力需求不高的场景比如数据预处理、小模型批量推理。注意这里不涉及显存隔离风险是某个任务把显存打爆会影响同卡其他任务。vDCU 虚拟化则是对显存和算力做切分把一张物理卡切成若干个逻辑卡每个逻辑卡有自己的显存配额和算力上限。这种方式适合多租户场景也适合把大卡切成小卡给多个推理服务用。海光这边有两种 vDCU 模式一种是按显存隔离来切一种是按算力配比来切后面第 2 章会详细拆。选型上我比较推荐的组合是训练和生产级推理用整卡开发和调试用共享多租户或者长尾推理用 vDCU。这个组合能覆盖 90% 的日常需求而且配置路径不冲突可以在同一个集群里同时启用。1.3 组件链路从驱动到 K8s 扩展资源整个链路从下往上分四段驱动和工具链DCU 驱动、ROCm 运行库、dcu-smi工具负责让操作系统识别硬件并提供健康检查和监控数据。容器运行时hygon 的 container runtime hook负责在容器启动时注入 DCU 设备节点和驱动库。device pluginK8s 的扩展资源上报组件以 DaemonSet 方式运行在节点上把 DCU 数量、vDCU 数量上报给 kubelet。AI 平台CubeStudio 或同类平台负责把用户请求翻译成 K8s 工作负载并指定资源申请。哪一段出问题后面全白搭。所以实操时不要急着部署 AI 平台先把前三段逐层验证确认 K8s 节点上能看到hygon.com/dcu资源了再往上接平台。2. 核心细节解析与实操要点2.1 设备侧驱动、Runtime、dcu-smi 缺一不可先交代环境。我们这批节点的操作系统是麒麟 V10 SP3内核版本比较老K8s 是 1.28容器运行时是 containerd 1.7.x。海光 DCU 的驱动包和 ROCm 工具链版本有强绑定我们最终用的是厂商提供的 DCU 驱动 5.x 版本配套的运行时组件。驱动装完之后第一件事不是配 K8s而是确认dcu-smi能正常输出。正常情况下执行dcu-smi会看到卡号、型号、显存总量、温度、利用率这些信息类似nvidia-smi。如果这里看不到卡或者卡状态显 示 error后面都不用看了先排查驱动加载问题。容器运行时这边因为 containerd 本身不像 docker 那样直接支持 runtime hook需要在config.toml里增加一个 runtime 类。我们用的是一个叫hygon的运行时处理器配置完成后Pod 创建时显式指定runtimeClassName: hygon容器里就能看到 DCU 设备了。这里有一个关键点容易踩坑dcu-smi在宿主机上正常不代表容器里正常。必须用一个临时 Pod 进去执行dcu-smi确认容器内能看到同样的卡信息并且/dev/dri/下有对应的 render 设备节点。如果节点有了但里面没有多半是 runtime 没有正确挂载设备而不是驱动问题。2.2 资源侧device plugin 如何把 DCU 上报给 K8sK8s 本身不关心你是什么加速卡它只认扩展资源。GPU 的实现是nvidia.com/gpuDCU 的实现是hygon.com/dcu。device plugin 的作用就是把物理卡映射成这种扩展资源并通过 Unix Socket 向 kubelet 注册。海光的 device plugin 也是标准实现DaemonSet 跑在每个 DCU 节点上。配置里主要注意三个参数资源名称、设备扫描路径、vDCU 开关。我们最开始用默认配置上报的资源名总是不对后来发现是环境变量没设置改成HYGON_RESOURCE_NAMEhygon.com/dcu才正常。设备扫描路径也要注意。DCU 设备在/dev/dri/下如果插件扫描的是/dev/nvidia*那自然什么都扫不到。好在海光默认配置就是扫/dev/dri不需要额外改。部署完 device plugin 之后用kubectl describe node看 allocatable 字段如果出现hygon.com/dcu: 8说明整卡上报成功。这时候提交一个申请hygon.com/dcu: 1的测试 Pod能调度上去就说明整卡链路通了。2.3 虚拟化侧两种 vDCU 的原理差异和适用场景vDCU 是我这次花时间最多的地方。海光的 vDCU 不是简单改个标签就完事而是在驱动层做资源切分大致分两种。第一种是“显存隔离型”把一张物理卡按显存大小切成多份每个 vDCU 独占一段显存。这种模式隔离性强一个任务把显存写爆了不会影响同卡其他任务但算力是共享的都在同一张物理卡上。适合多租户环境也适合把大卡切成小卡给多个推理服务并发用。缺点是比较笨因为算力没有做配额限制某个任务的密集计算可能会抢其他任务的算力需要业务方错峰使用。第二种是“算力配额型”按时间片或者算力占比来切分每个 vDCU 拿到一定比例的算力但显存不做严格隔离。这种模式适合算力敏感型任务比如多个训练任务同时跑希望每个人稳定拿到 20% 的算力防止互相抢占。缺点是需要额外的调度机制如果切分粒度设置不合理小切分会导致显著的性能开销。这两种模式在 K8s 里的呈现方式也不一样。显存隔离型是把每个 vDCU 当成独立的资源实例上报比如一张 64G 的卡切 4 个 vDCU每个 16GK8s 看到的是hygon.com/vdcu: 4。算力配额型是通过给同一张卡配多个 vDCU 实例的方式每个实例带一个算力比例属性调度时按比例分配。实操中我建议先明确业务诉求再选模式如果是线上推理服务想提高卡利用率选显存隔离型如果是内部训练多人共用一台卡机选算力配额型。两种模式可以同时启用但注意它们会占用同一张物理卡的资源池切分总数不要超过卡的硬件限制否则驱动会拒绝创建 vDCU。3. 实操过程与核心环节实现3.1 整卡接入 K8s 的完整配置过程先说整卡。整卡的配置链路在设备侧没问题之后主要就是三个文件containerd 配置、device plugin DaemonSet、测试 Pod。containerd 这边的配置比较绕需要在/etc/containerd/config.toml的plugins.io.containerd.grpc.v1.cri.containerd.runtimes下新增一段 runtime 配置。为了不破坏原有配置我先备份再改每改一次都重启 containerd然后立刻跑一个 Pod 验证。device plugin 用的是海光提供的 yaml核心内容简化下来大致是这个样子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: containers: - name: hygon-device-plugin image: hub.hygon.cn/base/hygon-device-plugin:latest env: - name: HYGON_RESOURCE_NAME value: hygon.com/dcu securityContext: privileged: true volumeMounts: - name: device-plugin mountPath: /var/lib/kubelet/device-plugins - name: sys mountPath: /sys volumes: - name: device-plugin hostPath: path: /var/lib/kubelet/device-plugins - name: sys hostPath: path: /sys部署完成之后验证分两步。先看节点kubectl describe node node-name确认hygon.com/dcu出现在 Capacity 和 Allocatable 里。再跑一个简单 PodapiVersion: v1 kind: Pod metadata: name: dcu-test spec: restartPolicy: Never runtimeClassName: hygon containers: - name: dcu-test image: hub.hygon.cn/base/hygon-dcu-test:latest command: [dcu-smi] resources: limits: hygon.com/dcu: 1如果kubectl logs dcu-test能看到卡信息说明整卡链路已经通了。这一步成功之后后续的共享和 vDCU 都是在这个基础上做扩展所以别嫌慢一定要确保这一步稳定。3.2 vDCU 切分配的完整配置过程vDCU 的启用方式是通过 device plugin 的环境变量来控制的和整卡模式共用同一个 DaemonSet 镜像。我们启用的两种模式分别对应两套配置但不能同时跑在同一个插件实例里所以我用两套 DaemonSet 加节点标签的方式让不同节点跑不同模式。显存隔离模式的 plugin 配置要点env: - name: HYGON_RESOURCE_NAME value: hygon.com/vdcu - name: HYGON_VDCU_ENABLE value: true - name: HYGON_VDCU_MODE value: memory - name: HYGON_VDCU_COUNT value: 4这里HYGON_VDCU_COUNT4表示把每张物理卡切成 4 个等显存份额的 vDCU。因为每张卡规格一样所以每张卡上报 4 个 vDCU。部署之后节点的 allocatable 会显示hygon.com/vdcu: 32这种数字8 张卡乘 4。算力配额模式的 plugin 配置则变成env: - name: HYGON_RESOURCE_NAME value: hygon.com/vdcu - name: HYGON_VDCU_ENABLE value: true - name: HYGON_VDCU_MODE value: quota - name: HYGON_VDCU_COUNT value: 4 - name: HYGON_VDCU_QUOTA value: 0.25HYGON_VDCU_QUOTA0.25表示每个 vDCU 最多占用物理卡 25% 的算力。这样切 4 个实例正好铺满一张卡不会出现算力超卖。如果只想切 2 个高配 vDCU就把 COUNT 设为 2QUOTA 设为 0.5。vDCU 的 Pod 调度和整卡类似资源名换hygon.com/vdcuruntimeClassName 保持不变。有一个细节要留意如果同一节点上的 Pod 申请了不同类型的 vDCU插件会先给显存隔离型分配物理显存段再给算力配额型分配剩余资源顺序反过来可能导致创建失败。3.3 CubeStudio 接入 DCU 资源池的关键配置CubeStudio 是我们内部基于 K8s 封装的多租户 AI 平台。它本身的架构不复杂控制面负责用户、配额、资源池和应用模板数据面就是 K8s 集群而整个 DCU 适配的关键在于“把 K8s 扩展资源映射成平台资源池”。平台支持自定义资源池每个资源池绑定一个或多个 K8s 集群和一组资源规格。我们建了一个叫dcu-pool的资源池调度标签绑定到带dcuhygon节点上资源类型填hygon.com/dcu和hygon.com/vdcu。CubeStudio 侧的核心配置有三块资源池定义、配额模板、镜像仓库。资源池这里必须写对资源名大小写hygon.com/dcu是全小写写成Hygon.com/DCU在 K8s 校验层就过不去。配额模板上整卡按hygon.com/dcu计费vDCU 按hygon.com/vdcu计费分属不同套餐。镜像这块最容易踩坑。DCU 的镜像不是随便拉一个pytorch/pytorch就能跑必须用海光提供的 ROCm 基础镜像或者基于它再二次封装。我们在 CubeStudio 里为 DCU 单独配了一个镜像仓库地址用户的训练镜像统一在基础镜像上叠加自己的代码。平台侧真正接入的时候有个特别容易忽略的点AI 平台自带的 Dashboard 可能默认展示 GPU 资源DCU 的资源统计不会显示。为此需要在平台监控模块里加映射规则把hygon.com/dcu统计到“加速卡”分类下和 GPU 统一展示。不做这步的话业务方在界面上看不到资源会以为没接入成功。3.4 DeepSeek 在 DCU 上的部署实战硬件和平台都通了最后一步就是把业务跑起来。我们以 DeepSeek 的蒸馏版本为例模型选的是DeepSeek-R1-Distill-Qwen-7B一个 7B 参数的模型在单张 DCU 上就能跑非常适合做适配验证。先说镜像。推理我们用 vLLM 的 DCU 适配版本官方发布的镜像不一定包含海光 DCU 的 kernel 适配所以要用 vLLM 在海光 DCU 上编译过的镜像。编译过程比较久我们直接用了厂商提供的基础推理镜像然后在上面补了一层 Python 依赖。启动命令可以写成这样python -m vllm.entrypoints.openai.api_server \ --model /models/DeepSeek-R1-Distill-Qwen-7B \ --served-model-name deepseek-7b \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.9 \ --max-model-len 8192 \ --trust-remote-code几个参数要注意。--trust-remote-code在 DeepSeek 的某些版本里必须加不然加载配置会报错。--max-model-len一定要结合显存算不要无脑开 32768不然 KV cache 会爆显存。7B 模型在 64G 显存上跑 8192 长度的模型上下文是没问题的如果切了 vDCU 只有 16G 显存就要把长度降到 2048。部署到 K8s 我们直接用一个 Deployment申请hygon.com/dcu: 1镜像内部自动启动 vLLM 服务。然后通过 ClusterIP Service 暴露 8000 端口CubeStudio 侧配一个“模型服务”应用用户就可以通过统一入口调用 DeepSeek 了。调用验证curl http://service-ip:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: deepseek-7b, messages: [{role: user, content: 写一段冒泡排序的Python代码}], max_tokens: 256 }如果返回正常的补全内容说明 DeepSeek 已经成功跑在海光 DCU 上整个链路从硬件到平台到模型全部打通。这个部署思路同样适用于其他并行化程度不高的开源模型主要是把 vLLM 底层算子的 DCU 适配搞定上层工作量其实不大。4. 常见问题与排查技巧实录4.1 高频报错速查表这批适配做完我们把团队踩过的坑整理了一张速查表分享出来供参考现象可能原因快速排查手段Pod 调度失败显示hygon.com/dcu资源不足device plugin 未上报或节点标签不对kubectl describe node看 allocatable容器内dcu-smi命令不存在镜像没有安装 DCU 工具链换用海光基础镜像或手动安装容器内看到设备但初始化失败runtime hook 没有正确挂载驱动库检查 Pod 的 runtimeClassName 是否为 hygonhygon.com/vdcu变成 0 或上报数量不对vDCU 环境变量未生效或切分总数超限查看 device plugin 日志确认配置vLLM 启动时报找不到 HIP kernel镜像没有经过 DCU 编译适配换用 DCU 适配版 vLLM 镜像DeepSeek 模型加载后第一次推理很慢vLLM 在做预热和图模式编译等待数分钟再测或用 benchmark 脚本预热这张表只列了高频问题具体细节下面展开。4.2 性能与稳定性排查经验最容易被忽视的坑是 vDCU 切分后的显存和算力不匹配。我们有次把一张 64G 的卡切了 8 个 vDCU每个标称 8G但跑 DeepSeek 7B 硬是 OOM。后来发现虽然显存是按 8G 切了但驱动默认给每个 vDCU 留了额外的 reserved 空间实际可用只有 6G 出头。模型加载前先把torch.cuda.mem_get_info()打出来确认真实可用显存再配 vLLM 参数这个习惯能省很多排查时间。另一个是算力配额模式下的性能回退。理论上一张卡切 4 个 25% 配额的 vDCU每个任务应该能拿到接近单卡 1/4 的性能但我们实测发现某些算子回退严重个别矩阵乘法的实际吞吐只有单卡的 15%。原因比较深和 DCU 的调度单元竞争有关切分粒度过细会导致缓存和指令发射冲突。所以在生产环境我建议配额模式单卡最多切 4 份别切到 8 份切太多性能反而不如排队等整卡。还有个容易被忽略的稳定性问题整卡共享模式下两个 Pod 同时往一张 DCU 上写显存驱动不会报错但会互相拖垮算力。我们后来在 CubeStudio 的配额策略里做了限制——共享模式只允许申请hygon.com/dcu-shared重新映射的一个资源名并且限制了同卡 Pod 数量。一定不要把共享模式作为默认模式放开给用户否则线上任务会互相干扰。4.3 平台侧的联动排查技巧CubeStudio 接入之后偶尔会遇到“资源池可用但任务提交失败”的情况。这时候不要只盯着平台日志先绕过平台直接用 kubectl 提交一个同资源需求的 Pod如果 kubectl 能成功而平台失败问题出在平台侧的资源映射字段如果 kubectl 也失败问题出在 K8s 调度侧。另外平台会缓存集群资源状态有时候 DCU 驱动更新导致节点资源变化平台侧数据还是旧的。我们遇到过节点上有卡但平台显示为 0 的情况就是因为 device plugin 上报的资源名变了平台缓存没刷新。解决办法是在 CubeStudio 后台手动触发一次“刷新集群资源”或者等平台侧的定时同步周期跑完再看。设备健康检查也建议放到平台侧。DCU 长时间跑任务可能出现温度过高、驱动报错但 K8s 本身不会感知。我们用 DaemonSet 方式跑了一个 dcu-exporter定时读取dcu-smi的卡状态把异常卡上报给平台平台自动将节点标记为不可调度。这套机制上线之后硬件故障导致的推理失败少了一大半。5. 一些真实体会这次做完之后我自己最大的感受是国产加速卡接入 K8s 并没有想象中那么“玄学”整条链路其实和 NVIDIA 的思路保持了一致只是每个环节都有对应的替代组件关键是要耐住性子把底层逐层验证清楚。如果我一开始就直接让 CubeStudio 的业务方提交任务肯定会在容器运行时和资源上报这两个地方来回折腾远不如现在这样一层层从dcu-smi推到 Pod再从 Pod 推到平台来的快。最后再分享一个小技巧所有 device plugin、runtime 配置的 yaml 文件和 containerd 配置的变更都放进 Git 里管理并且给每个节点打上清晰的标签比如gpu-typehygon-dcu、vdcu-modememory。有人觉得这种基础设施配置文件没必要版本管理但一旦集群规模超过十台变更记录能让你少掉很多头发。这次适配之后我们已经把整套流程固化成了内部的 onboarding 文档新节点加入时照着跑一遍就能自动接入这也算是把一次踩坑经历沉淀成了团队资产。