昇腾NPU接入Kubernetes:从驱动到CANN再到Device Plugin的完整指南
2026/9/17 3:39:20 网站建设 项目流程

把一张昇腾推理卡塞进节点,Kubernetes 并不会自动认识它。这就是做 AI 基础设施的同学最常遇到的第一道坎:GPU 那边有 Nvidia 一整套相对成熟的容器生态,昇腾 NPU 这边从驱动、CANN、容器运行时到 device-plugin,每一层都得自己趟一遍。文档散、版本多、命名还五花八门,加上大模型推理和微调任务越来越多,NPU 要接入 K8s 的需求早就不是实验性需求,而是生产环境里的刚需。

这篇文章把我实际趟过的链路完整过一遍,从裸机环境准备开始,依次解决好驱动、CANN、Ascend Docker Runtime、device-plugin 和监控,最后接到 CubeStudio 这类上层 AI 平台。整个流程适合正在做 AI 基础架构、算法工程化,或者想把手上昇腾机器真正用起来的读者,照着做可以把"NPU 只是插在服务器上的一块卡"变成"K8s 可以按需调度的一份资源"。

1. 先说清楚:Kubernetes 调度 NPU 的完整逻辑链路

很多第一次接触的人会问:我把 NPU 驱动装好了,npu-smi info也能看到卡了,为什么 K8s 还是不认识它?

原因是 K8s 默认只认识 CPU 和内存这类"内建资源",其余设备必须通过 Extended Resource 和 Device Plugin 机制注册进来。也就是说,硬件装上只是第一步,还要让 kubelet 知道这台节点上有几张卡、每张卡能被怎么分配、分配后容器怎么使用。整套链路拆开看,一共是五层:

  • 硬件和内核驱动:操作系统层面识别 NPU,生成/dev/davinci0这类设备节点,这是最底层的前提。
  • CANN 运行环境:昇腾的 Runtime、算子库、加速接口都在这一层。AI 框架最终是通过 CANN 来调用 NPU,而不是直接操作设备文件。
  • Ascend Docker Runtime:容器运行时负责把设备节点、驱动动态库、环境变量注入到容器里,让容器内进程真正"摸"到 NPU。
  • device-plugin:K8s 调度器的眼睛和手。它通过 gRPC 与 kubelet 通信,上报设备数量状态,Pod 调度到节点后再把设备分配信息返回给 kubelet。
  • 监控 exporter:从 NPU 设备上采集温度、利用率、显存占用等指标,接入 Prometheus,让运维有数据可看。

这五层之间的关系可以这样理解:驱动让 NPU 通电工作,CANN 封装了计算能力,Ascend Docker Runtime 负责把整个运行环境装进容器,device-plugin 是 K8s 的"资源中介",监控则是独立于调度链路之外、但生产环境绝对不能少的观测手段。

我在实际项目里见过一种错误做法:只装了驱动和 CANN,就直接在 K8s 上跑一个特权容器,打算绕过 device-plugin 和运行时。结果 Pod 倒是起来了,但容器里看不到设备节点,或者runtime error报错,最后还是要回来把每一层补齐。昇腾接入 K8s 没有捷径,但把链路捋清楚之后,每一步的报错都能一眼定位到具体层级。

2. 环境准备阶段最容易翻车的几个点

正式安装之前,先花半小时确认环境,能省下后面一整天的排障时间。昇腾的安装包对操作系统、内核、架构非常敏感,版本不匹配是踩坑重灾区。

2.1 硬件型号和软件包的对应关系

昇腾目前常见的推理卡有 Atlas 300I、300V Pro 等,对应昇腾 310P 系列芯片。安装之前先用lspci确认硬件是否被服务器识别:

lspci | grep -i ascend

如果输出为空,先检查服务器 BIOS 里 PCIe 设备是否启用,或者卡是不是没插紧。这一步不做,后面所有安装都会白忙。

软件包方面,驱动包名一般是Ascend-hdk-310P-npu-driver_版本号_linux-架构.run,CANN Toolkit 包名是Ascend-cann-toolkit_版本号_linux-架构.run。架构上要注意区分 x86_64 和 aarch64,拿错包安装直接报错。

2.2 操作系统、内核和版本配套关系

昇腾官方支持的常见宿主系统包括 Ubuntu 20.04/22.04、openEuler 22.03 等。安装驱动前必须确认内核头文件存在,否则驱动编译模块时会报错:

