昇腾NPU接入Kubernetes:从驱动到device-plugin的完整部署指南
2026/9/18 20:33:58 网站建设 项目流程

1. 为什么非要把昇腾 NPU 塞进 Kubernetes

先说个现象。这两年国产算力卡在训练和推理场景里出镜率越来越高,尤其是昇腾系列,很多团队手里已经握着几十张甚至上百张 Atlas 300I Duo、Atlas 800 这类设备。但问题也随之而来——大部分人的第一反应是“我直接在宿主机上装好驱动、跑 Python 脚本不就行了”,结果用着用着就发现根本顶不住。

原因很简单:当任务一多、机器一多,GPU 时代那套资源分配逻辑就失灵了。比如我有 8 张 NPU 卡,但 5 个团队同时要申请资源,谁能拿到哪张卡、用多长时间、怎么隔离、怎么回收?手工运维的话,一天光协调卡就得花掉大半天。而 Kubernetes 本来就是干这个的,它有成熟的调度器、资源模型、健康检查、弹性伸缩机制,唯一缺的就是“把 NPU 当成一种可调度资源”的适配层。

所以“昇腾 NPU 接入 Kubernetes”本质上解决的是三件事:让 K8s 认识 NPU、让 Pod 能用 NPU、让管理员能监控 NPU。听起来简单,实际做起来涉及的组件却不少——从底层驱动到 CANN 运行时,从容器 Runtime 到 device-plugin,再到监控采集,链路很长。这篇文章把我自己落地 CubeStudio 昇腾基础部署的完整过程整理出来,包括驱动和 CANN 的安装、Ascend Docker Runtime 的接入、device-plugin 的部署、监控配置,以及我踩过的坑和排查思路。无论你是刚接触昇腾不久,还是已经上手但卡在某个环节,这篇文章都能给你一条能直接照做的路径。

这里先说明一下整套架构的形态:Kubernetes 集群使用标准的 kubeadm 方式搭建(独立部署或已有集群均可),NPU 设备通过 device-plugin 以可调度资源的形式暴露给集群;容器运行时采用 Docker + Ascend Docker Runtime 的组合,让容器能够访问宿主机上的昇腾设备;监控层通过昇腾官方 Exporter 将 NPU 的利用率、温度、HBM 内存等指标接入 Prometheus。整体结构用一张简化的概念图来描述就是:

业务 Pod (挂载 NPU 资源) ↓ Ascend Docker Runtime (打通设备映射) ↓ 昇腾驱动 + CANN Toolit (宿主机) ↓ Atlas NPU 硬件设备

这个链路看着长,但每一步都是绕不开的。下面从最基础的硬件和驱动开始说。

2. 部署前准备:硬件型号、操作系统与网络环境

2.1 确认硬件型号和驱动适配关系

昇腾设备目前的形态分两类:一类是插在 x86 服务器 PCIe 槽位上的加速卡(比如 Atlas 300I Duo、Atlas 300V Pro),另一类是一体机形态的 Atlas 800 训练服务器。Kubernetes 接入方式没有本质区别,都是先把设备驱动在宿主机上装上,再通过 runtime 和 device-plugin 把设备透传给容器。

但有个关键点必须先确认:你的昇腾设备型号对应哪个固件版本和驱动版本。昇腾的驱动和固件是分开安装的,固件版本还得跟硬件匹配,装错了轻则功能异常,重则设备起不来。我这边用的测试机是两台 Atlas 300I Duo 推理卡,配套的驱动是 23.0.3 版本,CANN 是 8.0 版本。如果你的设备是 Atlas 800 或者是更新的型号,建议去昇腾社区官网查一下“昇腾硬件兼容性清单”再定版本。

提示:昇腾的驱动、固件和 CANN 三者之间有配套关系,不要只想着“装最新版”。最稳妥的方式是查对应产品的版本配套表,确保三个版本在一个兼容矩阵里。

2.2 操作系统与内核要求

昇腾对宿主机的操作系统有明确支持列表,我调研下来,Ubuntu 20.04/22.04、openEuler、CentOS 7.6/8.2 这些主流版本都在支持范围内,但并不是所有内核版本都能跑。比如 CentOS 7.6 默认的 3.10 内核太老,得换内核才能适配昇腾驱动。我的建议是直接用 Ubuntu 20.04 或 22.04,省去各种内核适配的折腾。

