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

海光DCU接入Kubernetes全攻略:从Device Plugin到vDCU虚拟化与DeepSeek部署

上个月我们接到一个内部任务把新到的一批海光 DCU 服务器接入现有 Kubernetes 集群再通过 CubeStudio 把这些算力以整卡、共享以及两种 vDCU 虚拟化模式开放给算法团队最后还要在集群里直接拉起 DeepSeek 推理服务。整套做下来踩了不少文档里没写清楚的坑也摸清了海光 DCU 在云原生环境里的脾气。这篇文章把我这段实操经验整理成一份可以直接照着做的接入笔记覆盖从驱动、Device Plugin、整卡调度到 vDCU 虚拟化和 CubeStudio 平台纳管再到 vLLM 部署 DeepSeek 的全链路适合正在做 DCU 集群建设、AI 平台适配或者模型推理服务化的同学参考。1. 接入链路全景DCU 从驱动到 K8s 调度要过哪几道关1.1 容器里能看到卡和“能被调度”是两回事很多第一次接触加速卡接入 Kubernetes 的同学第一反应是“把/dev/dri、/dev/kfd直接挂进容器不就完事了”。真实情况是这样确实能让容器里的程序访问到卡但调度器完全不知道集群里谁有卡、有几张卡、每张卡剩多少显存。结果是运维手工指定节点算法同学排队等资源利用率全靠缘分。Kubernetes 解决这个问题靠的是扩展资源Extended Resource加 Device Plugin 这套机制。扩展资源负责“上报”和“声明”比如节点上有几张 DCU就通过 kubelet 上报为hygon.com/dcu: 2。Device Plugin 则负责两件事一是把卡设备注册给 kubelet二是当 Pod 申请了扩展资源并被调度到节点后由它把对应的设备文件、环境变量真正注入到容器里。这里面有一个容易被忽略的细节扩展资源的调度是按“个数”来的不是按“显存大小”来的。如果你申请了hygon.com/dcu: 1调度器只会保证你拿到一张卡不保证这张卡还剩多少显存。这也是后面 vDCU 虚拟化要解决的问题之一。1.2 海光 DCU 接入 K8s 的完整四层链路以我们集群的成功实践来看从底层到应用层大概是这么一条链路第一层是节点驱动。海光 DCU 需要装对应的内核驱动模块通常装完后/dev/dri/下会出现renderD128/dev/hygon/或者类似目录下会暴露卡设备同时/dev/kfd也会出现这一块和 ROCm 生态的 AMD 设备路径很像。第二层是用户态运行时。容器里跑的进程要能调用 DCU需要 HIP/ROCm 运行库被打进镜像或者通过注入LD_LIBRARY_PATH的方式挂进去。我们实践下来更推荐直接把运行库打进基础镜像避免每次部署都依赖宿主机的库版本。第三层是 Device Plugin。它作为 DaemonSet 跑在每个节点上负责把设备信息上报给 kubelet同时处理设备注入。这一步做不好后面全是问题所以要先确认插件版本和你装的驱动版本匹配。第四层是平台调度。Device Plugin 上报后Kubernetes 自带的 kube-scheduler 就能靠扩展资源完成基本调度。CubeStudio 这类 AI 平台做的事情是在这之上再封装资源池、配额和任务提交逻辑让算法同学不需要手写 YAML。这四层每一层都可能成为接入失败的原因。所以我建议不要急着把平台接进来先把前三层用命令行手工验证通再让平台层介入。2. 环境摸底与版本对齐别急着部署插件2.1 节点侧检查清单和版本匹配表海光 DCU 的接入最忌讳的就是驱动、运行时、插件三个版本各自为政。我们第一批节点就踩过驱动版本和 Plugin 版本不匹配的坑导致插件报错节点容量一直刷不出来。下面这张表是我们在实际环境中稳定运行的一组版本匹配关系可以作为参考组件检查项目常见问题操作系统Kernel 版本与驱动兼容建议 4.19内核太老导致驱动编译失败DCU 驱动节点上执行hygon-smi或rocm-smi能否看到卡看不到卡先查dmesg多半是驱动没加载HIP/ROCm 运行时/opt/rocm/bin/hipcc --version版本与驱动匹配版本不匹配时容器内hipSetDevice失败Device Plugin资源名、设备路径与驱动实际暴露路径一致路径写错会导致节点有卡但 Pod 起不来容器运行时containerd / docker 能访问设备节点未放行设备时容器内无权限我建议把这张表做成你的“环境基线”每次换驱动版本都要重新对齐一次。2.2 用动态探测确认节点上的设备路径在部署 Device Plugin 之前先用最简单的方式确认设备到底在哪个路径。我们在节点上执行ls /dev/dri ls /dev/kfd hygon-smi --show如果hygon-smi能显示每张卡的 PCIe ID、显存大小和利用率说明驱动层是健康的。再看/dev/dri/renderD128是否存在这个节点文件是容器运行时注入设备时的关键路径。确认完设备路径后还需要给节点打上标签。标签的主要目的是让 scheduler 和 CubeStudio 能识别“这台机器是 DCU 节点而且是某型号的 DCU”。我们使用的标签格式如下kubectl label node dcunode01 accelerator/hygon-dcutrue kubectl label node dcunode01 dcu-modelZ100 kubectl label node dcunode01 dcu-memory32G这些标签在后面做节点亲和性、资源池划分时会非常有用建议一开始就定好命名规范别等平台接入了再补。3. 整卡接入Device Plugin 上报和第一个 DCU Pod3.1 部署海光 DCU Device Plugin整卡模式是所有接入方式的基础。先把整卡跑通再去做 vDCU 虚拟化排查问题会轻松很多。海光 DCU 的 Device Plugin 一般以 DaemonSet 方式部署关键点在于挂载目录和资源名的配置。我提供一个简化后的部署 YAML实际安装包以官方提供的为准但结构和下面这个类似apiVersion: apps/v1 kind: DaemonSet metadata: name: hygon-dcu-device-plugin namespace: kube-system spec: selector: matchLabels: name: hygon-dcu-device-plugin template: metadata: labels: name: hygon-dcu-device-plugin spec: hostNetwork: true containers: - name: device-plugin image: registry.example.com/hygon/device-plugin:v1.0 env: - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName securityContext: privileged: true volumeMounts: - name: device-plugin mountPath: /var/lib/kubelet/device-plugins - name: sys mountPath: /sys readOnly: true - name: dev mountPath: /dev volumes: - name: device-plugin hostPath: path: /var/lib/kubelet/device-plugins - name: sys hostPath: path: /sys - name: dev hostPath: path: /dev这里privileged: true是必须的因为插件需要访问宿主机的设备节点。hostNetwork: true不是绝对需要但我们遇到有些版本插件要用节点 IP 做健康检查开着更省事。部署完成后在节点上执行kubectl get pods -n kube-system | grep hygon-dcu如果 Pod 是 Running 状态就去检查节点容量有没有更新。3.2 验证节点容量是否报告正确检查节点容量我习惯用kubectl describe node重点关注Capacity和Allocatable两段kubectl describe node dcunode01 | grep -A 20 Capacity正常情况下你应该能看到类似这样的字段hygon.com/dcu: 2这里有两个容易踩的坑。第一扩展资源名必须符合 DNS 命名规范不能有下划线不能大写必须是域名/资源名的格式。第二Allocatable里的数量可能会比Capacity少因为 kubelet 默认会考虑给系统预留的资源这属于正常现象。如果Capacity一直没出现别急着改插件配置先把插件日志翻出来看kubectl logs -n kube-system $(kubectl get pods -n kube-system | grep hygon-dcu | awk {print $1})日志里通常会有很明确的报错信息比如“找不到设备”或者“socket 连接失败”。这类问题大概率是插件容器没有权限访问/dev目录检查 DaemonSet 的securityContext配置即可。3.3 第一个 DCU Pod 应该怎么定义节点容量刷新后直接提交一个最小化的测试 Pod。这个 Pod 只做一件事进入容器后执行rocm-smi或hygon-smi确认容器内能看到卡。apiVersion: v1 kind: Pod metadata: name: dcu-test spec: restartPolicy: Never nodeSelector: accelerator/hygon-dcu: true containers: - name: dcu-test image: registry.example.com/hygon/rocm-base:5.7.0 command: [/bin/bash, -c] args: - rocm-smi; sleep 30 resources: limits: hygon.com/dcu: 1提交后观察事件kubectl describe pod dcu-test如果 Pod 正常调度并看到rocm-smi输出了一张卡的型号和显存说明整卡链路已经通了。这一步结束Kubernetes 已经能把 DCU 当普通扩展资源来做最基本的调度了。4. vDCU 虚拟化的两种模式整卡之外的选择题4.1 为什么整卡模式不够用整卡模式虽然简单但实际使用中资源浪费相当可观。我们内部跑过一批小型推理任务模型本身的显存占用只有 6-8GB却被迫占用一整张 32GB 的卡还有算法同学想在开发环境做快速实验每天都在“抢卡”而不是“用卡”。vDCU 的作用就是把一张物理 DCU 拆成多个虚拟设备让多个任务共享同一张卡。市面上不同厂商实现方式各异但从用户视角看海光 DCU 的 vDCU 大体分两种模式独占模式和共享模式。理解这两种模式的本质差异直接决定你如何配置资源。4.2 vDCU 独占模式显存切分但彼此透明独占模式可以理解为“显存层面的硬切分”。一张 32GB 的 DCU可以按配置切出 4 个 vDCU每个 vDCU 独享 8GB。每个 vDCU 之间在显存和计算单元上尽量隔离某个任务崩溃不会导致同卡的其他 vDCU 直接不可用。在 Kubernetes 里使用这种方式通常需要在 Device Plugin 或者配套的 vDCU Manager 中配置分片方案。比如我们的配置里有半张卡、四分之一张卡这样的规格资源名也做了区分limits: hygon.com/vdcu-exclusive: 1配套的 vDCU 管理组件会根据 Pod 请求的规格找到一张剩余资源足够的物理卡然后分配对应的虚拟设备并注入容器。容器里看到的是一张 8GB“逻辑卡”跟整卡的使用方式没有区别。这种模式适合对显存边界要求严格、需要避免互相干扰的场景比如小模型微调、对稳定性要求高的推理服务。4.3 vDCU 共享模式时间片与动态调度共享模式则更接近“超卖 时分复用”。多个 vDCU 可以跑在同一张物理卡上共享显存和算力由驱动层或运行时负责任务的切换。共享模式的优点是利用率最高尤其在大量短生命周期任务并存的场景下一张卡可以同时承载好几个 Pod空转时间大幅降低。代价是性能确定性差某几个任务同时打满算力时其他任务会明显变慢甚至出现超时。如果你要让算法团队用它来跑 Notebook、调试代码、批量离线推理共享模式体验很好但如果跑的是在线服务建议还是用独占模式来兜底。4.4 两种模式的对照选型我把两种模式的差异整理成一张表方便你在平台资源池划分时做决策对比维度vDCU 独占模式vDCU 共享模式显存隔离固定切分互不侵占动态共享可能互相挤占性能确定性高适合在线推理低波动大适合离线任务利用率中碎片化后仍有浪费高适合大量小任务部署复杂度需要显式规划分片规格需要关注超卖和 OOM典型场景模型微调、在线推理Notebook、批量实验、流水线测试如果你问我怎么选我的建议是整卡 vDCU 独占 vDCU 共享三种规格同时开放对应不同作业类型。整卡给大模型训练独占 vDCU 给推理和小规模微调共享 vDCU 给算法同学日常开发。这样平台资源池能错开不会出现一个跑批任务把在线推理打挂的问题。5. CubeStudio 平台纳管让算法同学不碰 YAML 也能用卡5.1 平台如何拿到 DCU 资源信息CubeStudio 这类 AI 平台和 Kubernetes 之间的对接核心是通过 kube-apiserver 读取集群资源和事件。平台需要有权限访问节点信息、Pod 信息、CRD 信息才能把算力情况展示到 UI 上。我们在接入时先在集群里给 CubeStudio 所在的服务账号ServiceAccount绑定了一个只读权限的 ClusterRole让它能看到 DCU 节点上的扩展资源。接下来最重要的一个步骤是让平台能识别“DCU 资源”和“CPU/内存”不同。绝大多数平台在展示节点资源时默认只关心 CPU、内存和 GPU如果插件的资源名是hygon.com/dcu而平台的资源类型枚举里没有这个字段UI 上是不会显示算力规格的。我们的做法是在平台配置里增加“自定义资源”映射把hygon.com/dcu、hygon.com/vdcu-exclusive、hygon.com/vdcu-shared都注册成 GPU 类资源。5.2 资源池、队列和 Quota 设计平台层把算力暴露给用户前一定要先划好资源池。我们的划分逻辑是按作业类型分队列整卡队列queue-dcu-whole默认最大允许申请 8 卡主要跑大模型预训练、微调任务。vDCU 独占队列queue-dcu-exclusive资源配额为 8 个 8G vDCU跑在线推理。vDCU 共享队列queue-dcu-shared面向 Notebook 和短任务总量很大但允许超卖。Namespace 配额用来兜底。我们在每个队列对应的 Namespace 下都配置了 ResourceQuotaapiVersion: v1 kind: ResourceQuota metadata: name: quota-dcu-exclusive namespace: ai-exclusive spec: hard: hygon.com/vdcu-exclusive: 8这样能防止算法同学在提交任务时一次性把整个队列的资源都申请走其他任务永远排不上。5.3 Notebook 任务和训练任务的提交方式CubeStudio 通常自带 Notebook 模块算法同学直接在页面上选择“资源规格DCU 共享 8G”平台后端会帮他们生成一个 Jupyter Pod。我们当时给的模板简化后大概长这样apiVersion: v1 kind: Pod metadata: name: notebook-abc namespace: ai-shared labels: app.kubernetes.io/component: notebook spec: containers: - name: jupyter image: registry.example.com/hygon/dcu-pytorch:2.1.0 resources: limits: hygon.com/vdcu-shared: 1 ...训练任务的提交则复杂一些。如果是单机多卡任务比如申请 4 张整卡Kubernetes 默认调度器会尽量把 Pod 放到同一节点但严格保证前四张卡都在同一节点通常还要配置节点亲和性或者在平台侧做调度约束。我们在 CubeStudio 里对多卡任务做了“张数匹配”申请 4 张整卡的任务会寻找剩余整卡数大于等于 4 的节点再通过 nodeAffinity 锁到具体节点上。这里有一个经验在平台资源足够充裕之前少用“平台自动分配节点”的模式尽量对整卡多卡任务做节点亲和性约束否则常见问题是一个任务跨了两台节点算法同学还跑来问为什么多卡通信这么慢。6. 用 vLLM 在集群里跑 DeepSeek从镜像到 API6.1 选型确认vLLM 的 ROCm 路线能吃下 DeepSeek 吗集群 DCU 的调度和虚拟化都打通之后我们第一个正式业务是部署 DeepSeek 系列推理服务。选型上没有太多悬念直接用了 vLLM 的 ROCm 版本。原因很简单vLLM 有 OpenAI 兼容的 APIDeepSeek 系列模型在 HuggingFace 上有标准权重vLLM 社区对 DeepSeek 的模型结构适配也比较及时。在 DCU 上跑 vLLM本质是依赖海光 DCU 对 ROCm 生态的兼容性所以镜像选择上优先看有没有rocm标签的 vLLM 镜像。如果没有现成镜像可以基于官方 vLLM 源码自行编译编译时需要指定 HIP 平台。我们内部是直接从厂商提供的 ROCm 5.7 基础镜像上构建的。构建完成后记得在镜像里加上HSA_OVERRIDE_GFX_VERSION这种和计算架构相关的环境变量因为不同型号的 DCU 对应的 GFX 版本不同。关于这一点建议你根据实际 DCU 型号向驱动厂商确认不要盲目抄网上的值设错会导致程序运行时报错或者性能极差。6.2 DeepSeek 推理 Deployment 的完整 YAML选定推理框架后我把我们的 DeepSeek 推理服务 YAML 简化了一下。下面是核心部分注意资源限制那一段直接声明 DCU 资源apiVersion: apps/v1 kind: Deployment metadata: name: deepseek-vllm namespace: ai-exclusive spec: replicas: 1 selector: matchLabels: app: deepseek-vllm template: metadata: labels: app: deepseek-vllm spec: nodeSelector: accelerator/hygon-dcu: true containers: - name: vllm image: registry.example.com/vllm/vllm-openai:rocm-latest command: [/bin/bash, -c] args: - - python -m vllm.entrypoints.openai.api_server --model /models/deepseek-ai/DeepSeek-R1-Distill-Qwen-7B --served-model-name deepseek-r1-7b --tensor-parallel-size 1 --max-model-len 32768 --gpu-memory-utilization 0.85 --port 8000 ports: - containerPort: 8000 env: - name: HIP_VISIBLE_DEVICES value: 0 resources: limits: hygon.com/dcu: 1 readinessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 120 periodSeconds: 10有几个细节值得说明。HIP_VISIBLE_DEVICES在这里是控制容器内看到第几张卡Device Plugin 整卡模式下一般会自动注入但显式声明能避免某些镜像默认枚举设备时出错。--max-model-len一定要根据显存来评估。我们第一次设成 65536结果单卡 32GB 显存根本放不下初始化直接 OOM后来降到 32768 才稳定。字段太长背后的计算逻辑并不复杂模型权重加 KV Cache 的总占用必须小于你申请到的显存共享模式下尤其要谨慎。.85的gpu-memory-utilization是经验值给运行时和碎片留一点余量整卡跑可以到 0.9vDCU 独占 8G 的小规格任务我建议降到 0.8。6.3 服务暴露和 API 验证Deployment 创建后再创建一个 Service让平台内部其他服务都能访问apiVersion: v1 kind: Service metadata: name: deepseek-vllm-svc namespace: ai-exclusive spec: selector: app: deepseek-vllm ports: - port: 8000 targetPort: 8000 type: ClusterIP验证时直接用 curl 调 OpenAI 兼容接口curl -X POST http://deepseek-vllm-svc.ai-exclusive:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: deepseek-r1-7b, messages: [{role: user, content: 你好做一句自我介绍}], temperature: 0.7 }如果能正常返回文本说明 DCU 上的推理链路已经彻底跑通。这个服务地址还可以直接配到 vscode 的 Continue 插件、Codex 等工具的 OpenAI 兼容 Base URL 里等于给团队提供了一个内网可用的 DeepSeek API。6.4 共享模式和 vDCU 场景下的稳定性问题最后再说一个我们实际遇到的问题。最初为了让更多人同时试用 DeepSeek我把一部分推理实例放到了共享模式 vDCU 上。白天低峰期一切正常等跑批任务一上来分担到同一张物理卡上的多个推理请求延迟直接翻了几倍甚至出现客户端超时。排查后发现是共享模式下算力竞争导致的。vDCU 共享模式适合短任务但不适合长时间占用的服务。我们把所有在线推理服务迁到整卡和独占 vDCU 后问题就消失了。这也验证了我前面的观点在线服务和离线批量任务最好从一开始就在队列层面隔离。给做同样事情的同学一个建议在平台设置里加上“服务类型在线/离线”的标记在线服务默认只能选整卡或 vDCU 独占规格离线作业默认走 vDCU 共享规格。这个规则比任何事后人工干预都有效。7. 整个适配过程里最值得记住的几个经验整个项目做下来我最大的体会有三点。第一DCU 接入 Kubernetes 本身并不复杂复杂的是版本环境。驱动、ROCm runtime、Device Plugin、PyTorch/vLLM 镜像任何一个版本不匹配都会浪费你好几天时间。所以每次变更前先记录四张表节点系统版本表、驱动版本表、插件版本表、镜像基础版本表。靠记录而不是靠记忆。第二扩展资源只是“可用性”的入口不是“合理性”的保证。Kubernetes 原生的调度器不会感知显存碎片、不会感知卡间拓扑、不会感知 vDCU 竞争这些都需要你在平台侧或调度策略上补足。如果你只做简单的 Demo靠原生调度器就够了如果面对几十上百个任务最好还是通过 CubeStudio 这类平台把资源池、队列、Quota 管起来。第三不要在共享模式上跑在线服务。这个坑我说过很多次但每次都有同事想挑战一下最后都灰头土脸改回独占模式。不是说共享模式没用而是它更适合“容错度高”的任务。明确这条边界你的集群稳定性会直接上一个台阶。最后再分享一个小技巧每次改完 Device Plugin 或者 vDCU 配置不要直接大批量提交任务先起一个sleep 600的测试 Pod 占住资源然后手动跑一遍rocm-smi确认显存和算力符合预期再删掉。这一步虽然简单但能帮你过滤掉至少一半的“任务跑到一半 OOM”问题。
分享:

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

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