uname -r sudo apt install linux-headers-$(uname -r)

这里有个很容易忽略的问题:安装完内核更新包之后,如果重启进入的是新内核,但头文件没装对版本,驱动安装时依然会失败。极端情况下我遇到过linux-headers装的版本和当前内核不一致,驱动编出来的 KO 模块加载不了,npu-smi info一直显示 "No devices"。

版本配套是另一个重点。昇腾驱动和 CANN 有严格的配套约束,例如驱动版本和 CANN 版本必须落在官方兼容性列表里。不建议盲目追新,更不建议生产环境混搭版本。我自己的习惯是:决定要装某个 CANN 版本之前,先去昇腾社区查一下对应配套的驱动版本,记录下来再动手。

2.3 建议的版本组合参考

组件建议版本说明
操作系统Ubuntu 22.04 LTS生态最成熟,踩坑资料相对多
昇腾驱动24.0.0 及以上版本以官方发布为准,注意区分推理卡和训练卡驱动
CANN Toolkit8.0.RC1 及以上版本与驱动严格配套,不要跨版本
Kubernetes1.26 及以上Device Plugin API 稳定,对 Extended Resource 支持成熟
容器运行时containerd 1.7+ 或 Docker 24+需要支持自定义 runtime handler

如果条件允许,建议准备一个干净的测试节点,不要在已经跑着业务流的节点上做这套实验。等完整流程跑通后,再回填到生产环境也不迟。

3. 驱动与 CANN:算力底座怎么装稳

环境确认没问题之后,进入真正的安装环节。我习惯把这一步拆成两件事:先让 NPU 能被系统看见,再让 AI 框架能调用它。两者装完才能说底座就绪。

3.1 安装昇腾驱动

驱动安装之前再次确认内核头文件,然后执行安装包:

sudo ./Ascend-hdk-310P-npu-driver_24.0.0_linux-aarch64.run --full --install-for-all

--full参数表示安装完整组件,--install-for-all让所有用户都有访问权限。安装完成后千万别急着跑模型,先确认设备节点和工具是否正常:

ls /dev/davinci* npu-smi info

正常情况下npu-smi info能看到卡的温度、芯片型号、显存信息。如果提示权限不足,说明当前用户不在ascend用户组里:

sudo usermod -a -G ascend $USER

改完用户组要重新登录 session 才会生效。

3.2 安装 CANN Toolkit

CANN 是昇腾的计算架构,类似 GPU 生态里的 CUDA。AI 框架(PyTorch、MindSpore 等)通过 CANN 的 Acl Runtime 和算子库才能真正发起 NPU 计算。驱动都装好了但没装 CANN,容器里跑模型时会直接报libascendcl.so找不到之类的错误。

chmod +x Ascend-cann-toolkit_8.0.RC1_linux-aarch64.run ./Ascend-cann-toolkit_8.0.RC1_linux-aarch64.run --install

安装默认路径是/usr/local/Ascend/ascend-toolkit/latest,推荐固定这个路径,后面容器运行时挂载配置会用到。装完后要 source 环境变量:

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

为了让环境变量在每次登录时自动生效,可以加进/etc/profile.d/ascend.sh。这一步通常写在官方文档里,但很多人在容器化部署时才意识到:容器里的进程也需要这套环境变量,后面我们在 Ascend Docker Runtime 里解决。

3.3 安装过程中真实的报错和解决记录

现象根本原因处理方式
驱动安装到一半报kernel headers not found内核头文件缺失或版本不匹配安装linux-headers-$(uname -r)后重试
npu-smi info报权限拒绝用户不在 ascend 组usermod -a -G ascend $USER后重新登录
重启后找不到设备驱动模块未自动加载执行sudo modprobe drv_pcie并确认开机自启配置
容器内执行应用报 CANN 库缺失容器镜像里只有驱动没有 CANN使用带 CANN 的运行镜像,或挂载宿主 CANN 目录

我自己的经验是:不要急着把所有组件一次性装完再测试,每装一层就验证一层。驱动装完,npu-smi info能看;CANN 装完,跑一个简单的python -c "import torch; import torch_npu"能过,再进行下一步。宁可慢十分钟,也不要到最后链路排障时从头拆。

4. Ascend Docker Runtime:让容器真正"摸"到 NPU 的那一步