另外要注意的是,驱动安装需要编译内核模块,所以必须安装好 linux-headers 和 gcc、make 这些基础工具链。这一点很多人会忽视——比如你用的 Ubuntu Server 默认是最小化安装,连 build-essential 都没有,驱动安装脚本执行到一半就会报错。

还有网络环境。昇腾驱动和 CANN 的软件包非常大,比如 CANN Toolkit 解压出来有七八个 GB,从昇腾社区下载的时候如果网速不理想会很痛苦。建议配置好内网镜像源,或者提前把软件包下载到本地,避免在安装过程中因为网络中断导致半途而废。

2.3 Kubernetes 集群的基础状态

在开始装 NPU 相关组件之前,你的 Kubernetes 集群最好已经处于健康状态。不要求生产级高可用,但至少保证以下几点:

  • 控制平面和工作节点都能正常通信
  • 集群 DNS(CoreDNS)运行正常
  • 你计划接入 NPU 的节点上有足够的 CPU 和内存资源(device-plugin 本身很轻量,但 Pod 调度到该节点后会共享 CPU)
  • 节点上的 Docker 或 containerd 版本不要太老,我建议 Docker 20.10+,containerd 1.6+,太老的版本跟 Ascend Docker Runtime 的兼容性不好

如果集群还没搭好,可以用 kubeadm 先快速搭一套单节点集群来做测试。测试集群不需要追求高可用,把核心链路跑通才是重点。

3. 宿主机级部署:驱动、固件与 CANN 的安装细节

3.1 固件与驱动安装的先后关系

昇腾的软件栈安装顺序是固定的:先装固件,再装驱动,后装 CANN Toolkit。固件相当于设备的 BIOS,驱动是操作系统跟设备通信的通道,CANN 是上层的算子库和运行时。顺序错了,后面很容易出现找不到设备或者算子报错的问题。

安装包可以从昇腾社区官网下载,需要选择对应的型号和版本。我这里用的包名大概是这样的格式:

  • 固件包:Ascend-hdk-310p-npu-firmware_7.3.0_linux-aarch64.run(或 x86_64 版本)
  • 驱动包:Ascend-hdk-310p-npu-driver_23.0.3_linux-aarch64.run
  • CANN 包:Ascend-cann-toolkit_8.0.RC1_linux-aarch64.run

注意包名里的架构标识,不要拿 aarch64 的去 x86 机器上装。用 uname -m 先确认一下,x86_64 就选 x86_64,aarch64 就选 aarch64。

3.2 固件安装实操

在 Ubuntu 上安装固件前,先把依赖工具装好:

sudo apt update sudo apt install -y gcc make linux-headers-$(uname -r) build-essential

然后给安装包赋可执行权限,执行固件安装:

chmod +x Ascend-hdk-310p-npu-firmware_7.3.0_linux-x86_64.run sudo ./Ascend-hdk-310p-npu-firmware_7.3.0_linux-x86_64.run --full

安装完成后建议重启一次系统,让固件生效。这一步看似多此一举,但如果不重启,后续驱动安装时对设备的扫描可能不完整。有些资料说要断电重启,我自己测试发现普通 reboot 就够了,但如果你发现 npu-smi info 看不到设备,拔电再开一次是最彻底的。

3.3 驱动安装与验证

驱动包安装命令和固件类似:

chmod +x Ascend-hdk-310p-npu-driver_23.0.3_linux-x86_64.run sudo ./Ascend-hdk-310p-npu-driver_23.0.3_linux-x86_64.run --full

安装过程会编译内核模块,所以时间会比固件长一些。看到类似driver install success的输出就说明装上了。然后重启系统,用昇腾自带的工具验证设备是否被识别:

npu-smi info

正常情况下会输出设备列表,包括芯片型号、温度、HBM 内存、算力利用率等信息。如果输出No device found,大概率是固件和驱动的版本不匹配,或者内核模块没有正确加载,可以用 dmesg 查内核日志来定位。

注意:npu-smi 是昇腾驱动自带的命令行工具,路径一般在/usr/local/Ascend/driver/tools/下,可能需要将/usr/local/Ascend/driver/tools/加入 PATH 环境变量才能直接使用。

