尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

昇腾NPU接入Kubernetes:从驱动到CANN再到Device Plugin的完整指南

昇腾NPU接入Kubernetes:从驱动到CANN再到Device Plugin的完整指南 把一张昇腾推理卡塞进节点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-pluginK8s 调度器的眼睛和手。它通过 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-架构.runCANN 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 ToolkitCANN 是昇腾的计算架构类似 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 } } }配置完成后重启 Dockersudo systemctl restart docker4.2 用一条命令验证运行时生效在宿主机上执行下面这条命令如果npu-smi info能在容器里正常输出说明路径挂载和设备映射都工作了docker run --rm --runtimeascend \ -e ASCEND_VISIBLE_DEVICES0 \ -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_DEVICES0和 GPU 里的CUDA_VISIBLE_DEVICES是同一个思路控制容器能看到哪几张卡。如果用户想暴露所有卡写all即可。我踩过的一个典型坑是驱动目录挂载漏了lib64或tools中的某一个导致npu-smi命令在容器里能执行但真正跑模型时报Device open failed。这种问题很难排查因为表面上看设备节点已经有了。所以挂载路径一定要完整不要自己发挥。4.3 如果你的集群已经用了 containerd现在很多 K8s 集群默认用 containerd而不是 Docker这时docker.sock和daemon.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 ascendtrue 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 有权限访问 DCMIapiVersion: 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 和 GrafanaPrometheus 通过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: 15sGrafana 仪表盘没有全网统一标准但社区里能找到不少昇腾 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 限制建议防止一个用户申请掉全部 NPURuntimeClassName 配置必要由平台注入或由用户显式声明监控看板强烈建议平台用户也需要看到资源使用情况我见过不少平台接入失败的案例平台本身没问题但底层 device-plugin 没部署或者 runtime handler 没设置结果用户在界面上点了创建任务一直 Pending 或者在容器启动阶段失败。所以平台接入前先用上一节的测试 Pod 把底层链路验证清楚再让平台上业务。7.3 平台提交任务后的排障顺序如果平台提交的任务起不来不要直接在平台日志里翻按下面的顺序快速收缩问题范围kubectl get events --sort-by.lastTimestamp看 Pod 调度阶段有没有资源不足、节点选择器不匹配的问题。如果 Pod 一直 Pending查节点allocatable和capacity确认 device-plugin 是否上报资源。如果 ContainerCreating看容器运行时错误优先怀疑 runtimeClassName 没有生效或者 Ascend runtime 路径配置错误。如果 Pod Running 但任务报错进容器手动执行npu-smi info确认设备节点、驱动库和 CANN 环境变量是否完整。这个顺序我用了很多次基本能在 10 分钟内定位到具体问题层。核心原则是从 K8s 调度层往下排查不要一上来就去看业务代码。最后补充一点运维上的建议这套链路前后我折腾了不止一次最大的感受是很多问题不是出在某个安装包上而是出在版本配套和路径挂载上。昇腾驱动、CANN、Ascend Docker Runtime、device-plugin、exporter这些组件的版本就像齿轮一样必须咬合随意搭配大概率会踩坑。我现在的习惯是每批 NPU 节点交付时把所有组件的版本号记在一个固定 ConfigMap 里或者直接写进节点 annotation 上。这样三个月后升级组件或者排查问题不用再去翻安装日志直接看版本组合就能判断是不是配套出了问题。另外一个操作上的小建议首次跑通链路后把验证用的npu-testPod 和几条核心检查命令存成脚本放进团队文档。下次新加节点时照着脚本执行一遍十分钟就能确认新节点是否达到上线标准。昇腾接入 K8s 这件事说难不难说简单也不简单但只要链路清晰、验证充分它完全可以像管理普通工作负载一样稳定运行。
返回列表