驱动和 CANN 装好后,直接docker run一个普通的 PyTorch 容器,容器里大概率还是用不了 NPU。因为 Docker 默认不会把宿主机的设备节点和驱动库映射进容器。这时候就需要 Ascend Docker Runtime 出场。

4.1 为什么容器还需要一个独立运行时

设备文件(/dev/davinci0)、驱动动态库、CANN 库目录,这些资源不会自己出现在容器里。容器运行时需要在创建容器时,把这些路径注入进去。GPU 生态里的nvidia-container-toolkit干的是同一件事,Ascend Docker Runtime 就是昇腾的对应方案。

安装 Ascend Docker Runtime 同样是一个.run包:

./Ascend-docker-runtime_5.0.RC1_linux-aarch64.run --install

然后修改 Docker daemon 配置,在/etc/docker/daemon.json里注册这个运行时:

{ "runtimes": { "ascend": { "path": "/usr/local/Ascend/Ascend-Docker-Runtime/ascend-docker-runtime" } } }

配置完成后重启 Docker:

sudo systemctl restart docker

4.2 用一条命令验证运行时生效

在宿主机上执行下面这条命令,如果npu-smi info能在容器里正常输出,说明路径挂载和设备映射都工作了:

docker run --rm --runtime=ascend \ -e ASCEND_VISIBLE_DEVICES=0 \ -v /usr/local/Ascend/driver/lib64:/usr/local/Ascend/driver/lib64 \ -v /usr/local/Ascend/driver/tools:/usr/local/Ascend/driver/tools \ ascendhub.huawei.com/public/ascend-ubuntu:22.04 \ npu-smi info

这里ASCEND_VISIBLE_DEVICES=0和 GPU 里的CUDA_VISIBLE_DEVICES是同一个思路:控制容器能看到哪几张卡。如果用户想暴露所有卡,写all即可。

我踩过的一个典型坑是:驱动目录挂载漏了lib64tools中的某一个,导致npu-smi命令在容器里能执行,但真正跑模型时报Device open failed。这种问题很难排查,因为表面上看设备节点已经有了。所以挂载路径一定要完整,不要自己发挥。

4.3 如果你的集群已经用了 containerd

现在很多 K8s 集群默认用 containerd,而不是 Docker,这时docker.sockdaemon.json这套配置就不生效了。需要看当前 Ascend 容器运行时是否提供 containerd 插件方案。如果支持,需要在 containerd 配置里增加 runtime handler,并在 Pod 的runtimeClassName中指定。


5. device-plugin:把 NPU 变成 K8s 可调度的资源

宿主机环境已经通了,接下来要让 K8s 认识并调度这张卡。这里的主角是 device-plugin,它本质上是一个和 kubelet 通信的 gRPC 客户端。

5.1 Device Plugin 的工作机制

device-plugin 的主要职责有两个:

  • ListAndWatch:向 kubelet 上报当前节点上的设备列表以及健康状态。K8s 会把这些设备作为 Extended Resource 展示在 Node 上。
  • Allocate:当 Pod 被调度到节点、需要占用 NPU 资源时,device-plugin 告诉 kubelet 应该向容器注入哪些环境变量、设备节点和挂载路径。

理解这一点非常重要,因为它决定了排障方向:设备数量不对,去看 ListAndWatch 上报;容器起不来或设备注入失败,去看 Allocate 的返回内容和容器运行时配置。

5.2 部署 device-plugin DaemonSet

在昇腾社区里可以拿到 device-plugin 镜像,一般部署成 DaemonSet,确保每个 NPU 节点都跑一个。核心 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 nodeSelector: ascend: "true" containers: - name: device-plugin image: ascend-device-plugin:latest imagePullPolicy: IfNotPresent securityContext: privileged: true volumeMounts: - name: device-plugin mountPath: /var/lib/kubelet/device-plugins - name: sys mountPath: /sys volumes: - name: device-plugin hostPath: path: /var/lib/kubelet/device-plugins - name: sys hostPath: path: /sys

给 NPU 节点打上标签,然后部署:

kubectl label node <your-node> ascend=true kubectl apply -f ascend-device-plugin.yaml

部署完之后查看节点资源,如果能看到huawei.com/Ascend310P这类资源,说明上报成功:

kubectl describe node <your-node> | grep -A5 "huawei.com/Ascend"