3.4 CANN Toolkit 的安装与环境变量配置

CANN 是昇腾的计算架构,类似于 NVIDIA 的 CUDA,提供了算子库、图编译引擎和运行时。容器内的应用运行时需要依赖它,所以必须在宿主机上安装好。

安装方式也是直接执行 .run 包,但这里有个坑:CANN Toolkit 默认安装目录是/usr/local/Ascend/ascend-toolkit,如果你没有 root 权限,或者后续想多版本共存,建议通过--install-path参数指定目录。我这里是默认安装,命令如下:

chmod +x Ascend-cann-toolkit_8.0.RC1_linux-x86_64.run sudo ./Ascend-cann-toolkit_8.0.RC1_linux-x86_64.run --full

CANN Toolkit 安装完成后,需要设置环境变量。官方提供了一条初始化脚本,直接 source 就能搞定:

source /usr/local/Ascend/ascend-toolkit/set_env.sh

最好把这行写进/etc/profile~/.bashrc,这样每次登录终端就不用手动执行了。

验证 CANN 是否装好,可以先看目录结构:

ls /usr/local/Ascend/ascend-toolkit/latest

同时用 python 检查能否正常导入昇腾的 Python 包。昇腾提供了 torch_npu 和 mindspore 等框架插件,但那些是后续业务层的事情,这里先做一个最简单的算子验证即可:

python3 -c "import torch; import torch_npu; print(torch_npu.npu.device_count())"

如果能输出设备数量,说明 CANN 基础环境和 NPU 设备已经打通了。

这里补充一下,如果你在容器里跑训练,大多数镜像会自带 CANN 环境,宿主机上的 CANN 其实不是必需的,但建议仍然装一份,原因是后续排查问题时,宿主机上有一份完整的 CANN 环境会方便得多。比如容器内算子报错,你可以在宿主机上用同样的输入跑一遍,快速判断是环境问题还是代码问题。

4. Ascend Docker Runtime:打通容器与 NPU 的桥梁

4.1 为什么需要专门的 Runtime

Docker 默认情况下容器是看不到宿主机上的昇腾设备的。原因在于 NPU 设备在宿主机上不只是几个 /dev 节点,它还需要访问 /dev/davinci* 设备文件、/dev/hisi_hdc 等管理设备,同时依赖宿主机上的驱动库和 CANN 运行库。如果只是简单地把 /dev/davinci0 映射进容器,运行时会因为缺少库文件或无法访问管理设备而报错。

Ascend Docker Runtime 是华为官方提供的容器 Runtime 扩展,作用就是自动化完成设备映射和运行库注入。它本质上是一个 Docker Runtime Shim,在 runc 创建容器之前会修改 OCI 规范配置,把设备节点、库文件、环境变量都注入进去。

类比一下:NVIDIA 那边有 nvidia-container-toolkit,Ascend 这边对应的就是 Ascend Docker Runtime。用惯了 NVIDIA 生态的同学理解起来会很快。

4.2 安装与配置 Ascend Docker Runtime

Ascend Docker Runtime 的安装包同样是一个 .run 文件,当前版本的包名大概是Ascend-docker-runtime_2.0.0_linux-x86_64.run。安装命令:

chmod +x Ascend-docker-runtime_2.0.0_linux-x86_64.run sudo ./Ascend-docker-runtime_2.0.0_linux-x86_64.run --full

安装完成后,需要修改 Docker 的 daemon.json 配置,让 Docker 使用 Ascend Runtime。文件路径在/etc/docker/daemon.json,修改后的内容类似:

{ "runtimes": { "ascend": { "path": "/usr/local/Ascend/Ascend-Docker-Runtime/x86_64/ascend-docker-runtime", "runtimeArgs": [] } } }

path 路径要根据实际安装位置做调整,装完可以用 find 命令确认一下。然后重启 Docker:

sudo systemctl restart docker

验证 Runtime 是否生效:

docker info | grep -i runtime

如果能看见 ascend 字样,说明已经注册成功。

4.3 验证容器内能否正常访问 NPU

Runtime 配好之后,用官方提供的昇腾推理镜像跑一个测试容器,验证容器内能否看到 NPU 设备:

