手头有搭载海光DCU的服务器,想把它接进Kubernetes和基于K8s的AI平台,最直接的问题是:Kubernetes默认根本不认识DCU。你以为装好驱动、插上卡就能调度,结果节点里找不到任何DCU资源,Pod要么调度不成功,要么容器里看不到设备。CubeStudio这类AI平台又是在Kubernetes之上做封装,底层资源识别不了,上层的训练、推理任务自然全都卡住。这篇文章打算把海光DCU接入Kubernetes和CubeStudio的完整链路拆开来讲,包括整卡接入、共享调度、两种vDCU虚拟化方式,以及把DeepSeek这类大模型跑在DCU上的部署思路,适合正在做国产加速卡容器化适配的运维和平台开发同学参考。
1. 为什么非得把DCU“搬进”Kubernetes:容器化调度给AI平台带来了什么
1.1 没有容器调度时的DCU使用方式有多麻烦
在没有Kubernetes之前,DCU的用法通常很原始。训练或者推理任务想用DCU,需要手动登录服务器,自己配置设备用户权限,再通过环境变量指定某一张卡给某个进程。如果同一个机器上多个同学或团队都要用DCU,就需要人工协调:你用卡0到卡2,我用卡3到卡5,然后各自去改启动脚本。矛盾特别容易发生,尤其是有人申请了卡却一直不跑任务,资源被白白占住。而且这种方式完全没法限制进程的显存和算力,一个程序中无意间把显存打满,整机所有DCU任务都会被拖垮。
引入Kubernetes之后,节点的DCU资源可以由kubelet统一上报,用户在提交Pod时可以声明需要的DCU数量,调度器根据节点的空闲资源做分配。开发者不需要关心自己的任务到底落在哪台机器上,也不需要手动设置设备和环境变量,这一切由CNI、Device Plugin和容器运行时协作完成。AI平台(比如CubeStudio)也能在用户侧隐藏底层资源细节,向平台申请一个“多少卡、多少显存”的资源池,平台负责在后端创建Pod并保证隔离。
1.2 关键问题:Kubernetes的扩展资源机制适合承载DCU吗
Kubernetes本身并不理解什么是GPU、什么是DCU,它提供了一套扩展资源(Extended Resource)机制,让外部设备通过Device Plugin的方式接入。设备插件本质上是一个实现了gRPC接口的DaemonSet,运行在每个节点上,负责探测节点上的设备、向kubelet上报设备列表,并在Pod启动时把设备挂载进容器。
DCU虽然不是NVIDIA GPU,但底层思路是类似的。海光DCU的运行时基于HIP/ROCm生态,在Linux下以字符设备方式暴露给用户态程序,比如/dev/dcu0、/dev/dcu1之类。Device Plugin需要做三件事:扫描节点上有多少张可用的DCU卡;通过gRPC向kubelet注册,让节点capacity里出现类似hygon.com/dcu的资源名;在Pod要使用某张卡时,将对应设备节点和设备文件挂载到容器内,并注入必要的环境变量。
1.3 CubeStudio的模式:隔离底层、提供“类GPU”体验
CubeStudio这类平台一般会做一个统一资源抽象层,把Kubernetes的节点资源、存储资源和AI框架运行时管起来。用户看到的是一个友好的控制台,可以在上面创建开发环境、提交训练任务、启动在线推理服务。但平台本身不会魔法般识别DCU,它只认Kubernetes暴露出来的可调度资源。所以接入DCU的第一步,是让Kubernetes层面出现一个可调度的资源项,叫它hygon.com/dcu也好,叫它某个平台自定义的资源名也好,只要调度器能感知、kubelet能分配,CubeStudio就能透传给上层用户。
这一点很多人容易搞反:以为装个驱动、跑个dcmi能看到卡,平台里就能选了。实际上中间还隔着kubelet、Device Plugin、调度器还有平台层的资源映射关系。任何一个环节没做好,最终表现都是“平台里选不了DCU资源”或者“任务卡在Pending”。
2. 动手前的侦察:驱动、DCU工具链与CubeStudio的安装预期
2.1 驱动和基础工具链确认
先把最基础的事情做对。海光DCU服务器上需要安装对应的驱动和运行时,具体安装包以海光官方发布为准,不同操作系统内核版本对应的驱动版本差异很大,建议严格使用官方文档推荐的版本组合。
安装完成后,需要确认几点:
- 用
dcmi或者rocm-smi能否列出所有DCU设备,检查设备数量和健康状态。 - 确认设备节点存在,通常是
/dev/dcu0、/dev/dcu1这样的形式,或者在/dev/dri下看到对应的render节点。 - 确认驱动加载状态,比如
lsmod | grep dcu能看到核心模块。 - 确认用户态运行时库存在,后续容器内部要用到HIP运行时,需要提前了解驱动所配套的ROCm版本。
节点上这些条件都不满足的话,后面整卡接入和虚拟化都无从谈起。真实环境里我曾经碰到过驱动装了但设备节点没创建出来的情况,后来发现是系统里自带的旧版驱动模块冲突,卸载重装才恢复。所以第一步务必认真核对,不要急着往下走。
2.2 记录硬件拓扑和NUMA信息
这一步很容易被忽略,但到后面做性能调优时非常重要。DCU通过PCIe连接到CPU,不同的PCIe插槽可能挂在不同的NUMA node上。如果Pod被调度到某个CPU核心所在的NUMA node和DCU的NUMA node不一致,跨NUMA访存会明显增加拷贝时间。
排查时建议用以下命令确认:
lspci | grep -i nvidia # 如果有N卡,排除干扰 lspci | grep -i dcu # 查看DCU PCIe设备 lstopo-no-graphics # 查看CPU、内存、PCIe设备的拓扑关系记录每个DCU所在NUMA node、对应的PCIe地址,后续设计调度策略或配置CPU affinity时用得着。如果是多路服务器,往往会有多个NUMA node,DCU不一定平均分配,这一步要提前摸清楚。
2.3 CubeStudio的资源模型评估
在开始接入之前,需要先和平台侧确认一个关键信息:CubeStudio的资源定义是硬编码了NVIDIA GPU的资源类型,还是支持自定义扩展资源。不同版本能力不一样,但大部分基于Kubernetes二次开发的AI平台都会支持资源类型的配置扩展,区别只是入口在哪。
一般建议的路径是:
- 在CubeStudio的管理配置里找到“资源类型”或者“GPU资源”相关的配置页面。
- 尝试新增资源类型,名字和Kubernetes上报的扩展资源名保持一致,比如
hygon.com/dcu。 - 配置资源数值单位,通常按“张”或“块”计算,整数调度。
- 在节点组或资源池里关联已经打了DCU标签的节点。
如果平台不支持自定义资源类型,那就需要在调度器层面做映射,把平台默认的GPU资源请求改写成DCU资源请求,相当于做一层适配器。这种情况工作量会大不少,需要平台开发参与。所以动手前先看平台的定制能力,能省很多事。
3. 第一步整卡接入:Device Plugin上报资源与节点标签管理
3.1 自己写Device Plugin还是用官方方案
海光DCU的Device Plugin目前有几种选择:官方发布的开源插件、基于通用GPU Device Plugin改造的方案、以及完全自己实现一套。如果官方提供了和驱动版本匹配的插件,建议优先使用,因为官方插件对设备扫描、设备隔离、容器安全策略的处理通常更成熟。如果官方插件缺失,也可以基于HashiCorp或NVIDIA Device Plugin的框架思路,对接DCU的CDC扫描逻辑,自己实现一个精简版本。
无论选哪种,Device Plugin需要实现的核心接口就三个:
ListAndWatch:向kubelet上报当前设备列表,并监听设备变化。Allocate:在Pod调度到该节点后,返回要挂载的设备节点、环境变量和要设置的device cgroup。GetDevicePluginOptions:返回是否支持pre-start容器等扩展能力。
3.2 整卡模式下的Device Plugin配置示例
假设场景是:每张DCU整卡作为一个可调度单位,Pod请求hygon.com/dcu: 1,调度器就会把该Pod调度到有余量DCU的节点上。Device Plugin启动后,节点上会出现类似下面这样的一段Capacity信息。
Device Plugin用DaemonSet部署,核心配置大致如下(示意):
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: hygon-dcu-device-plugin image: registry.example.com/hygon-dcu-device-plugin:latest imagePullPolicy: IfNotPresent securityContext: privileged: true env: - name: DCU_DEVICE_PLUGIN_RESOURCE_NAME value: "hygon.com/dcu" volumeMounts: - name: device-plugin mountPath: /var/lib/kubelet/device-plugins - name: sysfs mountPath: /sys - name: dev mountPath: /dev volumes: - name: device-plugin hostPath: path: /var/lib/kubelet/device-plugins - name: sysfs hostPath: path: /sys - name: dev hostPath: path: /dev关键点:
- 必须使用privileged权限,否则没有权限访问宿主机的/dev设备。
- 需要挂载
/var/lib/kubelet/device-plugins,因为kubelet靠这个目录下的Unix socket和Device Plugin通信。 - 需要挂载宿主机’s
/dev和/sys,因为设备发现和设备挂载依赖这些目录。
3.3 验证整卡资源是否被kubelet识别
部署完DaemonSet之后,别急着跑任务,先验证节点上资源是否真的被上报了:
kubectl get nodes -L dcu.hygon.com/model kubectl describe node <node-name> | grep -A5 "Capacity"如果顺利,你应该能在Capacity或者Allocatable里看到:
hygon.com/dcu: 8这表示该节点有8张DCU整卡可以被调度。
接下来简单跑一个测试Pod,请求一张整卡,并检查容器内部是否能看到DCU设备:
apiVersion: v1 kind: Pod metadata: name: dcu-test-pod spec: restartPolicy: OnFailure containers: - name: dcu-check image: registry.example.com/rocm-dcu-check:latest command: ["/bin/sh", "-c"] args: - "dcmi -q; echo '------'; ls /dev/dcu*; echo '------'; env | grep DCU; sleep 3600" resources: limits: hygon.com/dcu: 1 requests: hygon.com/dcu: 1如果容器里执行dcmi -q能看到对应的DCU设备信息,并且环境变量里出现了类似ROCR_VISIBLE_DEVICES、DCU_VISIBLE_DEVICES这种设备可见性变量,说明整卡接入已经通了。
这一步操作中我踩过的一个坑是:容器里执行dcmi提示没有权限。原因是DCU设备节点在容器内虽然被挂载了,但容器内缺少对应设备文件的读写权限。解决办法是给容器加privileged: true,或者在安全策略中给Pod添加/dev/dcu0的设备白名单。生产环境建议用设备白名单方式,不要全局privileged。
3.4 节点标签:让调度有据可依
只有扩展资源还不够,节点标签是另一个关键。比如节点上有海光DCU的具体型号、驱动版本、虚拟化能力等信息,Kubernetes调度器没法自动获取,需要管理员手动打标签。建议至少打这几类:
dcu.hygon.com/node-arch:DCU架构代号。dcu.hygon.com/memory-gb:单卡显存大小,比如64。dcu.hygon.com/support-share:是否支持共享虚拟化。dcu.hygon.com/support-slice:是否支持静态切片。
打标签的方式:
kubectl label node <node-name> dcu.hygon.com/support-share=true kubectl label node <node-name> dcu.hygon.com/support-slice=true后面调度DeepSeek推理任务或训练任务时,可以通过nodeSelector或nodeAffinity精确匹配节点,避免把任务调度到没有虚拟化能力的节点上。
4. 两种vDCU虚拟化模式的取舍:静态切片与动态共享的计算逻辑
4.1 为什么需要vDCU:整卡资源浪费太严重
整卡模式很直接,但资源利用率往往低得吓人。举个例子:一张64G显存的DCU,如果只跑一个DeepSeek-R1-Distill-Qwen-7B的推理服务,模型权重可能只占不到10G显存,剩下大量显存和算力闲置。如果每张卡只能分给一个Pod,那等于用一堆豪华资源跑小任务,成本完全打不住。
vDCU虚拟化的核心目的就是打破“一张卡只能给一个进程”的限制。我把vDCU分成两类,一类是静态切片,一类是动态共享,它们解决的是完全不同的问题。
4.2 静态切片:把一张物理卡切成多个独立“小卡”
静态切片类似给DCU做物理级隔离。每张物理卡可以被划分成多个虚拟设备,每个虚拟设备拥有独立的显存区间和相对独立计算单元。Pod使用时,它看到一个虚拟的DCU设备,显存和算力都被限制在自己分到的范围内,互不干扰。
这种模式的特点是隔离性强,一个Pod把显存打满,不会影响同卡上的其他Pod。但其代价是灵活性低,一旦划分好就不能随便改,如果某张卡上切的都是7G的小实例,后面来了一个需要30G的大任务,这块卡就接不住,产生碎片化。
在CubeStudio的调度配置里,可以把静态切片注册成类似hygon.com/vdcu-slice的资源。提交任务时,除了申请数量,还需要通过注解声明需要的显存大小,比如:
apiVersion: v1 kind: Pod metadata: name: infer-slice-demo annotations: hygon.com/device-memory: "16Gi" spec: nodeSelector: dcu.hygon.com/support-slice: "true" containers: - name: infer image: registry.example.com/inference-server:latest resources: limits: hygon.com/vdcu-slice: 1 requests: hygon.com/vdcu-slice: 1调度器看到这个Pod之后,会从节点上查找显存足够且空闲的切片实例,然后通过环境变量把对应的虚拟DCU设备索引注入到容器里。
4.3 动态共享:多个Pod复用同一张物理卡
动态共享和静态切片思路完全不同。它不做显存级别的硬隔离,而是允许多个Pod在时间片或算力配额上复用同一张物理卡。你可以把它理解成把一张DCU当作一个时间共享的处理器,每个Pod分到一定比例的算力和有上限的显存配额。
这种方式的好处是资源利用率极高,适合大量轻量级推理服务并行。比如说四个Pod共享一张64G显存的DCU,模型都在同一个设备上跑,只要显存总量没有超过64G,并且算力需求没有把卡压满,大家都能正常工作。
但代价也很明显:性能会互相干扰。某个Pod發起大量计算时,其他Pod的推理延迟会明显上升。因此动态共享通常需要配合三个机制:显存配额管理、算力权重控制、任务优先级抢占。否则在生产环境很容易出现“某个任务把共享卡吃干抹净,其他任务集体超时”的问题。
动态共享在Kubernetes里的资源申请方式一般长这样:
apiVersion: v1 kind: Pod metadata: name: infer-share-demo annotations: hygon.com/device-memory: "8Gi" hygon.com/device-compute: "25" spec: nodeSelector: dcu.hygon.com/support-share: "true" containers: - name: infer image: registry.example.com/inference-server:latest resources: limits: hygon.com/dcu-share: 1 requests: hygon.com/dcu-share: 1hygon.com/device-memory表示这个Pod最多可以使用多少显存,hygon.com/device-compute表示它期望占用的算力权重比例。不同的vDCU实现命名和单位不太一样,但思路基本一致。
4.4 两种vDCU的适用场景对比
拿张表直接说结论:
| 对比项 | 静态切片(vDCU Slice) | 动态共享(vDCU Share) |
|---|---|---|
| 隔离级别 | 显存、算力硬隔离 | 显存配额 + 算力权重软隔离 |
| 单卡可承载Pod数量 | 取决于切分粒度,一般2到8个 | 可有十几个,取决于显存和负载 |
| 性能稳定性 | 高,隔壁Pod怎么折腾都不影响自己 | 较低,会有毛刺和延迟波动 |
| 配置灵活性 | 差,切分后难以动态调整 | 好,Pod之间按需申请 |
| 典型适用场景 | 多负载混合的稳定生产环境 | 轻量级推理、开发调试、批量短任务 |
| 需要额外组件 | vDCU切片管理服务 | 共享调度器和显存监控组件 |
在我的实践里,长期跑着若干个大模型推理服务的场景更适合静态切片;而CubeStudio上大量开发者调试、测试的短任务,用动态共享明显更划算。也见过不少团队混合用:基础大模型服务用静态切片,On-demand训练和临时推理用动态共享,一张物理卡同时跑两种业务形态。
5. DeepSeek上DCU:从模型加载到推理服务的部署实录
5.1 DeepSeek部署的显存估算
DeepSeek系列模型版本很多,部署时最先要解决的不是K8s配置,而是显存够不够。推理过程中显存占用主要由三部分构成:模型权重、KV Cache、运行时开销。
以DeepSeek-R1系列中的蒸馏版本为例,比如7B模型用半精度FP16加载,模型权重大约需要14GB显存,加上KV Cache和推理计算中间态,单张32G显存的DCU就能比较轻松地跑起来。如果要用更大量级的模型,比如DeepSeek-V3这类参数规模很大的稠密模型,单张卡基本扛不住,需要多卡并行,用张量并行把模型切到多张DCU上。
一个快速的显存估算方式:
所需显存 ≈ 模型参数量(亿) × 2Byte(FP16) × 1.2 + 并发数 × 单请求KV Cache大小例如7B模型,FP16权重约14GB,加上KV Cache预留4GB,再考虑运行时约2GB,32G显存是够的,64G则更游刃有余。如果模型版本是32B,FP16权重约64GB,至少需要两张32G卡或一张64G卡才能装下,并且还要看多卡互联带宽。
5.2 推理框架的选择与适配确认
DeepSeek生态里的推理框架也比较成熟,vLLM是社区最常用的一个。vLLM原生支持ROCm/HIP后端,在海光DCU的适配版驱动和运行时之上,理论上可以直接编译使用。但我实际操作时发现,DCU和AMD GPU的底层细节有差异,直接拿官方vLLM的ROCm版本不一定能完全跑满,厂商提供的补丁版本通常更可靠。
所以部署前务必确认:
- 当前vLLM版本是否支持你的DCU架构型号。
- 是否打过海光DCU相关patch。
- 容器镜像内是否包含匹配的HIP运行时和DCU工具库。
如果官方版本跑不起来,最简单的方式是使用厂商或社区已经构建好的推理镜像,而不是自己从零编译,能省至少半天的坑。
5.3 通过Kubernetes + CubeStudio部署DeepSeek推理服务
假设我们已经完成整卡接入,并决定使用一张32G显存的DCU整卡来跑DeepSeek-R1-Distill-Qwen-7B。推理镜像里已经装好了vLLM和DCU运行时,模型权重放在一个PVC中,那么Deployment清单大致可以写成这样:
apiVersion: apps/v1 kind: Deployment metadata: name: deepseek-r1-7b namespace: ai-prod spec: replicas: 1 selector: matchLabels: app: deepseek-r1-7b template: metadata: labels: app: deepseek-r1-7b spec: nodeSelector: dcu.hygon.com/support-slice: "true" containers: - name: vllm image: registry.example.com/vllm-dcu:latest imagePullPolicy: IfNotPresent resources: limits: hygon.com/dcu: 1 requests: hygon.com/dcu: 1 ports: - containerPort: 8000 name: http command: - /bin/bash - -c - > export ROCR_VISIBLE_DEVICES=${DCU_VISIBLE_DEVICES:-0} && python3 -m vllm.entrypoints.openai.api_server --model /models/deepseek-r1-distill-qwen-7b --gpu-memory-utilization 0.9 --max-model-len 8192 --trust-remote-code volumeMounts: - name: model-storage mountPath: /models volumes: - name: model-storage persistentVolumeClaim: claimName: deepseek-model-pvc这里比较关键的一个点是,Device Plugin在Allocate阶段会注入DCU_VISIBLE_DEVICES之类的环境变量,告诉容器可以使用哪些DCU设备。如果没有注入,容器内可能识别不到卡。不同厂商插件注入的变量名不一样,上面示例中我用的是DCU_VISIBLE_DEVICES,实际情况请以自己部署的插件为准。
--gpu-memory-utilization 0.9的意思是让vLLM最多使用90%的显存,预留一些给系统和其他组件。如果使用共享vDCU,则显存配额由注解控制,vLLM侧的--gpu-memory-utilization也要调低,避免因为请求超出配额被系统终止。
5.4 服务暴露和验证方法
推理服务启动后,先不去CubeStudio,直接在K8s里面验证:
kubectl get svc -n ai-prod kubectl logs -f deploy/deepseek-r1-7b -n ai-prod看到vLLM的启动日志里有“Starting vLLM server”的字样后,就可以用curl简单测试:
curl -X POST http://<service-ip>:8000/v1/completions \ -H "Content-Type: application/json" \ -d '{ "model": "deepseek-r1-distill-qwen-7b", "prompt": "解释一下什么是Kubernetes扩展资源", "max_tokens": 512, "temperature": 0.7 }'如果返回正常的文本结果,说明DCU接入和DeepSeek部署链路已经通了。接下来就可以在CubeStudio平台里把这个Deployment关联成在线推理服务,通过平台的门户对外提供API。平台本身一般不关心后端到底是什么模型框架,只要Pod能注册成服务,它能转发流量就可以。
6. 调度、监控与排障:真正上线前必须确认的几件事
6.1 调度策略调整:不要让DCU资源“平面化”
Kubernetes默认调度器对扩展资源只做总量判断,不感知DCU的NUMA拓扑,也不关心同一任务的多个DCU是否在同一个PCIe Switch下。如果对性能有要求,需要做两件事。
第一,给调度器配置Binpack策略,让Pod尽量集中到已有DCU占用的节点,而不是把任务打散到很多节点,否则每个节点都只用了一部分卡,剩余卡无法给大任务用。
第二,通过nodeAffinity和podAffinity给多卡任务绑定节点。比如要求在同一个节点或者同一个NUMA node上分配所有DCU,避免一个分布式推理服务跨节点通信,增加不必要的网络开销。
spec: affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: ["deepseek-r1-7b"] topologyKey: kubernetes.io/hostname上面的配置保证同一个Deployment的副本尽量落在同一台节点。如果是多卡张量并行,需要为每个worker创建Pod并确保它们在同一节点,这一点要结合CubeStudio的分布式任务编排能力来做,光靠Deployment的副本模式不够。
6.2 监控:从节点到容器的完整观测链
DCU监控是接入过程里最容易缺失的一环。很多集群在接入DCU之后,发现kubectl top node只能看到CPU和内存,推理服务快把DCU跑满了,监控面板还是一切正常。这种情况会直接影响容量规划和故障定位。
建议从三个层面补齐监控:
- 节点层:部署DCU指标导出器,定时采集
dcmi或rocm-smi的利用率、显存占用、温度、功耗、风扇转速。把这些信息转成Prometheus格式,再接入Grafana。 - 容器层:确保Device Plugin在Allocate时注入的
DCU_VISIBLE_DEVICES是有监控粒度的。多个容器共享一张卡时,单纯看整卡利用率看不出是哪个容器吃掉了算力,需要依赖虚拟化层暴露每个vDCU的指标。 - 平台层:在CubeStudio的资源监控页面里配置好DCU资源维度,让用户可以直接看到自己提交的任务用了多少DCU、显存占用曲线如何。
6.3 排障顺序:从设备到Layer一层层查
碰到DCU相关Pod报错,我的排查顺序基本是固定的。
第一步查设备层。在节点上直接执行dcmi -q看看所有DCU是不是健康,ls -l /dev/dcu*确认设备节点齐全。如果这层就有问题,后面做得再好也没有意义。
第二步查插件注册。看Device Plugin的Pod日志,确认有没有正常上报设备数量。再kubectl describe node确认节点Capacity里出现了hygon.com/dcu。如果没有出现,多半是kubelet和Device Plugin握手失败,检查/var/lib/kubelet/device-plugins目录下的socket文件是否存在。
第三步查调度。Pod一直Pending,先kubectl describe pod看调度事件。常见的错误是0/8 nodes available: 8 Insufficient hygon.com/dcu。排除资源不足的原因后,就要看是不是nodeSelector和标签不匹配。
第四步查容器内设备。Pod已经Running,但日志报找不到DCU设备。检查容器的环境变量里有没有注入DCU_VISIBLE_DEVICES,检查容器内对应的device文件是否存在。很多情况下是Device Plugin的Allocate实现不完整,只上报了资源但没有正确注入设备。
第五步才往上查应用。确认框架版本、驱动版本、显存参数这些应用侧问题。
6.4 虚拟化模式下的特殊排障场景
vDCU模式下的排障比整卡更复杂,因为故障可能出在调度器、虚拟化中间层、或者设备插件本身的配合上。
我遇到过两种典型问题。一种是动态共享模式下,好几个Pod运行在一张物理卡上,其中一个Pod因为模型并发过高导致显存超过配额,结果整个卡上的所有Pod都被系统杀掉。这个问题表面上是应用问题,根因是显存配额没有真正生效。检查办法是看vDCU的配置里是否启用了显存硬限制,以及容器启动时是否接受了相关cgroup参数。
另一种是静态切片模式下,任务提交时申明的hygon.com/device-memory大于实际可用切片,调度器没有拦截,任务一启动就因为设备初始化失败而CrashLoopBackOff。这个问题需要在平台侧加上前置的显存大小校验,避免把校验压力全部放在运行时。
这里我个人的建议是,在CubeStudio平台上做好vDCU类型和资源规格的列表化管理。用户申请资源时只能选择平台预定义的切片规格,而不是自由填任意显存大小,从入口上规避不合法请求。
最后的落地体会
整套海光DCU接入Kubernetes和CubeStudio的过程,说复杂也复杂,说简单也简单。复杂点在于中间链路很长:从驱动、设备文件、Device Plugin、kubelet、调度器,到平台资源映射,每层都要对上。简单的地方在于,只要把资源抽象和资源隔离这两个核心问题想清楚,后面的部署流程就是套模板的事。
根据我的经验,团队接入DCU时最不值得做的一件事就是一开始就追求最完美的虚拟化方案。不要第一天上手就想着静态切片加动态共享一把梭,而是先把整卡模式跑通,让一个真实任务能跑起来,然后再按照业务需求逐步加上vDCU、共享调度、监控和平台集成。一旦整条链路通了,后面所有优化都是在既有框架上做加法。
另外想提醒一句:芯片架构迭代很快,Kubernetes和AI平台版本也一直在更新,凡是涉及驱动、设备插件、平台版本匹配的步骤,都建议在测试环境完整演练一遍再上生产。海光DCU本身已经相当成熟,但整个容器化生态的细节适配仍然需要你亲手去踩一遍才算数。