上个月我们团队要把一批昇腾 910 的算力节点接进 Kubernetes 集群,本来以为就是装个驱动、部署个 device-plugin 的事情,真上手才发现从驱动、固件、CANN 到 Ascend Docker Runtime,再到 K8s 资源上报和监控,整条链路任何一个环节对不上版本,后面全是无休止的踩坑。CubeStudio 本身只是一个面向算法工程师的算力调度平台,底层接的还是 K8s,所以核心问题最终都会落在“NPU 怎么作为可调度的资源被 K8s 识别和分配”上。
这把我从裸机开始把昇腾 NPU 接入 K8s 的完整路径捋了一遍,覆盖驱动 / CANN / Ascend Docker Runtime / device-plugin / 监控五个层面。所有操作都是我实际跑过的,版本号可能随官方发布会有变化,但思路和坑是通用的。正在自建 K8s 集群、准备接昇腾卡的团队,或者刚拿到 Atlas 服务器不知道从哪下手的同学,这篇可以直接作为操作手册参考。
1. 接入前需要先搞清楚:NPU 在 K8s 里靠什么跑起来
1.1 从芯片到 Pod,这条链路有五个环节
很多同学一上来就找 device-plugin 的 YAML,觉得部署完就能调度了,其实这是本末倒置。K8s 的 device plugin 机制天生就只是个“资源上报器”,真正让容器里能跑 NPU 任务的是一整套从内核态到用户态的软件栈。我习惯把这条链路拆成五个环节:
第一环是物理芯片和内核驱动。昇腾芯片要工作,首先得有对应的内核模块被加载,这一步决定了/dev/davinci0这类设备节点、以及/dev/davinci_manager管理设备是否存在。没有驱动,后面一切免谈。
第二环是 CANN 用户态运行库。CANN 是昇腾的计算架构,类似 NVIDIA 生态里的 CUDA Toolkit,负责向上层框架(PyTorch、MindSpore、TensorFlow 等)提供算子库和运行时 API。算法工程师在容器里写代码离不开它,但它在 K8s 接入链路里更像是“被挂载进去的依赖”。
第三环是容器运行时。K8s 在创建 Pod 时,需要有一个 runtime 能把宿主机上的 NPU 设备及驱动目录“注入”到容器里。昇腾这边对应的就是 Ascend Docker Runtime,类比 NVIDIA 的 nvidia-container-toolkit。
第四环是 K8s device-plugin。它由 kubelet 启动,通过 Device Plugin API 把宿主机上的 NPU 数量汇报给 Kubelet,这样调度器才知道这台节点有多少张卡可以分配。
第五环是监控。NPU 和 GPU 一样,日常跑任务一定要看 AI Core 利用率、HBM 内存占用、温度、功耗这些指标,否则节点什么时候被打满、哪张卡开始过热,你完全不知道。
简单来说:前两环解决“单机能不能用”,第三环解决“容器能不能用”,第四环解决“K8s 能不能调度”,第五环解决“平台能不能观测”。 如果你在部署过程中发现“容器里跑不了”或者“调度不上去”,八成是这五个环节之间出现了断层。
1.2 关键组件与 NVIDIA 方案的对照
昇腾这套软件栈和 NVIDIA 生态的对应关系其实非常清晰,用熟悉的东西打比方会好理解很多。我整理了一张对照表,方便心里有个谱:
| 昇腾组件 | NVIDIA 对应组件 | 承担职责 | 部署位置 |
|---|---|---|---|
| NPU 驱动(含固件) | NVIDIA Driver | 加载内核模块,暴露设备节点 | 宿主机 |
| CANN Toolkit / NNRT | CUDA Toolkit | 提供算子、运行时和框架适配 | 宿主机 + 容器镜像 |
| Ascend Docker Runtime | nvidia-container-toolkit | 将设备、驱动库注入容器 | 宿主机 |
| ascend-device-plugin | nvidia-device-plugin | 上报 NPU 资源给 kubelet | K8s DaemonSet |
| dcmi / exporter | DCGM + exporter | 采集 NPU 状态指标 | 宿主机 + K8s |
这个对照不是为了让昇腾去模仿 NVIDIA,而是告诉你一个事实:这套生态里的每个组件都有明确的分工,缺一个环节就会多一类问题。以前有同事以为有了驱动就能在容器里跑,结果容器里/dev/davinci0根本不存在;也有人以为装上 runtime 就能被 K8s 调度,结果 kubelet 里压根没有资源可分配。
1.3 版本没那么随意,先对好兼容性矩阵
昇腾的版本绑定比 NVIDIA 严格得多。NVIDIA 驱动小版本装错了顶多性能异常,昇腾这边驱动、固件、CANN 三方版本对不上,经常直接给你报“ driver version is not matched with CANN version”。所以节点级基础部署的第一步不是下载安装包,而是先去昇腾社区官网确认兼容性矩阵。
兼容性文档里主要看三类信息:操作系统版本(Ubuntu 18.04/20.04/22.04,openEuler 等)、NPU 驱动版本和固件版本、CANN 版本。此外还有硬件形态的区分,比如 Atlas 800 训练服务器、Atlas 300I 推理卡、Atlas 300V 视频加速卡,不同的卡对应的驱动包完全不一样,不能混用。
我的经验是先把整条链路做成“版本快照”,比如:Atlas 800 训练服务器 + Ubuntu 20.04 + driver 23.0.RC1 + CANN 7.0.RC1 + ascend-docker-runtime 3.0.0 + ascend-device-plugin 2.0.0,记录在部署文档开头。后面所有节点、所有环境变量、所有镜像 tag 都以这个快照为准。等集群规模大了你会庆幸当时做了这一步,因为跨版本混用导致的隐性 bug 排查成本非常高。
2. 节点侧基础环境部署:驱动、固件、CANN 与 Ascend Docker Runtime
2.1 准备系统与基础依赖
节点侧部署的第一步是准备一个干净的系统。我这边使用的是 Ubuntu 20.04 LTS,内核版本保持发行版自带版本。昇腾驱动对内核版本比较敏感,如果你自己编译过内核,安装驱动时很可能会遇到“kernel header not found”或者模块编译失败之类的怪问题,所以建议尽量用标准内核,别折腾。
装驱动之前有两件事必须做:一是安装编译依赖,包括 gcc、make、linux-headers-$(uname -r),防止驱动在安装时需要现场编译内核模块;二是确认操作系统架构。昇腾服务器绝大多数是 aarch64 架构,但也有 x86_64 的版本,下载安装包时必须选对架构,我见过有人把 aarch64 的包装到 x86 机器上,安装器直接报错。
另外建议提前关闭系统的自动更新。AI 训练节点最怕半夜自动升级内核,第二天驱动模块加载失败,所有 NPU 任务全部挂掉。装驱动前可以用一条命令锁定内核版本,或者把 unattended-upgrades 直接关掉,生产环境省心很多。
# 安装编译依赖 sudo apt update sudo apt install -y gcc make linux-headers-$(uname -r) # 关闭自动更新(生产节点建议执行) sudo systemctl stop unattended-upgrades sudo systemctl disable unattended-upgrades2.2 安装驱动和固件,用 npu-smi 验证
驱动和固件在昇腾生态里通常是两个独立安装包,先装固件、再装驱动,顺序不要反。最开始我图省事直接跑驱动的 installer,结果设备节点一直不稳定,后来查文档才发现固件没装。固件负责底层芯片的微码和硬件初始化,驱动负责操作系统的内核模块,两者缺一不可。
拿到安装包后,执行权限加上,然后运行安装脚本。不同版本的安装参数略有差异,但一般都有--full全量安装和--install-for-all为所有用户安装这两个比较常用的选项。我习惯两个都带上,避免后续别的系统用户调用 npu-smi 时遇到权限问题。
# 先安装固件,再安装驱动(以 aarch64 示例,版本号以实际下载为准) chmod +x Ascend-hdk-910-npu-firmware_6.4.2.2_linux-aarch64.run sudo ./Ascend-hdk-910-npu-firmware_6.4.2.2_linux-aarch64.run --full chmod +x Ascend-hdk-910-npu-driver_23.0.rc1_linux-aarch64.run sudo ./Ascend-hdk-910-npu-driver_23.0.rc1_linux-aarch64.run --full --install-for-all安装完成后重启节点,然后用npu-smi info验证驱动是否正常加载。这个命令的输出类似nvidia-smi,能列出每张卡的芯片型号、健康状态、温度、AI Core 使用率、HBM 使用率等信息。
npu-smi info如果输出里能看到每张卡对应的设备编号davinci0、davinci1等,说明驱动和固件基本正常。如果提示npu-smi: command not found,先检查 PATH 里有没有/usr/local/sbin和/usr/local/bin,或者用绝对路径/usr/local/bin/npu-smi再试一下。
这里还有个容易被忽视的细节:NPU 驱动目录默认挂在/usr/local/Ascend/driver下,里面包含了版本信息文件version.cfg,后面很多配置文件要引用这个路径。只要记住这个目录是驱动侧的核心输出,后面看到version.cfg就不会慌。
2.3 安装 CANN Toolkit 并配置用户态运行环境
驱动没问题之后,接着装 CANN Toolkit。CANN 其实是好几类包的总称,最常用的是 Toolkit,里面包含开发套件和运行时;如果只是跑推理,还有更精简的 NNRT 包;如果是训练服务器,可能需要额外装 NNAE。刚上手时不用纠结,直接参考官方文档选一个与驱动版本匹配的 Toolkit 装就可以。
安装步骤比较简单,运行安装脚本,默认会安装到/usr/local/Ascend/ascend-toolkit目录下。装完之后要做的第一件事是同步环境变量,否则你写 Python 代码时import torch_npu一类的调用根本找不到 CANN 的库。
chmod +x Ascend-cann-toolkit_7.0.RC1_linux-aarch64.run sudo ./Ascend-cann-toolkit_7.0.RC1_linux-aarch64.run --install # 配置环境变量 source /usr/local/Ascend/ascend-toolkit/set_env.sh问题在于source set_env.sh只在当前 shell 生效。为了让容器外的用户态程序和后面所有运维操作都能正常使用 CANN,建议把环境变量写入/etc/profile.d/ascend.sh,这样每次登录都会自动加载。很多同学卡在“明明装了 CANN,python 却找不到相关模块”,十有八九就是环境变量没全局生效。
验证 CANN 是否装好,有一个很朴素的思路:跑一个最小的 Python 脚本,尝试导入 CANN 相关模块并调用设备侧接口。不过我日常更喜欢用命令行工具直接看,比如msnpureport、ascend-dmi这些工具如果能正常执行,说明环境变量基本没问题。更严谨的办法是拿一个官方提供的样例程序,在设备上跑一次矩阵乘法,这比什么命令都靠谱。
2.4 配置 Ascend Docker Runtime,让容器能“看到”算力设备
驱动和 CANN 装好之后,宿主机上已经能正常用 NPU 了,但 Docker 容器里还是没有。原因很简单:Docker 默认不会把宿主机的设备文件挂载进容器,你不会想让用户手动去指定--device /dev/davinci0 --device /dev/davinci_manager,那样既不安全也不好维护。Ascend Docker Runtime 就是替代这套手工操作的标准方案。
安装 Ascend Docker Runtime 之后,关键改动是在/etc/docker/daemon.json里注册一个名为ascend的 runtime。注册完成并重启 Docker 后,运行容器时带上--runtime ascend就能自动注入设备节点和驱动库。
{ "runtimes": { "ascend": { "path": "/usr/local/Ascend/Ascend-Docker-Runtime/ascend-docker-runtime", "runtimeArgs": [] } } }修改完配置后执行systemctl restart docker,然后验证 runtime 是否生效:
docker run --rm --runtime ascend \ -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ ascendhub.huawei.com/public/ascend-infer:23.0.RC1-ubuntu20.04 \ npu-smi info这里有一点容易踩坑:runtime 能把设备节点注进去,但容器里如果缺少对应的 CANN 用户态库,程序还是会失败。所以生产环境一般会做一个专门的基础镜像,把 CANN Toolkit 打进镜像里,或者至少把宿主机的 CANN 目录挂载进去。团队里的算法工程师不需要关心 runtime 细节,但平台侧运维一定要把这个“驱动 + 运行库 + 容器镜像”三者匹配的原则定下来,否则算法那边会因为镜像版本对不上而浪费大量时间。
3. 把 NPU 资源上报给 K8s:device-plugin 接入实战
3.1 K8s 设备插件机制:kubelet 如何感知 NPU
Kubernetes 本身并不知道 NPU 是什么东西,它只认识资源名字和数量。device-plugin 要做的就是让 kubelet 知道“这台机器上有 8 张 Ascend910,每张可以用名字huawei.com/Ascend910来表示”。
这里依赖的是 K8s 的 Device Plugin 框架。通俗讲,kubelet 会监听一个 Unix socket(默认在/var/lib/kubelet/device-plugins/下),device-plugin 启动后与 kubelet 建立 gRPC 连接,然后通过ListAndWatch接口持续上报设备列表。当用户 Pod 申请这个资源时,kubelet 又通过Allocate接口拿到设备挂载信息,并把设备节点和宿主机目录注入到容器里。
理解了这个机制,你就能明白为什么 device-plugin 必须跑在节点上、必须以 DaemonSet 方式部署、而且还必须能访问到 hostPath 目录。很多新手把 device-plugin 当普通 Deployment 部署,结果它只能上报到某一个节点,其他节点完全看不到 NPU 资源。
3.2 部署 ascend-device-plugin
昇腾官方提供了 ascend-device-plugin,一般以 DaemonSet 部署在kube-system命名空间。它的 YAML 不复杂,核心是要通过环境变量告诉插件应该上报什么样的资源名、以及对应的驱动版本路径。
下面是一个简化版的 YAML 骨架,实际使用时建议从官方仓库获取最新版本:
apiVersion: apps/v1 kind: DaemonSet metadata: name: ascend-device-plugin namespace: kube-system spec: selector: matchLabels: app: ascend-device-plugin template: metadata: labels: app: ascend-device-plugin spec: hostNetwork: true containers: - name: device-plugin image: ascendhub.huawei.com/public/ascend-device-plugin:2.0.0 env: - name: DEVICE_PLUGIN_VERSION value: "2.0.0" - name: DEVICE_PLUGIN_RESOURCE_NAME value: "Ascend910" securityContext: privileged: true volumeMounts: - name: device-plugin mountPath: /var/lib/kubelet/device-plugins - name: driver mountPath: /usr/local/Ascend/driver volumes: - name: device-plugin hostPath: path: /var/lib/kubelet/device-plugins - name: driver hostPath: path: /usr/local/Ascend/driver部署命令一行就够:
kubectl apply -f ascend-device-plugin.yaml kubectl get pod -n kube-system -o wide | grep ascend部署完成后要注意 Pod 的状态。CrashLoopBackOff 很常见,原因通常是挂载路径不对、驱动目录不存在、或者环境变量设置错误。排查时先看日志,kubectl logs输出的信息一般足够定位问题。
有一点经验值得单独说:DEVICE_PLUGIN_VERSION这个环境变量不是随便填的,它需要和挂载进去的驱动目录里version.cfg的版本匹配。官方插件会用这个字段决定如何上报设备数量、如何命名资源。如果版本对不上,插件可能会误判你的卡型,导致资源名和你预期的不一致。
3.3 验证节点资源和 Pod 调度
device-plugin 部署起来之后,第一件事是确认 kubelet 真的收到了资源信息。用kubectl describe node <node-name>,在输出里找Capacity和Allocatable字段:
Capacity: cpu: 192 memory: 503Gi huawei.com/Ascend910: 8 Allocatable: cpu: 192 memory: 503Gi huawei.com/Ascend910: 8如果能看到huawei.com/Ascend910: 8,说明设备上报成功。接着写一个最小测试 Pod,声明资源请求和限制,验证调度链路是否打通:
apiVersion: v1 kind: Pod metadata: name: npu-test spec: containers: - name: main image: ascendhub.huawei.com/public/ascend-infer:23.0.RC1-ubuntu20.04 command: ["sleep", "3600"] resources: limits: huawei.com/Ascend910: 1 requests: huawei.com/Ascend910: 1创建 Pod 后,如果状态变成 Running,说明调度成功;进到容器里执行npu-smi info,如果能看到设备,说明从驱动到 runtimt、再到 device-plugin 的整条链路已经打通。这里提醒一下:Pod 里的requests和limits对 device plugin 资源都写上,类似 GPU 的调度规则,只写 limits 虽然可以跑,但有些版本会有资源记账不准的问题。
3.4 在 CubeStudio 中创建 NPU 任务:平台视角下的实操
集群底层打通之后,剩下的问题就是如何把算力交给算法团队使用。CubeStudio 会把底层 K8s 的节点、资源、镜像这些细节封起来,对外提供项目空间、任务模板和资源配额。你在平台界面上创建一个训练任务时,界面上会有一个资源规格下拉框,里面能看到huawei.com/Ascend910对应的卡数选项,选择 1 卡、2 卡还是 8 卡,本质上就是在帮你生成对应 requests/limits 的 K8s Pod/Job。
实际操作时,需要先在 CubeStudio 的“资源池配置”里,将已经接入昇腾节点的工作负载调度到对应资源池。平台侧会把 device-plugin 上报的资源数量归集到资源池中,算法同学创建任务时才能选到昇腾规格。如果节点已经接入 K8s,但 CubeStudio 里看不到昇腾资源,多半是因为资源池没有关联该节点,或者在资源定义里没启用昇腾资源类型。
至于 vNPU 虚拟化、多卡共享这类高级能力,它们是构建在 device-plugin 之上的额外逻辑,并不是基础接入必须的内容。对大多数团队来说,先把物理卡 1:1 调度跑通,再考虑算力切分才是稳妥路线。
4. NPU 监控体系:从命令行到平台看板
4.1 监控看哪些指标
NPU 接入 K8s 后,监控往往是最容易被拖延的部分。很多人觉得npu-smi info能看温度、能看使用率,就已经够了。但集群一旦上了规模,你不可能逐台机器去敲命令,必须有一套指标采集链路,才能知道资源池的实时水位和故障隐患。
昇腾 NPU 的重要监控指标和 GPU 大同小异,我日常最关注这几类:
- AI Core 利用率:反映 NPU 计算单元是否真的在干活,模型跑起来但利用率很低,说明可能卡在 I/O 或者算子调度上。
- HBM 使用率:显存占用情况,OOM 前看它最容易提前发现风险。
- 温度:通常警戒线在 85°C 左右,超过 90°C 大概率要触发降频保护。
- 功耗:判断节点是否处于高负载状态,也能辅助定位物理故障。
- 设备健康状态:
npu-smi info里会有一列 Health Status,有芯片异常时能第一时间看到。
这些指标从npu-smi info的文本输出里其实都能拿到,问题是文本不好做时序聚合。所以监控方案的核心就是:找一种方式,把 NPU 指标转成 Prometheus 格式的 metrics 并暴露出来。
4.2 部署 NPU 指标 exporter
昇腾生态里有多种方式采集 NPU 指标,比较常用的是基于 DCMI(Device Config Manage Interface)接口实现的 exporter。DCMI 是昇腾提供的一套设备管理接口,类似 NVIDIA 的 NVML,exporter 通过它拿到温度、功耗、利用率等信息,然后暴露成 HTTP 接口给 Prometheus 抓取。
部署方式和 device-plugin 类似,一般以 DaemonSet 形式运行在每个昇腾节点上,每个 exporter 只负责本机的指标采集。采集到的指标命名形如dcmi_temperature、dcmi_ai_core_usage、dcmi_hbm_usage等,Prometheus 配置一个 target 规则即可。
apiVersion: apps/v1 kind: DaemonSet metadata: name: npu-exporter namespace: monitoring spec: selector: matchLabels: app: npu-exporter template: metadata: labels: app: npu-exporter spec: hostNetwork: true containers: - name: exporter image: your-registry/npu-exporter:latest ports: - containerPort: 9100 hostPort: 9100配置好 Prometheus 后,在 Target 页面确认 exporter 都是 Up 状态。然后随便查一条指标,比如dcmi_ai_core_usage,能查到数据就说明链路通了。这一步不要拖到任务跑起来才做,否则等出了问题再想追历史趋势,就什么都看不到了。
4.3 告警配置与 CubeStudio 联动
有指标之后,告警规则是刚需。我建议第一批规则先做基础设施级保护,技术含量不高,但能救命:
- 节点 NPU 温度超过 80°C,持续 5 分钟,告警。
- 单个设备 HBM 使用率超过 95%,持续 10 分钟,告警。
- 设备健康状态非正常,立刻告警。
- 节点上报的 NPU 设备数量与预期不符,立刻告警。
最后一个规则很少有人提,但实际非常有用。因为一旦设备挂掉,device-plugin 上报的资源数会变化,K8s 和用户可能都感知不到,但告警系统可以先发现。运维上这叫“算力资产巡检”,通过比对kubectl get nodes和 Describer 里的设备数,能自动识别算力损失。
在 CubeStudio 里,这些监控指标可以统一在项目空间展示。平台侧一般会提供一个监控面板,把 Pod 级别的 NPU 利用率、内存占用和节点级温度功耗聚合在一起。算法同学不用登录 Grafana 就能在任务详情页看到资源曲线,这对定位“代码没跑起来 vs 算力不够”这类问题是很有帮助的。
理论上这些看板也可以直接用 Grafana 实现,但融入平台的收益是权限模型统一、多项目隔离,不用每个人都会配 PromQL 才能看监控。
5. 常见问题与排查技巧实录
5.1 装完驱动 npu-smi 不存在或者报错
这个问题的样式很多,最常见的有两种。一种是npu-smi: command not found,大概率是 PATH 没配好,昇腾工具默认在/usr/local/bin或/usr/local/sbin,检查一下这两个目录,或者直接用绝对路径。另一种是执行 npu-smi 时报ERR_TOOL_BUSY或者driver is not initialized,主要原因是驱动安装后没重启,或者固件和驱动版本不匹配。我处理这类问题的标准动作是:重启节点,再看内核模块是否加载。
lsmod | grep drv如果drv_pcie、drv_devmm这类模块没有加载,说明驱动没有真正起来。这时回到安装日志,确认固件先于驱动安装、且版本在兼容矩阵内。一键重装驱动前,建议先彻底卸载残留组件,避免新旧版本混在一起。
5.2 容器里看不到 NPU 设备
宿主机上 npu-smi 正常,容器里ls /dev/davinci*确什么都没有。这个问题 90% 出在 Ascend Docker Runtime 没有生效。先用一个最简单的命令验证:
docker run --rm --runtime ascend \ -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ ascendhub.huawei.com/public/ascend-infer:23.0.RC1-ubuntu20.04 \ ls /dev/davinci*如果这条命令看不到设备,优先检查/etc/docker/daemon.json里的 runtime 路径是否正确,以及 Docker 是否真的重启成功。注意有的部署方式不是直接注册到 dockerd,而是通过 containerd 的配置项,K8s 使用 containerd 作为 CRI 时,需要额外在 containerd 里配置 runtime class,不能只盯 Docker 那边。
5.3 Pod 一直 Pending,节点上没有可分配 NPU 资源
设备插件已经部署,kubectl describe node却看不到 NPU 资源。先分清是网络不通、版本不匹配还是插件本身没上报。最常见的原因是 Pod 的资源名和节点上报的资源名不一致。比如插件上报的是huawei.com/Ascend910,但测试 Yaml 里写的是huawei.com/Ascend310,调度器当然不会分配。
另一个容易忽略的点是 device-plugin 的挂载路径。如果插件容器里看不到宿主机驱动目录/usr/local/Ascend/driver下的version.cfg,它就无法判断卡类型和版本。这种情况日志里会打印 driver version 相关的报错,处理方法是调整 DaemonSet 的 hostPath 挂载路径,确保驱动目录真实存在且权限可读。
5.4 device-plugin CrashLoopBackOff
Device-plugin 反复重启时,先别急着删 Pod,看日志最直接。常见原因有:插件镜像版本和驱动版本不兼容、挂载目录权限不够、以及 kubelet 的 device plugin socket 目录没有正确挂载。插件的 fliewall 问题也遇到过,不过那是少数。
这里说一个经验:如果集群里同时有 GPU 和 NPU 节点,device plugin 的 DaemonSet 默认会在所有节点上部署,GPU 节点上因为没有 NPU 驱动,插件会直接报错。最好用 nodeSelector 给昇腾节点打一个专属标签,比如ascend=true,让插件只跑在有 NPU 的节点上,省去无意义的报错。
spec: template: spec: nodeSelector: ascend: "true"5.5 常见问题速查表
我把部署过程中最常碰到的几类问题整理成了速查表,方便现场排查时对照。
| 现象 | 可能原因 | 排查方法 / 解决建议 |
|---|---|---|
| npu-smi 不存在 | PATH 未配置或驱动未装 | 用绝对路径 /usr/local/bin/npu-smi,重装驱动 |
| 容器内无 davinci 设备 | Ascend Docker Runtime 未生效 | 检查 daemon.json,重启 docker,逐条验证 runtime |
| 节点无 NPU 资源上报 | device-plugin 未运行或版本不匹配 | 查看插件日志,核对驱动 version.cfg |
| Pod 一直 Pending | 资源名不匹配或配额不足 | describe node 和 Pod 事件,对齐资源名 |
| 任务运行报 CANN 版本错误 | 驱动/CANN/镜像三方版本不一致 | 统一版本快照,重新构建基础镜像 |
| exporter 有数据但 Grafana 无图 | Prometheus target 未拉到或标签不匹配 | 检查 exporter 端口,看 Prometheus 采集日志 |
最后分享一个我的部署习惯
如果你准备在自己的环境复现这套东西,我建议按“单节点先跑通、再铺集群”的节奏来。我第一次部署时直接上来就搞 8 节点的分布,结果每台机器的问题都不一样,排查起来非常被动。更务实的做法是:先把一台节点从驱动到监控全链路打通,记录每一步的版本号和配置文件,再把这套配置固化成脚本或部署文档,批量推到其他节点。
至于 CubeStudio 与底层 K8s 的适配,只要 K8s 层的资源上报和调度正常,平台侧基本就是水到渠成的事。比较关键的是要在 CubeStudio 的资源池里把昇腾资源规格定义清楚,否则算法同事在选择算力规格时会看不到对应选项。
这套链路里最容易被低估的其实是“版本对齐”。驱动、固件、CANN、Docker Runtime、device-plugin,五个组件环环相扣,任何一环的小版本偏差都可能让整个系统不可用。建议在团队内部维护一份版本匹配表,每次升级前先在测试节点验证,再决定是否批量操作。踩过几次这种坑之后你就能体会到,很多看起来玄乎的 NPU 调度问题,本质上都是版本和配置问题。