docker run --rm -it \ --runtime ascend \ --device=/dev/davinci0 \ --device=/dev/davinci_manager \ --device=/dev/hisi_hdc \ -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ -v /usr/local/Ascend/ascend-toolkit:/usr/local/Ascend/ascend-toolkit \ --entrypoint /bin/bash \ ascendhub.huawei.com/public/ascend-foundation/ascend-pytorch:latest

容器起来后执行:

npu-smi info

能看到设备信息就说明 Runtime 映射成功了。这里有一点必须提醒:不同版本的容器镜像,需要的挂载和 --device 参数可能不同,官方文档里写得很细,不要照抄网上过时的命令。

4.4 与 Kubernetes 的衔接方式

集群里 Pod 使用 NPU 设备时,其实不会直接通过docker run --runtime ascend这种方式,而是靠 kubelet 在创建容器时自动选择 Runtime。kubelet 启动参数里需要配置--runtime-class或者通过 CRI 的 RuntimeClass 机制来指定。但昇腾官方给的 device-plugin 部署方案里,默认是把 runtime 通过 kubelet 的配置指向了 ascend。

具体来说,有两种做法:第一种是在 kubelet 的启动参数里添加--docker-runtime=ascend,但这个参数在一些新版本 K8s 里已经废弃了;第二种是借助 RuntimeClass 资源对象,在 Pod 的 YAML 中声明runtimeClassName: ascend。我更推荐第二种,因为它的粒度更细,可以让集群里同时存在使用普通 Runtime 的 Pod 和使用 Ascend Runtime 的 Pod,互不干扰。

不过要注意,使用 RuntimeClass 的前提是 containerd 或 CRI-O 的 runtime 配置里已经注册了 ascend。如果节点上用的是 Docker 加 dockershim(K8s 1.24 之前),RuntimeClass 实际上是不生效的。这块跟集群版本强相关,需要结合实际情况来选择方案。

5. device-plugin 部署:让 Kubernetes 认识 NPU 资源

5.1 device-plugin 的职责

Kubernetes 原生不认识“昇腾 NPU”这种资源,它只认识 CPU 和内存。为了让 K8s 能够调度 NPU,需要按照 Device Plugin 框架写一个插件,这个插件会做三件事:

  • 向 kubelet 注册节点上有多少张 NPU 卡
  • 在 Pod 需要 NPU 时,告诉 kubelet 把哪个设备编号(比如 /dev/davinci0)分配给这个 Pod
  • 监控设备健康状态,设备异常时从可用列表中摘除

昇腾官方提供了一个 device-plugin 实现,GitHub 仓库名是 Ascend/ascend-device-plugin,里面有完整的部署 YAML 和二进制。这个插件以 DaemonSet 方式运行在每个 NPU 节点上,这样每个节点的 kubelet 都能感知本地设备。

5.2 安装步骤与资源名解析

先拉取代码和镜像:

git clone https://github.com/Ascend/ascend-device-plugin.git cd ascend-device-plugin make build docker build -t ascend-device-plugin:latest .

如果不想自己编译,官方也提供了镜像,可以把镜像标签改成你需要的版本直接拉取。但考虑到有些内网环境无法访问外网,自己编译也算是一个必备技能。

部署方式很简单,官方提供的 YAML 里包含了 DaemonSet、ServiceAccount、ClusterRole 等资源,直接 apply 即可:

kubectl apply -f device-plugin.yaml

部署完成后,device-plugin 会向 kubelet 注册一个新的资源名,这个资源名是ascend.com/npu。注意它不是 GPU 那种nvidia.com/gpu,所以在写 Pod 的资源申请时千万别写错。

5.3 验证调度与资源分配

用一个测试 Pod 验证调度是否正常。创建如下 YAML:

apiVersion: v1 kind: Pod metadata: name: npu-test-pod spec: restartPolicy: OnFailure containers: - name: npu-test image: ascendhub.huawei.com/public/ascend-foundation/ascend-pytorch:latest command: ["/bin/bash", "-c", "npu-smi info && sleep 3600"] resources: limits: ascend.com/npu: 1

执行:

kubectl apply -f npu-test-pod.yaml kubectl describe pod npu-test-pod

在 Events 里看到类似ascend.com/npu: 1的资源分配记录,并且容器进入 Running 状态,说明 device-plugin 已经生效。进入容器执行npu-smi info应该能看到一张 NPU 卡。

