ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

海光DCU接入Kubernetes实战:从Device Plugin到DeepSeek推理

海光DCU接入Kubernetes实战:从Device Plugin到DeepSeek推理 搞国产加速卡上 Kubernetes说实话是一件比想象中更“磨人”的事。海光 DCU 虽然是典型 ROCm 路线但只要在平台层、驱动层、调度层各就各位它也能像普通 GPU 一样被 K8s 当成资源调度甚至在 CubeStudio 这种 AI 平台上直接跑起 DeepSeek 这类大模型。这篇文章就是我近期完整做完一轮“海光 DCU 接入 Kubernetes CubeStudio 平台 跑起来 DeepSeek”的实操记录内容包括整卡接入、共享模式、两种 vDCU 虚拟化模式的配置和选型判断以及最后把模型服务发布到平台给团队使用的落地细节。适合正在做 AI 平台、K8s 资源池化或者在大模型推理链路上折腾国产算力的人参考。1. 接入前的整体思路与资源模型设计1.1 为什么不是“装个驱动就能跑”很多第一次接触海光 DCU 的人第一反应是“这就是一块国产卡像 NVIDIA 一样装驱动就能用”。这句话对一半。单机上的确是驱动 工具链的事但一旦要接进 Kubernetes就要多出两层事情调度层和设备层。Kubernetes 本身不认识 DCU 这个异构资源。它只会调度 CPU、内存这类原生资源其他加速卡都必须通过 Device Plugin 机制把设备数量注册成 Extended Resource。也就是说你得有一个 daemonset 跑在每个 DCU 节点上由它通过/var/lib/kubelet/device-plugins/这个 unix socket 向 kubelet 上报“我这个节点有几张海光 DCU”。kubelet 再把数量透传给 scheduler之后 pod 里写resources.limits和requests.hygon.com/dcu: 1才能被正常调度。设备层更隐蔽一些。DCU 走的是 ROCm 生态容器起来之后光有设备节点不够镜像里还得有对应版本的 ROCm 库容器运行时也要把/dev/kfd、/dev/dri这类设备节点映射进容器。很多部署翻车不是调度问题而是容器内根本没有正确暴露 DCU 设备程序直接报 “no such device”。所以接入前你要先明确一条链路驱动就位 → 设备插件注册 → 节点资源上报 → 调度器感知 → 容器运行时映射设备节点 → 镜像里有匹配的 ROCm 库。每一环缺了你在上层做得再漂亮任务也起不来。1.2 整卡、共享、vDCU 虚拟化三种资源模型怎么选资源模型这一块一定要在接入前和算法团队对齐。我这次落地时把 DCU 资源拆分成了三种用法第一是整卡模式。一个 pod 独占一张 DCU资源名用hygon.com/dcu申请数量只能是非负整数。好处是简单、稳定、性能不受邻居影响适合大模型推理、多卡并行训练这种强占用场景。坏处也明显如果模型很小整卡的算力和显存就白白浪费了大半。第二是共享模式或者叫时间片共享。设备插件在单张物理卡上注册出多个虚拟资源分片比如一张卡拆成 10 份每份资源名是hygon.com/vdcupod 可以申请0.2、0.5、1这种小于 1 的值。底层其实还是在同一张物理卡上排队轮流用适合 Notebook、小规模调试、轻量推理这类负载。这种模式对显存没有物理隔离你需要在插件侧配置显存上限否则多个 pod 同时申请大显存总量会直接超卖甚至 OOM。第三是 vDCU 空间分割相当于把一张物理 DCU 按“显存 算力配额”切成多份每份是独立的虚拟设备。这个和 NVIDIA 的 MIG 比较像隔离粒度更硬性能更可控适合严格划分资源边界的生产场景。这里要说明一点不同厂商、不同版本的 vDCU 实现差异其实不小。有的只在软件层做分片不真正隔离 L2 和显存控制器有的做得很深虚拟设备之间几乎互不影响。我建议你在备注里只写“这是我们内部基于海光 DCU 虚拟化方案的配置”不要指望抄一份配置就能在所有集群通用。1.3 CubeStudio 在整个链路里的位置CubeStudio 这类 AI 平台我的理解是它把 Kubernetes 的底层能力包装成算法工程师能懂、能直接用的界面和 API。它下面还是 namespace、pod、service 这一套只是上层多了项目工作空间、Notebook、训练任务、模型服务这类概念。所以接入 DCU 这件事实际上可以拆成三层来推进设备层也就是驱动、工具链、容器运行时对 DCU 的支持这一层我在 1.1 已经说了。集群层就是在 K8s 上部署设备插件把 DCU 变成可调度的标准资源。平台层就是在 CubeStudio 里把 DCU 资源映射到用户可见的配额和模板上让算法工程师在界面上点一个“使用 1 张 vDCU”就能跑 Notebook 或者发推理服务。这三层里最容易被忽略的是平台层。因为即使集群里已经能调度 DCUCubeStudio 的配额体系如果不把hygon.com/dcu加进去用户在界面创建 Notebook 时根本申请不到这个资源最后还是会绕回来找你“手动运维”。我在第 4 部分会专门讲怎么在平台层把这些资源透传出去。2. 整卡模式接入实操2.1 基础环境准备与验证顺序开始接 K8s 之前我习惯先在一台裸节点上把 DCU 单机环境跑通。这里的顺序是死的先装驱动再验证 ROCm最后跑一个简单算子测试比如 torch 检测设备数量。需要注意的点是驱动和 ROCm 库的版本要严格对应。海光 DCU 的上层软件栈基本是跟随 ROCm 版本演进如果宿主机核心驱动是某一个版本容器镜像里的 ROCm 库是另一个版本很容易出现程序起不来或者算子计算结果异常。建议所有基础镜像都从一个专门的镜像仓库拉取不要让大家随手docker pull一个带 CUDA 的镜像然后硬跑。单机验证没问题之后我再给节点打上 label 和 taint比如kubectl label node cn-dcu-01 hygon-dcutrue kubectl taint nodes cn-dcu-01 dcus-onlytrue:NoSchedule打 taint 的意义在于避免普通业务 pod 被随机调度到 DCU 节点上。DCU 节点上的显存和算力都是给 AI 负载用的普通服务跑上去不光浪费还可能因为兼容性问题直接出错。你要在后续的 DCU 相关 pod 里加上对应的 tolerations确保只有明确声明需要 DCU 的 pod 才能落到这些节点上。验证单机环境的命令也不复杂核心就几个# 检查驱动模块 lsmod | grep hygon # 检查 ROCm 是否识别设备 rocm-smi --showproductname # 检查 PyTorch 是否抓到 DCU python3 -c import torch; print(torch.cuda.is_available(), torch.cuda.device_count())ROCm 生态里torch.cuda这个接口是兼容的很多代码不需要改就能跑但它内部走的是 HIP 转换层。能识别设备和能正常跑算子中间还是有距离的建议用一个小矩阵乘或者卷积算子实测一遍。2.2 部署整卡 Device Plugin设备插件的部署方式和 NVIDIA 的 nvidia-device-plugin 基本一致我用的是一个标准的 DaemonSet关键配置如下apiVersion: apps/v1 kind: DaemonSet metadata: name: hygon-dcu-plugin namespace: kube-system spec: selector: matchLabels: app: hygon-dcu-plugin template: metadata: labels: app: hygon-dcu-plugin spec: hostNetwork: true tolerations: - key: dcus-only operator: Exists containers: - name: hygon-dcu-plugin image: registry.internal/hygon/hygon-dcu-plugin:latest securityContext: privileged: true volumeMounts: - name: device-plugin mountPath: /var/lib/kubelet/device-plugins - name: dcu-dev mountPath: /dev/hygon volumes: - name: device-plugin hostPath: path: /var/lib/kubelet/device-plugins - name: dcu-dev hostPath: path: /dev/hygon这个插件的核心逻辑就是扫描宿主机上的 DCU 设备节点然后通过 Device Plugin 协议向 kubelet 注册资源。它的资源名通常是hygon.com/dcu具体名称以你拿到的插件版本为准但注册逻辑是一样的。部署之后先别急着跑模型先确认资源有没有被正确上报kubectl describe node cn-dcu-01 | grep -A5 Resource如果hygon.com/dcu出现在 Capacity 里说明设备插件已经注册成功。如果没出现优先看插件的日志以及宿主机/var/lib/kubelet/device-plugins/目录是否可写。这两个是最高频的失败点。2.3 验证整卡调度与多卡申请资源上报成功之后用一个最简单的 pod 验证调度链路apiVersion: v1 kind: Pod metadata: name: dcu-verify spec: tolerations: - key: dcus-only operator: Exists containers: - name: verify image: registry.internal/rocm/hygon-base:latest command: [rocm-smi] resources: limits: hygon.com/dcu: 1这里注意requests和limits里都要写hygon.com/dcu。设备插件注册的是 Extended Resourcekubelet 只会按 limits 来分配设备,但如果你不写 requests调度器在计算节点资源余量时可能会出错。建议两个都写保持一致。多卡申请也是同样的方式hygon.com/dcu: 4就能在一个 pod 里拿到 4 张卡。但这里有一个我在实际使用中踩过的坑如果集群里有多个 DCU 节点调度器默认不关心这 4 张卡是不是在同一个节点上因为一个 pod 只会调度到一个节点而 kubelet 在分配设备时如果一张卡被别的 pod 占用了它就认为这个节点不满足条件。逻辑上没问题但会有一些奇怪的“想不到为什么卡住”的场景比如另一张卡被一个小任务占着导致一个大任务一直 Pending。这时用kubectl describe pod看事件基本都能看出来是资源不满足。2.4 整卡模式下的几个坑整卡模式看上去简单实际推进中我碰到过几类问题这里集中说一下。第一类是驱动和容器镜像版本不匹配。宿主机驱动是 A 版本镜像里的 ROCm 库是 B 版本程序一启动就报设备初始化失败。排查方法很简单宿主机和容器内分别跑一次rocm-smi对比输出是否正常。如果宿主机正常、容器内报错先确认镜像里的 ROCm runtime 版本再确认设备节点是不是被容器运行时正确映射进去了。第二类是容器内看不到设备。这个和 map 方式有关系。整卡模式下插件一般会把宿主机/dev/kfd、/dev/dri/*映射进容器但如果 pod 里没有声明这些设备的权限或者容器运行时配置了设备白名单DCU 一样会“消失”。我习惯在验证 pod 里加一句ls -l /dev/kfd先可视化确认设备节点在不在。第三类是共享场景下整卡资源被超卖。虽然整卡模式下hygon.com/dcu是整数资源但如果同一张卡以某种方式被多个 pod 共享插件会做资源扣减。这时候要特别注意插件配置里的“每卡共享数”。比如一张卡被分成 4 份插件注册给 kubelet 的资源总量是 4 而不是 1如果你在 pod 里写hygon.com/dcu: 1实际拿到的是 1/4 卡而不是整卡。很多团队的第一次事故就是从这里开始的。3. 共享与 vDCU 虚拟化配置解析3.1 什么时候需要共享和虚拟化整卡好用但贵且浪费。我们这边实际的负载画像里很大一部分是算法工程师调试代码、跑小规模数据实验、临时起一个推理服务验证效果。这类负载的算力需求可能只有整卡的 20%~30%如果每人独占一张卡资源很快就见底了。共享和 vDCU 虚拟化解决的就是这个“资源碎片”问题。把一张大卡切成多份每份分给不同用户或者不同任务资源利用率能明显上去。我这次实际落地的情况是同一批节点没做共享之前只够跑 8 个大任务做了时间片共享之后同时能支撑 20 多个中小任务代价是部分任务 latency 变长。但共享不是没有代价。最直接的影响是性能稳定性。时间片共享模式下多个任务在物理设备上排队执行遇到算力饥饿型任务其他人的执行时间会明显变长。所以做共享之前一定要和算法团队约法三章哪些任务必须整卡哪些任务可以共享哪些任务可以接受虚拟化后的性能波动。3.2 第一种 vDCU时间片共享模式时间片共享的原理一句话概括就是“排队轮流用 GPU”。设备插件在单卡上注册出多个虚拟资源片比如一张卡切 10 片kubelet 上报hygon.com/vdcu: 10pod 申请hygon.com/vdcu: 1就表示拿到 1/10 的卡。插件侧的核心配置大概是这样的我这次用的方案是在插件环境变量里指定共享模式apiVersion: apps/v1 kind: DaemonSet metadata: name: hygon-vdcu-time-slice-plugin namespace: kube-system spec: selector: matchLabels: app: hygon-vdcu-time-slice template: metadata: labels: app: hygon-vdcu-time-slice spec: hostNetwork: true tolerations: - key: dcus-only operator: Exists containers: - name: vdcu-plugin image: registry.internal/hygon/hygon-vdcu-plugin:latest env: - name: VDCU_MODE value: time-slice - name: DEFAULT_VDCU_COUNT value: 10 - name: DEFAULT_MEMORY_PER_VDCU value: 4Gi securityContext: privileged: true volumeMounts: - name: device-plugin mountPath: /var/lib/kubelet/device-plugins volumes: - name: device-plugin hostPath: path: /var/lib/kubelet/device-pluginsDEFAULT_VDCU_COUNT决定一张卡暴露多少份虚拟资源DEFAULT_MEMORY_PER_VDCU决定每份虚拟设备给 pod 的显存上限。这里必须说清楚时间片共享模式本身不隔离显存。多个 pod 理论上都能看到完整物理显存只是通过插件限定了“可以分配多少”真正能不能限制住要看插件的实现深度。保守的做法是十份虚拟设备的内存配额加起来不要超过物理卡显存比如 64GB 卡每份给 4Gi就是 40Gi留 24Gi 的余量做缓冲。pod 申请方式很简单resources: limits: hygon.com/vdcu: 2这表示申请 2 份 vDCU也就是一张物理卡 20% 的时间片份额。3.3 第二种 vDCU空间分割模式空间分割模式是我更推荐给生产环境的一种用法尤其是当你有明确的“多租户隔离”需求时。它的原理是把物理 DCU 的显存和计算资源按照固定比例切分成多个虚拟设备每个虚拟设备有独立的显存区域和算力配额同一物理卡上的不同虚拟设备之间耦合更小性能和稳定性都远好于时间片。空间分割模式下物理卡之间的资源不再是一份份浮动的“时间”而是一个个固定的“切片”。我这边拿到的插件版本里可以通过一个分区配置来定义每张物理卡怎么切分apiVersion: v1 kind: ConfigMap metadata: name: hygon-vdcu-partition namespace: kube-system data: partition.yaml: | physical-devices: - address: 0000:42:00.0 slices: - name: slice-a memory: 32Gi compute: 0.5 - name: slice-b memory: 16Gi compute: 0.25 - name: slice-c memory: 16Gi compute: 0.25配置里address是物理卡在 PCIe 总线上的地址memory是分给这个虚拟设备的显存compute是算力配额比例。三点加一起不能超过 100%。像上面这个是 64GB 卡32 16 16 正好 64Gi算力 0.5 0.25 0.25 正好 1.0。空间分割模式下pod 的申请更接近“我要一个多大显存的设备”resources: limits: hygon.com/vdcu-memory: 32Gi当然不同插件暴露的资源名不一样有的用hygon.com/vdcu有的用hygon.com/vdcu-memory你用的时候看清楚厂商文档就行。整体思路不变虚拟资源被切分得越细资源利用率越高但单任务能用的最大算力和显存就越受限。3.4 两种虚拟化的对比与选型做选型之前我把两种模式的关键差异放一起对比过维度时间片共享空间分割隔离粒度时间维度共享任务轮流执行显存、算力配额物理切分显存隔离通常不隔离靠插件配额控制每份虚拟设备有独立显存上限性能稳定性受邻居任务影响大相对稳定接近物理隔离配置成本低一个 daemonset 即可较高需要定义物理卡切分计划适用场景Notebook、调试、低优先级任务多租户生产环境、推理服务我的建议是如果团队还在摸索阶段先从时间片共享起步。它配置简单插件重启、切换模式都不影响物理卡出了问题好回滚。等负载模型清晰了哪些服务要稳定 QoS、哪些用户可以共享资源都摸清楚之后再把核心推理服务迁到空间分割模式上。另外强调一点无论哪种模式容器内的HIP_VISIBLE_DEVICES相关逻辑都可能需要适配。vDCU 是虚拟出来的逻辑设备容器里看到的设备索引和物理设备不一定一一对应算法同学习惯性地写死设备号可能会拿到错误的虚拟设备。这个问题我在第 6 部分会给出排查方法。4. CubeStudio 平台对接与资源透传4.1 CubeStudio 的资源模型理解CubeStudio 在我的理解里它的核心是把 Kubernetes 的 namespace 抽象成“项目空间”每个空间里有配额、用户权限、预置模板。算法工程师不直接接触 pod 和 deployment而是在界面上创建 Notebook、提交训练任务、发布推理服务。这意味着平台层要把hygon.com/dcu、hygon.com/vdcu这类资源加进它的配额和模板体系。如果只把资源注册到 K8s 集群但平台层不认这种资源结果就是用户在界面上创建 Notebook 时配额里根本选不到 DCU还是会走回“手动 yaml”的老路。我这边接入时的事实是平台层面需要做两步事情。第一步是在平台的资源配额页签里新增一条资源项名字就填hygon.com/dcu同时配置好单用户或单项目的上限。第二步是调整平台的模板仓库让 Notebook 和训练任务的默认模板里带上 DCU 资源的请求代码。这两步做完用户才能在界面上真正用到 DCU。4.2 资源配额与模板配置配额配置这一块各个版本的 CubeStudio 界面不太一样但底层逻辑都是“在 namespace 里创建 ResourceQuota”apiVersion: v1 kind: ResourceQuota metadata: name: ai-prod-quota namespace: ai-prod spec: hard: hygon.com/dcu: 16 hygon.com/vdcu: 40这一步很关键。如果你不创建 ResourceQuota用户理论上可以申请无限多的 DCU 资源把一个 8 卡节点直接打满其他项目就饿死了。我给每个项目设了两层限制整卡总数不超过物理卡总量统一用hygon.com/dcu控制共享和虚拟化场景用hygon.com/vdcu控制总份数这样即使有人折腾虚拟化也很难把整台节点搞垮。模板层面要改的是 Notebook 和训练任务模板里的 spec。核心就是在 container 的 resources 里写上 DCU 的请求同时把容器镜像换成带 ROCm 和 DCU 依赖的基础镜像。我这边把几个常用镜像重新打了一遍基础镜像只装 ROCm runtime、Python 环境然后按需要叠加 PyTorch、vLLM 等最后统一推到一个内网 registry避免平台用户各自拉错镜像。4.3 用户侧实操一个算法工程师怎么用 vDCU以我最常遇到的需求为例算法工程师要在平台上开一个 Notebook用半张卡跑数据预处理。他在 CubeStudio 的界面上创建 Notebook选择工作空间、镜像版本然后在资源配置里选择“DCU 共享模式 0.5 卡”或者直接填“vDCU: 5”。平台生成出来的底层 pod 大致是这样apiVersion: v1 kind: Pod metadata: name: notebook-user-001 namespace: ai-prod spec: tolerations: - key: dcus-only operator: Exists containers: - name: notebook image: registry.internal/rocm/hygon-notebook:latest resources: requests: hygon.com/vdcu: 5 limits: hygon.com/vdcu: 5平台能做到这一步关键是把抽象资源和真实调度打通。你在他界面上看到的是“0.5 卡”底下对应hygon.com/vdcu: 5看到的是“整卡 × 2”底下对应hygon.com/dcu: 2。这个映射关系必须在平台配置和模板里保持一致否则就会出现用户在界面选了 4 卡实际 pod 只申请了 1 卡这种“隐性错误”。这个对接过程里我遇到的最大的坑是平台自身的资源展示组件不认识自定义资源。有的版本里界面上的 GPU 数量是写死的字段需要改源码或者通过配置扩展。这种情况只能打补丁或者联系厂商升级没有捷径。好在我们这次用的是较新版本扩展资源能自动识别出来。5. 在 DCU 集群上部署 DeepSeek5.1 模型选型与硬件匹配DeepSeek 现在是个很宽泛的概念从几 B 的蒸馏小模型到 671B 的 MoE 大模型都有。部署之前必须先想清楚“我要跑哪一个”。我这次选了 DeepSeek-R1-Distill-Qwen-32B 作为一个性价比很高的中间档位。32B 模型用 FP16 加载大约需要 64GB 显存单张 64GB 的深算 DCU 刚好放得下不需要跨卡。如果你手里的卡是 32GB 的建议选 14B 甚至 7B 蒸馏版本。DeepSeek-V3 这种上百 B 级别的 MoE 模型单张卡肯定不行需要多卡 tensor parallel那就直接走整卡模式一次申请 8 张卡甚至更多。模型选型这里有个容易被忽视的点vLLM 这类推理引擎对显存的需求不只是模型权重还有 KV cache。所以模型权重 KV cache 激活显存三者加起来才是真实显存占用。我用 32B 模型时单卡 64GB 理论够用但如果并发请求很多KV cache 一上来就会撞到显存天花板。这种情况要么调低gpu-memory-utilization要么换更小的量化版本。5.2 推理引擎选择与镜像准备DCU 上跑大模型推理常用的引擎是 vLLM因为它支持 ROCm 后端。但注意不是所有 vLLM 版本都能直接跑在海光 DCU 上需要镜像里包含针对 DCU 做过适配的 ROCm 版本。我这边用的是一个内部重新构建的 vLLM-DCU 镜像基础是 ROCm 6.x打了海光 DCU 的适配补丁然后用 vLLM 的 OpenAI 兼容 API 起服务。镜像准备时几个环境变量很关键env: - name: VLLM_TARGET_DEVICE value: rocm - name: HIP_VISIBLE_DEVICES value: 0VLLM_TARGET_DEVICE是让 vLLM 走 ROCm 设备栈HIP_VISIBLE_DEVICES是控制容器内可见的 DCU 设备索引。在整卡模式下插件会自动设置设备可见性但在 vDCU 虚拟化场景里这个变量可能不会自动正确赋值需要你根据插件实际情况手动确认。5.3 在 K8s 上的部署配置示例我把 DeepSeek-R1-Distill-32B 部署成一个标准 Deployment通过 Service 暴露出 OpenAI 兼容的接口。核心配置是这样的apiVersion: apps/v1 kind: Deployment metadata: name: deepseek-r1-32b namespace: ai-prod spec: replicas: 1 selector: matchLabels: app: deepseek-r1-32b template: metadata: labels: app: deepseek-r1-32b spec: nodeSelector: hygon-dcu: true tolerations: - key: dcus-only operator: Exists containers: - name: vllm image: registry.internal/vllm/vllm-dcu:0.6.3 command: [python3, -m, vllm.entrypoints.openai.api_server] args: - --model - /models/DeepSeek-R1-Distill-Qwen-32B - --served-model-name - deepseek-r1-32b - --tensor-parallel-size - 1 - --gpu-memory-utilization - 0.9 - --max-model-len - 8192 - --port - 8000 env: - name: VLLM_TARGET_DEVICE value: rocm ports: - containerPort: 8000 resources: requests: hygon.com/dcu: 1 limits: hygon.com/dcu: 1 --- apiVersion: v1 kind: Service metadata: name: deepseek-r1-32b namespace: ai-prod spec: selector: app: deepseek-r1-32b ports: - name: http port: 8000 targetPort: 8000几个参数说明一下。--tensor-parallel-size 1表示单卡推理因为 32B 模型单卡能装下--gpu-memory-utilization 0.9表示允许 vLLM 最多占用 90% 的显存剩下的给其他开销留缓冲--max-model-len 8192限制了单条对话的上下文长度这个直接影响 KV cache 大小一定要结合业务场景调整不要盲目开大。如果你想用多卡并行跑更大的模型把--tensor-parallel-size改成 2 或 4同时资源请求改成hygon.com/dcu: 4。注意 tensor parallel 一定要让这几张卡在同一个节点上跨节点几乎跑不起来所以nodeSelector和节点资源余量要提前确认好。5.4 部署后的性能与稳定性调优模型能跑起来只是第一步。我上线之后做过几轮调参最有价值的是下面几个。第一是并发窗口。vLLM 的并发能力受显存和请求大小双重影响。我把--gpu-memory-utilization从 0.9 调到 0.92 之后并发上限涨了一圈但出现长上下文请求时偶发 OOM。最终定在 0.9 比较稳。第二是批处理调度。vLLM 默认是 continuous batching会动态把到达的请求拼成一个 batch 跑所以不需要手动做请求排队。但如果你的上游偏重短请求可以在网关层做一下请求合并或者限流避免瞬时大流量把推理服务打挂。第三是显存可视化。部署完以后我习惯在容器里定期跑rocm-smi看显存和利用率曲线。这个比看日志直观很多能一眼看出模型权重占了多少、KV cache 涨了多少、剩余容量还有多少。5.5 通过 CubeStudio 暴露给团队使用模型服务跑在一个普通的 Deployment 里对平台用户来说还是太底层了。我这次的做法是先在 CubeStudio 里注册一个“模型服务”资源指向上面这个 Deployment 对应的 Service然后在平台的模型网关里配置 API Key。团队同学的使用路径就变得很直观打开 CubeStudio → 进入模型服务页面 → 找到 deepseek-r1-32b → 看到调用地址和一个测试页面。直接就能发对话请求测试效果。平台网关会负责鉴权、限流、路由转发底层的 pod 扩缩容和资源调度还是走 K8s。这个流程的收益很明显算法团队不再需要关心 yaml 和 pod平台运维也不必天天回答“为什么我的推理服务又重启了”。出问题时我先看平台侧的访问日志如果请求都没到集群那就排查网关如果到了集群但容器日志报错再去看模型推理引擎和 DCU 资源情况定位路径清晰很多。6. 常见问题与排查技巧实录6.1 调度层问题速查现象可能原因排查与解决kubectl describe node看不到hygon.com/dcu设备插件未部署或注册失败查看 device plugin 日志检查/var/lib/kubelet/device-plugins目录权限Pod 一直 Pending节点标签不匹配、资源不足、配额不够kubectl describe pod看 Events重点看调度失败的 reason 和 messagePod 显示已调度但容器一直 ContainerCreating设备节点映射失败或镜像拉取超时检查节点的 kubelet 日志查看/dev/kfd、/dev/dri是否正常挂载创建 ResourceQuota 后资源不起作用资源名和 device plugin 注册的不一致重新核对hygon.com/dcu的拼写查一下 API 版本是否支持自定义资源配额调度层的问题九成以上可以在半个小时之内通过kubectl describe定位。最烦的是那种“资源明明够了Pod 却一直 Pending”的情况这时可以加上--v6看 scheduler 日志或者直接看事件的最后一条通常会给出“node(s) didnt match node selector”或者“Insufficient hygon.com/dcu”之类的明确原因。6.2 运行层问题速查现象可能原因排查与解决容器内程序报 “no such device”设备节点未映射或驱动缺失容器里执行ls -l /dev/kfd宿主机执行rocm-smi对比模型推理偶发 OOMKV cache 或显存配额不足降低--gpu-memory-utilization拆小请求多卡任务性能差异巨大部分卡被其他任务占用或温度过高rocm-smi查看占用率调整时间片共享策略HIP_VISIBLE_DEVICES设置无效vDCU 虚拟设备与物理设备索引不一致在容器里打印实际可见设备索引不要依赖物理编号运行层最经典的一个问题是容器里能看到 DCU 设备但程序仍然报显存不足或者算子初始化失败。这种情况我建议先从“宿主机正常、容器内异常”这个维度入手把程序启动时的完整报错日志打出来特别留意有没有触碰到 ROCm 版本的 API 变更。很多时候不是硬件问题是软件栈里的一个版本缝隙。6.3 独家避坑技巧分享几个在我踩过的坑里总结出来的经验。第一vDCU 模式下不要相信物理设备索引。我在一台 8 卡节点上做空间分割测试时明明申请的是第 2 张物理卡上的虚拟设备容器里看到的设备索引却是 0。如果算法代码里写死了torch.cuda.set_device(1)应用就可能跑到错误的卡上。解决办法是让容器启动脚本里打印一份“当前可见设备列表”和“当前可用显存”先确认环境再跑训练。第二给虚拟化资源留足余量。时间片共享模式下别把每份虚拟设备的内存配额加满到物理卡上限。我这边遇到过三次因为“配额总和等于物理显存”导致偶发 OOM 的故障最后都是把总和控制在物理显存的 80%~90% 才稳定下来。第三基础镜像一定要统一管理。DCU 依赖的 ROCm 库版本一旦五花八门排查成本会指数上升。建议所有 AI 镜像基于同一个基础镜像构建由一个人或一个小团队负责升级和发版其他人只拉取使用。第四设备插件和调度器的升级顺序要谨慎。先升级设备插件等节点资源上报稳定之后再升级 Kubernetes 版本或者 AI 平台。反过来的话调度器不认识新的扩展资源名可能会导致旧的整卡任务无法调度。我个人在实际操作中的体会是海光 DCU 接入 Kubernetes 这件事技术难度其实并不是很高真正花时间的都是细节对齐。驱动版本、设备节点映射、虚拟化资源命名、平台配额扩展每一项单独拎出来都不复杂但它们串在一起任何一个缝隙都会让上层任务跑不起来。最后再分享一个小技巧在设备插件和模型服务都稳定之后建议把kubectl describe node和rocm-smi的采集脚本做成定时任务把节点资源余量和显存使用情况同步到监控系统。这个投入很小但能让你在接到“节点又出问题”的反馈时第一时间判断是物理资源问题还是软件问题少走很多弯路。这个内容后续还可以继续扩展比如把多套 DCU 集群做成联邦调度或者往上接一个统一的大模型网关让不同业务共享同一批 DCU 推理服务这些都是后话先把基础链路跑稳最重要。
返回列表