输出里会显示类似huawei.com/Ascend310P: 1的可分配数量。如果这里看不到,问题集中在 device-plugin 和宿主机驱动的连通性上,优先查 device-plugin 的日志。

5.3 写一个测试 Pod 验证调度

apiVersion: v1 kind: Pod metadata: name: npu-test spec: runtimeClassName: ascend containers: - name: npu-test image: ascendhub.huawei.com/public/ascend-ubuntu:22.04 command: ["npu-smi", "info"] resources: limits: huawei.com/Ascend310P: 1

特别注意:Extended Resource 在 K8s 中的使用限制是requests必须等于limits,而且只能写在limits里。如果只写requests或两者数量不一致,调度器会直接报错。

kubectl apply -f npu-test.yaml kubectl get pod -o wide kubectl logs npu-test

看到npu-smi info的输出,说明从 K8s 到底层硬件这一整条资源链路已经打通。

5.4 说一句算力切分

310P 系列支持算力切分,可以让一张物理卡被切分成多个逻辑实例,device-plugin 可以配合这种模式,把更多的请求分发到同一张卡上。但切分方案会引入更多配置项,第一次接入时建议先用整卡跑通,后续再按需求研究切分。不要一上来就上复杂配置,出了问题很难分清是基础链路的问题还是切分配置的问题。

6. 监控:NPU 指标接进 Prometheus 的实操

资源调度通了,下一步是让"看得见"变成"看得清"。集群里如果只有 Pod 状态,没有 NPU 的温度、利用率和显存监控,业务真出问题的时候就像开车没有仪表盘。

昇腾生态虽然没有和 NVIDIA DCGM 完全对标的一套全家桶,但基于 CANN 的 DCMI 接口做的 Prometheus exporter 在社区里已经很常见。部署方式不复杂,核心是一个独立 exporter 进程,它从 NPU 设备读取指标,暴露成/metrics接口。

6.1 部署 exporter 到 NPU 节点

常见的做法是部署一个 DaemonSet,让每个 NPU 节点都跑一个 exporter。关键点是要挂载宿主机驱动目录,并让 exporter 有权限访问 DCMI:

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 nodeSelector: ascend: "true" containers: - name: ascend-exporter image: ascend-exporter:latest ports: - containerPort: 9100 volumeMounts: - name: driver-lib mountPath: /usr/local/Ascend/driver/lib64 - name: hisi mountPath: /usr/local/Ascend/driver/tools resources: requests: cpu: 100m memory: 128Mi limits: cpu: 200m memory: 256Mi volumes: - name: driver-lib hostPath: path: /usr/local/Ascend/driver/lib64 - name: hisi hostPath: path: /usr/local/Ascend/driver/tools

部署后访问节点 IP 加端口,如果看到一堆ascend_前缀的指标,说明 exporter 正常。

6.2 接入 Prometheus 和 Grafana

Prometheus 通过ServiceMonitor或者静态scrape_configs采集 exporter 指标。这里给一个 ServiceMonitor 示例:

apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: ascend-exporter namespace: monitoring spec: selector: matchLabels: app: ascend-exporter endpoints: - port: metrics interval: 15s

Grafana 仪表盘没有全网统一标准,但社区里能找到不少昇腾 NPU 监控面板。建议至少把下面几个指标放到首页:

指标名含义使用场景
ascend_npu_temperatureNPU 当前温度高温告警和降频排查
ascend_npu_ai_core_utilizationAI Core 利用率确认业务是否真的在跑
ascend_npu_hbm_usageHBM 显存使用量提前发现显存耗尽风险
ascend_npu_power实时功耗能效分析和成本核算

6.3 配一个实用的告警规则

告警规则不在多,在于能抓住真正影响业务的问题。我目前在生产环境保留的 NPU 相关告警就三条:

groups: - name: ascend-npu.rules rules: - alert: NPUTemperatureHigh expr: ascend_npu_temperature > 85 for: 5m labels: severity: warning annotations: summary: "NPU 温度超过 85 度" - alert: NPUDeviceDown expr: ascend_npu_healthy == 0 for: 2m labels: severity: critical annotations: summary: "NPU 设备异常" - alert: NPUHBMUsageHigh expr: ascend_npu_hbm_usage / ascend_npu_hbm_total > 0.9 for: 10m labels: severity: warning annotations: summary: "NPU 显存使用超过 90%"