这里有个比较容易踩的坑:如果节点上有多张卡,但 device-plugin 只注册了部分卡,原因通常是 device-plugin 的配置文件里指定了设备列表。默认配置是自动发现所有设备,但如果你改了AscendDeviceConfig文件,就得确保里面的卡号是真实存在的。

5.4 多卡分配与隔离机制

昇腾设备插件支持两种分配模式:整卡分配和虚拟化分片。默认是整卡,也就是一个 Pod 申请ascend.com/npu: 1就会独占一张卡,这张卡不能被其他 Pod 共享。如果卡少任务多,可以考虑开启昇腾的 vNPU 功能,把一张物理卡切成多个虚拟 NPU,但前提是你的设备型号支持 vNPU,且驱动和固件版本足够新。

从我实际测试来看,vNPU 的隔离效果跟硬件强相关。Atlas 300I Duo 这类推理卡对 vNPU 的支持目前还不是特别成熟,偶尔会出现显存不隔离导致任务之间互相影响的情况。如果只是做推理服务部署,我建议优先使用整卡模式,简单且稳定。

6. 监控 NPU 状态:Prometheus + 昇腾 Exporter

6.1 为什么监控很重要

NPU 设备跟 GPU 一样,在高负载下容易发热降频,HBM 内存也可能被 OOM。如果不做监控,任务跑到一半变慢了、甚至挂了,你都不知道原因是什么。在 Kubernetes 环境里,监控数据还可以联动 HPA 做弹性伸缩,比如当 NPU 利用率超过 80% 时自动扩容 Pod。

昇腾提供了一套监控方案,核心组件是ascend-exporter,它是一个独立运行的二进制或容器,通过昇腾驱动暴露的接口读取设备状态,然后转换成 Prometheus 指标格式。

6.2 部署 ascendent-exporter

从 GitHub 拉取 ascendent-exporter 代码后,构建镜像并部署:

git clone https://github.com/Ascend/ascend-exporter.git cd ascend-exporter make build docker build -t ascend-exporter:latest .

运行方式可以直接作为 Linux 进程跑,也可以在 K8s 里以 DaemonSet 方式部署。我建议 DaemonSet 方案,因为这样每个有 NPU 的节点都会有一个 exporter 实例,Prometheus 通过服务发现就能自动抓取所有节点的指标。

给一个精简的 DaemonSet 示例:

apiVersion: apps/v1 kind: DaemonSet metadata: name: ascend-exporter namespace: monitoring spec: selector: matchLabels: app: ascend-exporter template: metadata: labels: app: ascend-exporter spec: hostNetwork: true containers: - name: ascend-exporter image: ascend-exporter:latest ports: - containerPort: 9100 hostPort: 9100 volumeMounts: - name: driver mountPath: /usr/local/Ascend/driver securityContext: privileged: true volumes: - name: driver hostPath: path: /usr/local/Ascend/driver

这里用 hostNetwork 是为了让 Prometheus 直接通过节点 IP 加端口访问 Exporter,避免额外的 Service 配置。如果节点上 9100 端口已被占用,可以改成一个不冲突的端口。

6.3 Prometheus 抓取配置与常用指标

在 Prometheus 的配置文件里增加一个 job:

scrape_configs: - job_name: 'ascend-npu' static_configs: - targets: - '10.0.0.11:9100' - '10.0.0.12:9100'

当然,如果你是用 Prometheus Operator,更好的是添加一个 ServiceMonitor 或 PodMonitor 来做自动发现。

常用的指标包括:

  • ascend_npu_utilization:NPU 算力利用率
  • ascend_npu_memory_usage:HBM 内存使用量
  • ascend_npu_temperature:芯片温度
  • ascend_npu_power:功耗

Grafana 里导入昇腾官方提供的 dashboard JSON,就能直接看到可视化界面。我在实测中发现,温度的采集频率不需要太高,15 秒一次足够,频繁采集反而会增加 driver 的读取压力。

7. 常见问题与排查技巧实录

7.1 容器内看不到 /dev/davinci 设备

这个问题七八成是 Ascend Docker Runtime 没配好。先检查 Docker daemon.json 里的路径是否正确,再确认 runtime 是否注册成功。还有一种情况:容器启动时没有通过--device映射设备,或者在 K8s 里没有设置 RuntimeClass。

