把昇腾 NPU 接进 Kubernetes,这句话听起来就是一行需求,但实际上背后藏着一整条软件链:驱动、CANN、Ascend Docker Runtime、device-plugin,再加上监控。任何一个环节没对上,NPU 在容器里就是“看不见、摸不着、调度不了”。最近在 CubeStudio 这套环境里完整跑了一遍昇腾基础部署,从裸机装驱动一直到 K8s 里跑通多卡训练,中间踩了不少坑,也把整条链路的逻辑彻底捋顺了。这篇就按实操顺序把步骤、原理和排查经验一次讲清楚,适合正在做昇腾 NPU 容器化接入、或者准备在企业 K8s 集群里纳管异构算力的朋友参考。
1. 昇腾 NPU 接入 Kubernetes 的整体思路
1.1 容器要“看见” NPU,绕不开这三件事
很多人第一次接触这个需求时会觉得,K8s 里调度 CPU、内存这么成熟,把 NPU 加进去不就是多一种资源吗?实际上没那么简单。Kubernetes 默认认识的资源只有 CPU 和内存,GPU 是依靠 device-plugin 以 Extended Resource 的形式上报给 kubelet,再由调度器感知和分配的。昇腾 NPU 走的是同一个机制,但底下的硬件和软件栈比 GPU 更特殊一点。
要让容器真正用上昇腾 NPU,必须解决三件事。
第一件是系统层面能不能看见设备。昇腾 NPU 在宿主机上体现为/dev/davinci0、/dev/davinci1这类设备节点,外加一个/dev/davinci_manager管理设备。这些设备由驱动程序创建,驱动没装好,一切免谈。
第二件是容器层面能不能访问设备。即使宿主机能看到设备,容器默认是隔离的,你必须在创建容器时把设备文件、驱动目录、CANN 相关库都挂载进去,并且配置好运行时环境。这一步在昇腾体系里由 Ascend Docker Runtime 自动完成。
第三件是调度层面能不能按卡分配。K8s 调度器不知道davinci0是什么东西,需要 device-plugin 把节点上的 NPU 资源数量上报给 kubelet,并负责在 Pod 启动时把具体的设备列表交出去。三件事环环相扣,任何一层断了,都会表现为“资源有了但容器里用不了”或者“节点上明明有卡但调度不上去”。
1.2 软件栈分工:驱动、CANN、Runtime 与 device-plugin 各管一段
整条链路可以拆成四个软件角色,它们各管一段,职责非常清晰,但都是缺一不可的。
表格里列一下,方便对照:
| 组件 | 作用 | 出问题时的典型现象 |
|---|---|---|
| 固件与驱动 | 初始化 NPU 硬件,创建/dev/davinci*设备节点,提供npu-smi工具 | 宿主机npu-smi info报错,找不到设备 |
| CANN Toolkit | 提供算子库、图编译、运行时等开发能力,类似 CUDA 在 NVIDIA 体系里的位置 | 训练时报算子不兼容、库文件找不到 |
| Ascend Docker Runtime | Docker/containerd 创建容器时自动注入 NPU 设备、驱动库和 CANN 环境变量 | 容器内没有/dev/davinci*,npu-smi无法执行 |
| device-plugin | 向 kubelet 上报huawei.com/Ascend910等扩展资源,支持调度与设备分配 | 节点资源数为 0,Pod 调度失败或分配不到设备 |
以我实际部署的感受来说,最容易翻车的不是驱动本身,而是 Runtime 和 device-plugin 之间的配合。因为 Runtime 管的是“容器起来时设备在不在”,device-plugin 管的是“这个 Pod 被调度到节点后,容器该用哪张卡”。如果只装了 device-plugin 没配 Runtime,调度能成功,但容器进去发现/dev/davinci0不存在,训练直接启动失败,而且报错信息还很不直观。
1.3 为什么用 CubeStudio 来做这套基础部署
CubeStudio 在我们的环境里是一个统一管理昇腾算力资源和部署流程的平台,它把所有碎片化的操作收敛成了可复用的流程。刚刚接触昇腾 K8s 接入时,可以完全靠手搓命令,但如果集群规模变大、节点变多,每次都去 SSH 到每台机器上装驱动、改配置显然不现实。CubeStudio 的价值在于把“节点纳管 -> 驱动安装 -> Runtime 配置 -> device-plugin 部署 -> 监控接入”串成一条标准的部署流水线,任何新节点加入后能快速复制环境。
当然,平台只是把操作标准化了,底层每一步的原理还是得搞清楚。下面所有实操内容,都是我在裸机环境下先手工验证过一遍,再固化成 CubeStudio 里的部署流程的。接下来按顺序拆解。
2. 环境准备与版本配套检查
2.1 硬件形态与操作系统选择
昇腾 NPU 的产品形态比较多。如果是 Atlas 300I/300T 推理卡,通常是 PCIe 插卡,插在通用服务器上;如果是 Atlas 800 训练服务器,一般是整机交付,里面有多张昇腾芯片。不管哪种形态,对 Kubernetes 接入来说看到的都是/dev/davinci*设备,只是数量不同。
操作系统方面,昇腾官方支持的主要是 Ubuntu、openEuler、CentOS 等常见发行版,但要注意 CPU 架构。昇腾服务器大多是aarch64(ARM 架构),也有少数 x86 平台,驱动包和 CANN 包都是分架构的,下载时一定要选对。我第一次部署时误下了 x86 的 CANN 包,在 aarch64 机器上直接提示架构不匹配,浪费了不少时间。
Kubernetes 版本建议选 1.26 以上的稳定版,因为新版 K8s 对扩展资源的调度、设备插件的接口更成熟。如果集群还是用 Docker + cri-dockerd 的旧模式,或者已经切换到 containerd,Runtime 的配置方式会略有不同,这个后面会单独说。
2.2 版本配套关系是最大的隐形坑
昇腾软件体系的版本配套关系非常严格,这是新手最容易踩的坑。驱动、固件、CANN 三者必须满足官方配套表的要求,不是说“驱动是最新的就行”。比如某张训练卡,固件需要 X 版本,驱动需要 Y 版本,CANN 需要 Z 版本,三者搭错一个,轻则告警,重则设备直接挂掉。
我整理了部署前必查的几项信息:
- 硬件型号:
npu-smi info能够输出芯片型号、固件版本、驱动版本,前提是驱动已经装好。 - 固件版本:如果卡是全新的,需要先刷固件再装驱动。
- CANN 版本:工具包分为
cann-toolkit、cann-kernels等,一定要和驱动版本、昇腾芯片匹配。 - torch_npu / MindSpore 版本:后续要跑训练框架的话,框架版本和 CANN 版本也要对齐。
昇腾社区官网有“软件配套表”,里面有非常详细的版本矩阵。我的建议是,先确定要用什么训练框架(PyTorch 还是 MindSpore),再根据框架要求的 CANN 版本反推驱动和固件版本,这样最不容易出错。
2.3 安装前三条硬检查
在开始安装之前,我会强制自己在每台节点上做三件事。
第一,确认系统干净。如果有旧版驱动残留,直接用./Ascend-hdk-*.run --uninstall卸载干净,或者用npu-smi info看看是否已经能识别设备。强行覆盖安装偶尔能成功,但容易留下版本残留。
第二,确认内核头文件齐全。昇腾驱动安装时会编译内核模块,需要当前内核对应的 kernel-devel 或 linux-headers 包。很多节点装完系统后内核升级过,但头文件没跟上,驱动装到一半就报编译失败。
第三,确认 BIOS 和 PCIe 状态。用lspci | grep -i ascend或lspci | grep -i huawei看设备是否存在,如果看不到设备,先排查硬件插槽、PCIe 链路,而不是急着装软件。
这三条检查五分钟就能做完,但能省下后面半小时的排错时间。
3. 驱动与 CANN 安装实操
3.1 安装固件和驱动
在昇腾网站上按硬件型号和操作系统下载好固件包和驱动包之后,安装命令很简单,都是.run包执行。以 Atlas 训练卡为例,命令大致是:
# 安装固件 ./Ascend-hdk-910b-firmware_6.3.3_linux-aarch64.run --full --quiet # 安装驱动 ./Ascend-hdk-910b-npu-driver_23.0.rc3_linux-aarch64.run --full --quiet注意不同型号的包名不一样,910b只是示例。--full表示完整安装,--quiet表示静默模式,不给交互提示。装完驱动之后,执行:
npu-smi info如果能看到类似下面的输出,说明驱动和固件已经正常工作:
+------------------------------------------------------------------------------------+ | npu-smi 23.0.rc3 Version: 23.0.rc3 | +-------------------+-----------------+--------------------------------------------------+ | NPU Name | Health | Power(W) Temp(C) Hugepages-Usage(page)| | Chip | Bus-Id | AICore(%) Memory-Usage(MB) HBM-Usage(MB) | +===================+=================+==================================================+ | 0 | OK | ...如果这里就报错,先不要继续往后走,一定是驱动或固件有问题。
3.2 验证驱动并安装 CANN Toolkit
驱动装好之后,npu-smi能看到卡,只能说明设备节点有了。接下来要装 CANN Toolkit,这是让上层框架(PyTorch、MindSpore)能够调用 NPU 算力的关键。CANN 的安装包同样是.run格式:
./Ascend-cann-toolkit_8.0.RC1_linux-aarch64.run --install安装完成之后,需要把环境变量加到 shell 配置里,CANN 的set_env.sh脚本会帮你一次性配好所有路径:
source /usr/local/Ascend/ascend-toolkit/set_env.sh建议把这一行写到/etc/profile或每个用户的.bashrc里,否则每次登录都要手动 source。对容器场景来说,这个环境变量实际上不是由宿主机传递的,而是由 Ascend Docker Runtime 在容器启动时注入的,所以宿主机的环境变量配置主要用于裸机验证。
3.3 驱动、CANN 装完后先做一次裸机验证
很多人装完 CANN 就直接跳到 K8s 环节,这是不对的。至少要花五分钟在宿主机上确认“裸机可以调用 NPU”。
最简单的验证是跑一个 Python 脚本,确认 torch_npu 能正常装载并识别设备:
import torch import torch_npu print(torch.npu.device_count()) print(torch.npu.get_device_name(0))如果输出正常的设备数量和名称,说明驱动、CANN、torch_npu 三者已经打通。这时候再去接容器化和 K8s,排错范围会小很多。我踩过一个教训:当时直接上了容器,出了问题排查了半天,最后发现是宿主机裸机环境下 CANN 版本和 torch_npu 不匹配。如果先做裸机验证,问题在第一步就暴露了。
4. Ascend Docker Runtime 接入容器运行时
4.1 Ascend Docker Runtime 到底做了什么
昇腾的 Ascend Docker Runtime 在角色上很像 NVIDIA Container Toolkit。它的原理是:Docker 创建容器时,可以通过--runtime参数指定一个自定义 OCI Runtime,这个 Runtime 在真正启动容器进程之前,会把宿主机上的/dev/davinci*设备、驱动目录、CANN 库目录以及环境变量注入到容器里。
所以它本质上不是“让 NPU 变快”的组件,而是“让容器看见 NPU”的组件。如果没配置 Runtime,即使设备节点存在,容器内的 namespace 也看不到这些设备文件,自然无法访问。
安装 Ascend Docker Runtime 很简单,把包解压到宿主机目录,然后配置 Docker。解压后的目录通常包含一个ascend-docker-runtime可执行文件,这就是我们要挂到 Docker 里的 Runtime。
4.2 Docker 运行时配置
对于使用 Docker 作为容器运行时的情况,需要修改/etc/docker/daemon.json。这里有个细节:昇腾官方提供的安装脚本有时候会直接帮你把配置写好,但也有时候只解压文件。手动配置的话,格式如下:
{ "runtimes": { "ascend": { "path": "/usr/local/Ascend/Ascend-Docker-Runtime/ascend-docker-runtime", "runtimeArgs": [] } } }配置完成后重启 Docker:
systemctl restart docker docker info | grep -A5 Runtimes如果配置正确,docker info的 Runtimes 列表里会出现ascend。
在测试阶段,建议先手动跑一个容器看看设备是否注入成功:
docker run --rm --runtime=ascend -it \ ascendhub.huawei.com/public/ascend-mindspore:latest \ npu-smi info容器里能看到 NPU 信息,说明 Runtime 生效了。
4.3 containerd 场景下的配置
如果你的 K8s 集群用的是 containerd(现在主流版本基本都是),不能只配 Docker,还需要把昇腾 Runtime 接入 containerd。containerd 的配置在/etc/containerd/config.toml,需要在 CRI 插件下面增加 runtime 配置。
大致的配置段如下,实际操作时版本不同格式会稍有差异:
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.ascend] runtime_type = "io.containerd.runc.v2" runtime_path = "/usr/local/Ascend/Ascend-Docker-Runtime/ascend-docker-runtime"改完以后重启 containerd:
systemctl restart containerd这里要特别提醒,K8s 1.24 版本之后默认不再支持 Docker 作为运行时(除非额外部署 cri-dockerd),所以新集群几乎都是 containerd,配置好 containerd 的 Runtime 是必须做的一步。有些部署文档只写了 Docker 而没写 containerd,照着做就会卡在容器起不来。
4.4 验证容器内 NPU 是否可见
Runtime 配完后,除了用docker run --runtime=ascend验证,还要验证 containerd 环境下是否也能自动注入。可以通过crictl工具创建一个测试容器,或者直接跳到下一步,用 K8s 的 Pod 来验证。我的经验是,越早验证容器层,后面 device-plugin 出问题时就越容易定位是调度问题还是运行时问题。
有一个细节要留神:如果容器内的镜像没有安装npu-smi工具,即使设备注入成功,你也无法用npu-smi info检验。所以验证镜像要选昇腾官方带工具的镜像,或者自己在基础镜像里拷贝一份驱动下的npu-smi可执行文件。
5. device-plugin 部署与资源调度验证
5.1 Extended Resource 机制:K8s 怎么知道节点有 NPU
K8s 官方留给异构设备接入的标准接口是 device plugin 框架。device-plugin 是一个运行在节点上的 gRPC 服务,kubelet 启动时会去/var/lib/kubelet/device-plugins/目录下寻找 Unix socket,然后通过这个 socket 和 device-plugin 通信。
device-plugin 需要做两件事:第一,向 kubelet 上报这个节点上有多少张 NPU 卡,这个数字会体现在节点的allocatable里;第二,当 Pod 被调度到该节点后,kubelet 会拿着 Pod 请求的资源数量问 device-plugin 要具体的设备 ID,device-plugin 返回/dev/davinci0、/dev/davinci1这样的设备列表和对应的驱动挂载信息。
昇腾体系里,扩展资源的名称一般是huawei.com/Ascend910或者huawei.com/Ascend310,取决于芯片型号。Pod 的 YAML 里只要写上:
resources: requests: huawei.com/Ascend910: 1 limits: huawei.com/Ascend910: 1调度器在看到这类资源请求时,就会自动把 Pod 分配到有对应资源的节点上。
5.2 部署 Ascend device-plugin
昇腾的 device-plugin 是以 DaemonSet 形式部署的,也就是说每个节点上跑一个 agent,负责上报本节点设备、响应 kubelet 的分配请求。部署前确认几件事:
- 节点上已经配好 Ascend Docker Runtime(或 containerd Runtime)。
- 节点驱动已经正常,
npu-smi info能看到设备。 - 给节点打上标签,方便调度和筛选,例如:
kubectl label node <node-name> accelerator=huawei-ascenddevice-plugin 的 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-k8sdeviceplugin:latest imagePullPolicy: IfNotPresent securityContext: privileged: true volumeMounts: - name: device-plugins mountPath: /var/lib/kubelet/device-plugins - name: ascend-driver mountPath: /usr/local/Ascend/driver volumes: - name: device-plugins hostPath: path: /var/lib/kubelet/device-plugins - name: ascend-driver hostPath: path: /usr/local/Ascend/driver这里需要解释一下为什么要挂载/usr/local/Ascend/driver。device-plugin 需要访问宿主机驱动里的某些模块来获取设备状态和分配信息,如果不挂载,插件可能能启动但拿不到设备列表。
部署完成后,查看节点资源:
kubectl describe node <node-name> | grep -A5 "huawei.com/Ascend910"如果一切正常,capacity和allocatable里会显示对应的卡数量。如果这里为 0,多半是 device-plugin 的 Pod 有问题,去看日志。
5.3 跑一个测试 Pod 走通全链路
节点资源上报成功之后,创建测试 Pod 验证整条链路。昇腾官方的推理或训练镜像体积比较大,但胜在环境齐全。我在 CubeStudio 环境里用的验证 YAML 大致是:
apiVersion: v1 kind: Pod metadata: name: ascend-test spec: restartPolicy: OnFailure containers: - name: ascend-test image: ascendhub.huawei.com/public/ascend-mindspore:latest command: ["sleep", "3600"] resources: requests: huawei.com/Ascend910: 1 limits: huawei.com/Ascend910: 1 securityContext: runAsUser: 0创建后进入容器执行:
kubectl exec -it ascend-test -- npu-smi info容器内能正常显示 NPU 信息,说明从驱动到 Runtime 再到 device-plugin 的整条链路已经打通。这时候如果直接用昇腾官方镜像跑一段训练代码,比如用 MindSpore 跑 LeNet,或者用 torch_npu 跑一个矩阵乘法,能真实看到算力调用。
5.4 用 Kubernetes Dashboard 发布测试服务
在实际交付的时候,很多运维同学习惯用 Kubernetes Dashboard 来管理服务和 Pod,而不是每次敲kubectl apply。这里有一个典型的使用场景:想通过 Dashboard 创建一个新的 Pod 作为新服务发布。
具体操作路径是这样的:在 Dashboard 的 Namespace 里选择对应命名空间,进入“工作负载 -> Pod”,点击右上角创建按钮,可以直接粘贴 YAML,也可以走表单。如果你走表单,需要手动填镜像地址和资源请求,但 Dashboard 的旧版本表单在“资源请求”里不一定支持huawei.com/Ascend910这种自定义资源,所以稳妥的做法是直接选“从 YAML 创建”,把上面那份测试 Pod 的 YAML 粘贴进去。
另外要强调一点:Dashboard 本身是一个高权限管理组件,千万不要把它暴露到公网。企业内部建议通过 ingress 加认证、或用 kubectl proxy 方式访问,利用 KubeConfig 的 token 鉴权。之前安全圈通报过不少 Kubernetes 未授权访问漏洞,很多就是 Dashboard 或 API Server 直接暴露在公网,没有开启 RBAC 限制。Kubernetes 只要配置了合理的 RBAC,给 Dashboard 账号只授予需要的 namespace 的只读或指定权限,就能避免大多数风险。
6. 监控体系搭建:NPU 状态可视化
6.1 快速排查:容器内看 npu-smi
接入 K8s 之后,最基础的监控还是npu-smi。这个工具在宿主机可以看整机的卡,在容器内只能看到分配给当前 Pod 的设备。如果容器内执行npu-smi info只看到一张卡,而宿主机上明明有四张卡,这是正常的,因为 Runtime 只把分配给 Pod 的卡注入到了容器里。
一条非常实用的命令是持续刷新当前设备状态:
watch -n 1 npu-smi info在训练过程中,可以观察 AICore 利用率、HBM 占用率、温度、功耗这几个指标。利用率长期低于 30%,说明算子下发或者数据读取有瓶颈;HBM 接近满,说明 batch size 或者模型尺寸需要调整。
6.2 Prometheus + 导出器采集 NPU 指标
生产环境不可能靠人肉watch npu-smi,需要把指标接入 Prometheus 和 Grafana。昇腾的监控方案有两类,一类是官方提供的 exporter,另一类是自己写脚本基于驱动接口采集。
官方 exporter 的部署方式一般是 DaemonSet,在每个 NPU 节点上跑一个指标导出器,暴露/metrics接口给 Prometheus 抓取。指标包括 NPU 温度、HBM 使用量、AICore 利用率、芯片功耗等。如果你暂时找不到合适的官方 exporter,也可以用 Python 脚本每隔 5 秒解析一次npu-smi info的输出,转成 Prometheus metrics 格式,这不是最优雅的方案,但能快速解决问题。
Prometheus 采集端的配置,只需要在scrape_configs里增加一个 job,选择带有ascend-exporter标签的节点:
scrape_configs: - job_name: 'ascend-npu' kubernetes_sd_configs: - role: pod relabel_configs: - source_labels: [__meta_kubernetes_pod_label_app] action: keep regex: ascend-exporter打上标签、配置完 Prometheus,再到 Grafana 里导入一个 NPU 相关的 dashboard,很快就能看到全集群 NPU 的实时状态。我自己习惯把“卡健康状态”“平均利用率”“温度告警”放在一个面板上,运维同事看板子不需要懂昇腾细节也能快速定位问题。
6.3 训练场景的深层次监控与性能分析
Prometheus 监控解决的是“节点活着吗、卡忙不忙”的问题,但真实训练场景里还需要更深的性能数据。比如在跑 swift + Megatron 大规模模型训练时,经常出现“卡利用率不错,但整体吞吐上不去”的情况,这时候必须看通信和算子层面的 profile。
CANN 自带的 msprof 工具可以抓取 NPU 算子耗时和通信耗时。在容器里执行类似:
msprof --output=/tmp/profiling python train.py跑一小段时间后,分析输出的op_statistic和timeline,能看到每个算子耗时、AICore 利用率、HCCS 通信等待时间等。这一步对于我们后期做分布式训练调优非常有价值,尤其是多卡并行时,通信时间占比过高的话,需要检查单卡 batch size、梯度同步策略、是否启用了混合精度等设置。
还有一个小技巧:用torch_npu跑 PyTorch 时,torch.npu.synchronize()可以用来做计时基准,避免异步执行导致的时间测量不准。这个在评估单卡算子性能时很关键,否则你会以为算子很快,其实根本没跑完。
7. 常见问题与排查技巧实录
7.1 device-plugin 上报资源为 0
先看 device-plugin 的 Pod 日志,常见报错是拿不到设备列表。这时候按顺序检查:
npu-smi info在宿主机上是否正常。- device-plugin 是否挂载了
/usr/local/Ascend/driver。 - 节点是否打上了 device-plugin 需要匹配的标签。
- 如果用的是 containerd 而不是 Docker,要确认 device-plugin 的存活探针和 kubelet 通信正常。
有个容易忽略的点:device-plugin 是通过 kubelet 的 socket 通信的,如果 kubelet 启动时加了--feature-gates=DevicePlugins=false(老版本有这个参数),或者 socket 目录权限不对,插件注册不会成功。新版本 K8s 里DevicePlugins默认开启,一般不会遇到,但排查时值得确认。
7.2 Pod 调度失败,提示节点资源不足
明明kubectl describe node里显示有 NPU 资源,但 Pod 一直 Pending。大概率是以下原因:
- Pod 请求的资源名和节点上报的资源名不一致。比如节点上报的是
huawei.com/Ascend910,而 Pod 写的是huawei.com/Ascend910B,调度器自然认为资源不存在。 - 资源请求值超过了节点可用值。比如节点只剩 1 张卡,而 Pod 一次性申请 2 张。
- 节点被打了
taint,Pod 没有对应的容忍。
用kubectl describe pod查看调度事件是最快的排查方式,事件里会明确写出为什么节点不可用。不要靠猜,直接看调度器给出的事件信息。
7.3 容器内看不到/dev/davinci设备
这个问题的锅基本在 Runtime。如果 Pod 请求了 NPU 资源,调度和分配都成功了,但容器内没有设备,先确认以下配置:
- 如果是 containerd,
config.toml里是否加了 ascend runtime。 - 如果是 Docker,
/etc/docker/daemon.json里runtimes是否配置了ascend,Docker 是否重启。 - Pod 创建时是否实际上用了默认 runtime 而不是 ascend runtime。有些环境里 device-plugin 分配了设备,但 Runtime 没有生效,设备自然进不到容器。
- 容器镜像里是否真的存在
/dev/davinci*的挂载位置。设备文件由 Runtime 在启动时创建在容器内,和镜像无关,但如果没有 Runtime 介入,容器内自然没有。
排查时可以先在容器内执行ls /dev/davinci*,如果提示 No such device,再去宿主机上检查 Runtime 配置,效率最高。
7.4 CANN 算子报错与版本不匹配
这是所有问题里最隐蔽的一类。训练时算子报错或者无法识别的设备类型,搜索结果会指向 CANN 兼容性问题。昇腾的版本矩阵非常严格,尤其是 torch_npu、CANN、驱动固件三者之间。
排查思路是:
npu-smi info # 看驱动和固件版本 cat /usr/local/Ascend/ascend-toolkit/latest/version.cfg # 看 CANN 版本 pip show torch-npu # 看 torch_npu 版本三者对不上,直接去昇腾社区查配套表。出现这类问题不要浪费时间猜原因,版本矩阵是明规则,照着改就完事了。
7.5 安全与权限相关的几个坑
最后说几个实际部署中容易忽略的安全问题。昇腾驱动和 CANN 的工具链很多需要 root 权限,容器里跑训练时,如果镜像内没有普通用户,可以加securityContext.runAsUser: 0临时解决,但生产环境建议在镜像里创建专用用户,结合 PSP/Pod Security Admission 限制 root 权限。
Kubernetes Dashboard 这类管理组件,必须配合 RBAC 最小权限使用。不要图省事给 dashboard service account 绑定cluster-admin,否则一旦 Dashboard 被未授权访问,整个集群就危险了。可以在命名空间级别授予只读权限,或者使用临时 token 登录。集群网络层面也要限制 Dashboard 只允许内网访问,不建议直接暴露 NodePort 到公网。
最后再分享几个实战中的小习惯
昇腾 NPU 接入 Kubernetes 这套流程跑通之后,维护成本主要在版本升级和节点扩容上。我个人的经验是:每次有新的驱动或 CANN 版本发布,先在测试节点上完整跑一遍“驱动 + CANN + Runtime + device-plugin + 训练验证”,确认没有问题再推到生产节点,千万不要直接在线上批量升级。
节点扩容时,如果新节点加入集群后 device-plugin 的资源没有显示,先不要急着重启 kubelet,检查一下新节点的驱动是否安装、是否和已有集群节点版本一致。昇腾设备在集群内保持版本统一很重要,混用驱动版本虽然短期能跑,但后续大规模训练时容易出现隐性故障。
另外一个小细节:给 NPU 节点设置资源预留时,要留出 CPU 和内存给 device-plugin、exporter 本身体面运行,否则节点资源紧张时,基础组件的 Pod 可能被驱逐,影响设备上报和监控采集。调度器层面可以通过在 device-plugin 的 DaemonSet 里设置tolerations和priorityClassName来规避这类问题。
这套部署方案目前在我们 CubeStudio 环境里已经稳定运行了一段时间,支撑了从单卡推理到多卡 swift + Megatron 训练的各种负载。希望这份实操记录能帮你少走一些弯路。