监控告警我会放在资源调度打通之后立刻做,不要拖。因为没有监控的 NPU 集群,出问题时很难快速定位是业务问题还是硬件问题,最后往往是互相甩锅。

7. CubeStudio 接入:平台层如何调度和验证 NPU

前三章和监控解决的是"NPU 可用、可调度、可观测"的问题,但真正面向算法工程师和业务团队的,往往是一个类似 CubeStudio 的 AI 开发运行平台。平台层的作用是把底层 K8s 资源封装成一个个可交互的开发环境、训练任务和推理服务。如果你的团队用的不是 CubeStudio,而是自研平台或者其他开源平台,这一章的对接逻辑同样适用。

7.1 平台层在整条链路中的位置

CubeStudio 在我的理解里,本质上是 K8s 之上的一个控制台和任务调度门户。它通过 K8s API 与集群通信,当用户点击"创建推理服务"时,平台会帮用户构造一个 Pod 或 Deployment,并明确申请huawei.com/Ascend310P这类 Extended Resource。

这要求平台在创建 Pod 时至少做对三件事:

  • 在资源声明里带上完整的 NPU 资源名和数量。
  • 为工作负载指定runtimeClassName: ascend,确保容器运行时知道要去调用 Ascend 运行时注入设备。
  • 配置好镜像仓库的 Secret,拉取包含 CANN 运行环境的推理镜像。

平台层做好了这三件事,算法工程师才能做到"我只需要在界面上选几张卡,然后就能跑模型",完全不用关心驱动和 device-plugin。

7.2 一个典型的平台接入检查清单

检查项是否必要说明
K8s 集群可调度 NPU 节点必要kubectl describe node能看到 NPU 资源
默认 StorageClass必要Notebook 和模型权重需要持久化存储
ImagePullSecret必要昇腾镜像通常来自专用仓库
ResourceQuota 限制建议防止一个用户申请掉全部 NPU
RuntimeClassName 配置必要由平台注入或由用户显式声明
监控看板强烈建议平台用户也需要看到资源使用情况

我见过不少平台接入失败的案例:平台本身没问题,但底层 device-plugin 没部署,或者 runtime handler 没设置,结果用户在界面上点了"创建",任务一直 Pending 或者在容器启动阶段失败。所以平台接入前,先用上一节的测试 Pod 把底层链路验证清楚,再让平台上业务。

7.3 平台提交任务后的排障顺序

如果平台提交的任务起不来,不要直接在平台日志里翻,按下面的顺序快速收缩问题范围:

  1. kubectl get events --sort-by=.lastTimestamp,看 Pod 调度阶段有没有资源不足、节点选择器不匹配的问题。
  2. 如果 Pod 一直 Pending,查节点allocatablecapacity,确认 device-plugin 是否上报资源。
  3. 如果 ContainerCreating,看容器运行时错误,优先怀疑 runtimeClassName 没有生效,或者 Ascend runtime 路径配置错误。
  4. 如果 Pod Running 但任务报错,进容器手动执行npu-smi info,确认设备节点、驱动库和 CANN 环境变量是否完整。

这个顺序我用了很多次,基本能在 10 分钟内定位到具体问题层。核心原则是从 K8s 调度层往下排查,不要一上来就去看业务代码。

最后补充一点运维上的建议

这套链路前后我折腾了不止一次,最大的感受是:很多问题不是出在某个安装包上,而是出在版本配套和路径挂载上。昇腾驱动、CANN、Ascend Docker Runtime、device-plugin、exporter,这些组件的版本就像齿轮一样必须咬合,随意搭配大概率会踩坑。

我现在的习惯是,每批 NPU 节点交付时,把所有组件的版本号记在一个固定 ConfigMap 里,或者直接写进节点 annotation 上。这样三个月后升级组件或者排查问题,不用再去翻安装日志,直接看版本组合就能判断是不是配套出了问题。

另外一个操作上的小建议:首次跑通链路后,把验证用的npu-testPod 和几条核心检查命令存成脚本,放进团队文档。下次新加节点时,照着脚本执行一遍,十分钟就能确认新节点是否达到上线标准。昇腾接入 K8s 这件事,说难不难,说简单也不简单,但只要链路清晰、验证充分,它完全可以像管理普通工作负载一样稳定运行。

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

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

立即咨询