排查命令:

docker info | grep -i runtime ls -l /dev/davinci*

7.2 device-plugin 注册成功但 Pod 一直 Pending

从现象上看,Pod 一直处于 Pending 状态,describe 之后看到有空资源但调度不上去。这种问题最常见的原因是 device-plugin 上报的资源名跟 Pod 请求的资源名不一致。比如 YAML 里写的是ascend.com/npu,但插件实际注册的可能是ascend.com/vNPU(开了虚拟化后)。查一下节点资源:

kubectl describe node <node-name> | grep ascend

如果节点上没有可分配的ascend.com/npu,就是注册名对不上。

7.3 任务跑起来后 npu-smi 显示利用率很高但实际速度很慢

在高并发推理场景下,这类问题一般不是硬件问题,而是软件栈层面的算子编译开销太大。CANN 第一次执行某个算子时会触发 AOE 或者算子编译,耗时可能达到几十秒甚至几分钟。解决办法是预先执行一次 warmup,或者开启 CANN 的算子缓存功能,把编译结果落到磁盘上。这个优化在正式上线前一定要做。

7.4 突然发现某张卡设备状态变为 Abnormal

device-plugin 本身有健康检查机制,设备异常时会自动把这张卡从可用资源里摘除,避免继续往上面调度任务。这时候要做的是登录节点,用 npu-smi info 看具体错误码,比如 HBM 报错、温度报警等。如果只是过热,清理一下风扇积灰或者降低机房温度就能恢复;如果是 HBM 报错,这卡大概率得返修了。

7.5 常见问题速查表

问题现象可能原因解决方向
宿主机找不到 NPU固件/驱动未装或版本不匹配按配套表重装固件和驱动
docker run 找不到 runtimeAscend Docker Runtime 未注册检查 daemon.json 和重启 Docker
容器内没有设备节点未映射设备或卸载了 Runtime确认 --device 参数和 RuntimeClass
Pod Pending 无法调度资源名不对或 device-plugin 未部署kubectl describe node 查看资源名
NPU 利用率上不去算子编译开销大开启算子缓存或执行 warmup
Exporter 无指标输出driver 路径未挂载或端口冲突检查挂载点和端口占用
温度过高导致降频散热不足或风道阻塞物理排查散热环境

7.6 一条高效的排查链路

我自己在实战中总结了一套排查逻辑:先看硬件层(driver 是否正常识别),再看运行时层(Runtime 是否注册成功),然后看调度层(device-plugin 是否正确上报资源),最后看业务层(容器内是否真正拿到设备并运行)。

这套顺序看似基础,但真到了排障的时候,大多数人是一上来就翻容器日志、改代码,折腾半天发现是驱动没装好。从底层往上排查,效率会高很多。

8. 层面踩过的坑和最终建议

部署到现在,整个链路已经通了。最后说几个我自己的感悟,也算是对后来者的一点建议。

如果你只是手上有一两张卡想试点,确实可以直接在宿主机上跑,不用上 K8s。但一旦涉及多团队共享算力、需要弹性伸缩、需要统一监控,这套 K8s 方案就是必须的。投入的时间成本虽然不低,但长远看是划算的。

实操时有一个容易被忽略的点:昇腾的 Docker 镜像一般都非常大,动辄十几个 GB,拉了镜像之后节点磁盘就告急。建议提前规划好容器镜像存储空间,或者配置镜像清理策略,不然过两个月磁盘写满,Pod 会全部 Evict。

另外,昇腾软件栈的版本迭代速度比 NVIDIA 快得多,社区资料也比较零散。不要试图找到一个“万能教程”一次装好,最靠谱的方法是:每一步都按官方文档走,验证成功再进入下一步。如果哪一步卡住了,优先查昇腾社区官方 FAQ,而不是在搜索引擎里随便找答案——因为版本一换,很多网上步骤就不成立了。

目前我的测试集群已经稳定跑了两周,跑着几个 CV 推理任务和 PyTorch 训练任务,NPU 利用率和温度都在正常范围。后续我打算尝试把昇腾的 vNPU 功能接进来,测试一下多租户的隔离效果,等有结果了再跟大家分享。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询