ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

昇腾NPU接入Kubernetes全流程实操:从驱动到device-plugin的完整指南

昇腾NPU接入Kubernetes全流程实操:从驱动到device-plugin的完整指南 昇腾NPU接入Kubernetes这件事我最早踩坑的时候心里就一个念头这跟GPU那套完全是两码事。NVIDIA的思路是装个device-plugin、容器里挂上nvidia-smi就完事但昇腾这条链路里驱动、CANN、Ascend Docker Runtime、device-plugin四层组件少一个都不行而且每一层装错了都不会直接报“我错了”只会让你在某个深夜对着一个起不来的Pod怀疑人生。这篇文章把我完整跑通Ascend 910系列接入K8s的过程、命令、配置和踩过的坑都整理出来给你一份可以直接照着做的实操路径。适合看这篇内容的人比较明确正在做AI平台基础设施、想把昇腾NPU纳入Kubernetes调度体系的工程师在昇腾环境上跑训练或推理、需要搞懂资源是怎么被分配出去的应用侧同学以及那些准备从GPU平滑迁移到昇腾、想知道两边接入方式到底差在哪里的团队。看完之后你应该能独立完成一台昇腾服务器从裸机状态到K8s可调度NPU全流程并且知道出了问题该去哪一层排查。1. 昇腾NPU接入K8s的整体链路拆解先搞清楚一件事NPU本身只是一个PCIe设备Kubernetes完全不认识它。你需要在硬件之上叠一套软件层把物理设备翻译成K8s能理解的资源再把资源注入到容器里。这条链路从上到下大概是这个关系K8s API Server认识的是资源名比如huawei.com/Ascend910device-plugin负责上报这种资源而容器真正能用上NPU靠的是运行时把设备文件挂进去运行时执行设备映射时又要依赖驱动和固件已经把设备初始化好了CANN则提供容器内能够调用的算子库和运行时环境。1.1 四层组件各自的职责边界很多人搞混这四层是因为它们在名字上有点相似、安装顺序又环环相扣。我习惯用一句话概括每层是干什么的驱动与固件让操作系统能看到并管理NPU硬件装完之后宿主机上执行npu-smi info能看到板卡信息。CANN Toolkit昇腾的计算软件栈类比CUDA Toolkit提供算子库、图编译引擎和运行时API是跑模型必需的。Ascend Docker Runtime负责在容器创建时把NPU设备文件和驱动依赖注入容器类比nvidia-container-toolkit。device-pluginK8s侧的组件负责把NPU资源上报给调度器让Pod可以按资源名申请NPU。这个顺序不是随便定的而是有依赖关系的。驱动装不上后面CANN就算装上也没法初始化Runtime没有注册进Docker或containerddevice-plugin即使上报了资源Pod起来了也拿不到设备节点。1.2 为什么不能照搬NVIDIA的GPU接入方式对比NVIDIA生态你会发现昇腾在容器化这块做得非常“重”。NVIDIA的驱动在宿主机开发库全部打进镜像里device-plugin启动时直接把/dev/nvidia*和nvidia-smi挂到容器即可昇腾这边除了设备节点/dev/davinci*还需要一堆驱动动态库和系统接口同步映射进容器这些操作如果靠手工挂载来做配置量大且极易遗漏。Ascend Docker Runtime的意义就在这它像一个自动化的“搬运工”在容器创建前把设备节点、库文件、环境变量一次性准备好。我个人理解昇腾选择这种方式一是因为NPU的IO管理需要用户态驱动库参与二是CANN的运行时依赖真实硬件状态。所以头几次实操时先别动手改配置把这条链路在脑子里过清楚后面遇到任何问题都能定位到具体层。2. 驱动与CANN的安装先让宿主机能看到NPU再谈容器容器化部署再怎么花哨根基都在宿主机上。这块我吃过最大的亏就是装驱动前没确认内核版本跟固件是否匹配结果驱动模块加载失败、系统日志刷了一堆报错还找不到原因。所以建议每一步都留验证记录。2.1 驱动与固件的配套关系与安装昇腾NPU的驱动安装包是HDKHardware Development Kit形态发布的里面包含driver和firmware。下载的时候注意平台架构x86和arm64的包是分开的名字一般是类似Ascend-hdk-xxx_linux-x86_64.run这种格式具体以官方发布为准。安装命令经典且直接chmod x Ascend-hdk-*.run ./Ascend-hdk-*.run --full --install装完驱动后要做两件事一是确认内核模块加载成功常见模块名有npu、davinci_drv等可以用lsmod | grep davinci检查二是查看设备节点是否生成昇腾NPU的设备文件是/dev/davinci0、/dev/davinci_manager这种ls /dev/davinci*如果什么都看不到说明驱动初始化失败。这里有个容易忽略的细节新内核升级后驱动模块可能没有自动重新编译这是驱动装完重启之后npu-smi不可用的常见原因。处理方式是确保系统内核版本与驱动包支持的版本列表一致尽量不要在装驱动后随意升级内核。在验证驱动前先检查一下内核模块状态比直接重启机器高效得多。2.2 CANN Toolkit安装与set_env.shCANN Toolkit的安装包格式类似Ascend-cann-toolkit_xxx_linux-x86_64.run命名里的版本号需要和驱动版本配合官方有配套关系表下载前务必对照。安装命令chmod x Ascend-cann-toolkit_*.run ./Ascend-cann-toolkit_*.run --install默认安装路径是/usr/local/Ascend/ascend-toolkit。重点来了任何进程在跑昇腾相关的程序之前必须先加载CANN的环境变量否则会出现找不到libascendcl.so之类的库错误。source /usr/local/Ascend/ascend-toolkit/set_env.sh一个非常常见的坑是你在宿主机手动source了环境变量Operator或容器启动脚本里没source然后程序报错第一反应以为是CANN坏了。我建议把这行source写进所有昇腾相关服务比如后续的device-plugin如果有本地采集任务的systemd单元或启动脚本中一劳永逸。2.3 宿主机验证npu-smi info怎么看驱动和CANN装完后验证就一件事npu-smi info。正常输出会列出所有物理NPU包括芯片型号、健康状态、AI Core数量、HBM容量、当前温度和功耗。这可比CUDA生态的nvidia-smi信息量更细比如温度、功耗、AI Core利用率都有。如果这一步输出为空或者报“No devices found”就别往下走了后面装再多的容器组件都没意义。这时候排查路径是先看ls /dev/davinci*设备节点再看内核日志dmesg | grep -i davinci最后确认HDK里的固件版本和驱动是否匹配。宿主机有小概率出现芯片模式不是默认状态可以用npu-smi工具查看和设置芯片模式但一般情况下默认就能用。3. Ascend Docker Runtime容器侧“看得到”NPU的关键一环插一句话前面所有工作是让宿主机“认识”NPU但Kubernetes最小的调度单元是PodPod里的容器默认是隔离的看不到宿主机的/dev/davinci*。Ascend Docker Runtime就是解决“隔离与透传”这对矛盾的。3.1 Runtime的安装与Docker daemon配置昇腾的Ascend Docker Runtime安装包同样以.run方式发布解压后目录里通常有install.sh脚本执行即可完成大部分配置。脚本核心动作是注册一个名为ascend的runtime到Docker。安装完成后需要确认Docker daemon配置在/etc/docker/daemon.json里应该能看到类似这样的runtime配置{ runtimes: { ascend: { path: /usr/local/Ascend/Ascend-Docker-Runtime/ascend-docker-runtime, runtimeArgs: [] } } }配置完成后必须重启Docker守护进程systemctl restart docker。这一步漏掉的话后面docker run --runtimeascend会提示找不到runtime。验证Runtime是否生效最快的方法是直接在宿主机起一个测试容器docker run --rm --runtimeascend --device/dev/davinci0 \ -e ASCEND_VISIBLE_DEVICES0 \ -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ -v /usr/local/dcmi:/usr/local/dcmi \ -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \ ascendhub.huawei.com/public-ascend/ascend-mindspore:latest \ npu-smi info如果容器内能正常输出NPU信息说明Runtime的设备映射和驱动透传都是OK的。注意命令里我把宿主机驱动目录、dcmi目录和npu-smi工具都挂进了容器这些是容器内执行npu-smi的必要条件。3.2 ASCEND_VISIBLE_DEVICES与设备映射ASCEND_VISIBLE_DEVICES环境变量的用法类似NVIDIA的NVIDIA_VISIBLE_DEVICES指定容器可见的物理NPU编号比如0表示只映射第0张卡all表示映射所有卡。这个变量建议在Pod的env里显式声明不要依赖Runtime的默认值因为不同版本默认行为有差异。如果你在多卡环境下测试可以先不设这个变量看看Runtime是否把所有NPU都映射进来。正常情况下一张卡对应一个/dev/davinciN设备节点。假如容器里ls /dev/davinci*只看到一个设备而宿主机明明有四张卡优先检查ASCEND_VISIBLE_DEVICES有没有被设置成单卡。3.3 containerd场景只配Docker是不够的很多K8s集群的容器运行时是containerd而不是Docker。这时候即使你前面把Docker的runtime配置得再好Kubelet通过CRI创建的Pod也完全不会走Docker的runtime设备自然不会被注入。containerd需要额外在/etc/containerd/config.toml里注册runtime。华为官方文档对这块有明确说明一般是在[plugins.io.containerd.grpc.v1.cri.containerd.runtimes]下增加一个ascend配置段指定runtime二进制路径。我见过太多人只配置了Docker最后Pod起来后ls /dev/davinci0一片空白。所以部署前先确认集群的container runtime到底是什么kubectl get nodes -o wide # 查看CONTAINER-RUNTIME列确认是docker还是containerd如果是containerd严格按照官方提供的方式在config.toml配置ascend runtime并重启containerd。这里提个建议整个集群如果都是containerd环境日常调试时尽量用crictl而不是docker命令避免“Docker里正常K8s里不行”的魔幻局面。4. device-plugin让K8s调度器感知NPU资源链路走到这一步宿主机有驱动容器也有Runtime能把设备映射进去但Kubernetes依然不知道“这台机器上有几张NPU”。device-plugin就是专门干这个的它让调度器把NPU当成像CPU、内存一样的可调度资源。4.1 Device Plugin的工作机制与资源名昇腾官方开源的device-plugin部署在Gitee的ascend-device-plugin仓库本质是一个DaemonSet跑在每个NPU节点上。插件会扫描宿主机上可用的NPU设备然后向API Server上报自定义资源。资源名一般是huawei.com/Ascend910或huawei.com/Ascend310取决于芯片型号。调度器看到这类自定义资源后就会在调度Pod时做匹配。Pod的resources.limits里声明多少调度器就会把Pod绑定到资源足够的节点。device-plugin还会把设备信息和Pod的关联关系通过环境变量比如ASCEND_RT_VISIBLE_DEVICES取决于插件版本传递给容器。4.2 节点打标签与插件部署部署device-plugin前先给节点打上标签方便DaemonSet通过节点选择器只调度到NPU节点。昇腾文档里推荐用SetNodeLabelUpdater的方式自动打标签但手动打标签其实更快kubectl label node node-name acceleratorhuawei-Ascend910然后创建device-plugin的DaemonSet。官方的yaml里通常会挂载/etc/ascend配置文件并使用节点标签做选择器。apply之后查看Pod状态kubectl get pods -n kube-system | grep ascend如果DaemonSet的Pod是Running且READY说明插件已经成功注册NPU资源。验证方式很简单查看节点可分配资源kubectl describe node node-name里能看到huawei.com/Ascend910: 4类似字样。4.3 多卡与NPU切分的实践边界昇腾NPU在K8s里默认是整卡调度也就是一张物理卡一次性分配给一个Pod这点和NVIDIA默认的GPU整卡模式一致。如果你需要把一张大卡比如910B切成多个vNPU给不同Pod用那属于昇腾虚拟化方案的范围对CANN版本、固件、device-plugin配置都有额外要求不是开箱即用的。如果你当前集群里跑的是普通单卡训练任务整卡调度完全够用。切分场景下也要留意每个Pod里的ASCEND_VISIBLE_DEVICES是Runtime负责映射的但具体切出的卡索引由device-plugin上报的vNPU资源决定两者不能混淆。第一次做NPU调度验证时建议先申请卡数1、vNPU关闭的最小化配置把链路跑通再调大资源。5. NPU监控指标从npu-smi到Prometheus全链路接入K8s之后很多人会忽略监控但训练任务跑起来后利用率、温度、功耗这些指标就是头等大事。不说团队管理你自己排查一个训练变慢的问题没有指标数据就只能靠猜了。5.1 npu-smi和dcmi能拿到什么宿主机的npu-smi info可以实时查看NPU的AI Core利用率、HBM内存使用率、温度和功耗这是最直接的指挥信号。但它本质是命令行工具没法长期存储和可视化。昇腾的DCMIDevice Control Management Interface库则提供了一套更偏编程的接口很多监控Exporter就是基于DCMI采集指标再暴露成Prometheus格式。常见的指标包括AI Core利用率、HBM内存利用率、芯片温度、板卡功耗、PCIe带宽等。训练卡一般要重点关注利用率是否持续打满推理卡则更多看功耗和温度趋势。5.2 用Exporter暴露指标并接入Prometheus昇腾社区和官方都提供Prometheus Exporter部署形态通常也是DaemonSet。启动后会暴露一个HTTP端口提供metricsPrometheus配置里直接按节点发现或服务发现去抓取即可。抓取路径类似scrape_configs: - job_name: ascend-npu static_configs: - targets: [exporter-svc:9108] labels: instance: ascend-npu-exporter部署完可以先用curl验证一下metrics接口内容确认出现了类似ascend_npu_ai_core_usage_rate、ascend_npu_hbm_usage_rate、ascend_npu_temperature、ascend_npu_power这样的指标名再接入采集。注意Exporter需要挂载DCMI相关的库和设备节点权限要求挺高容器最好以特权模式或显式声明对应capabilities运行。5.3 监控面板与告警阈值参考我自己的实际习惯是接入Prometheus之后再配一个Grafana面板把每张卡的AI Core利用率、HBM利用率和温度放在一张图上。训练集跑起来后肉眼就能看出哪张卡是热点、哪张卡在偷懒。告警阈值方面我用的保守值参考如下指标告警阈值说明AI Core利用率持续15分钟低于20%大概率训练代码或数据集有瓶颈芯片温度 85℃超过这个值要检查风道和散热HBM利用率持续超过95%可能需要调batch size或模型并行策略板卡功耗接近标称最大功耗长时间贴着上限要留意电源冗余阈值得结合具体型号和业务调整910系列和310系列的功耗特征完全不同别机械照搬。不过思路是一致的先有指标再谈优化和告警。6. 全链路验证与高频踩坑排查最后把整个链路的验证方法和踩坑经验串一遍。这部分我想说的是真正部署升腾环境时遇到的每一个问题都要能快速定位到它是属于驱动层、Runtime层还是K8s调度层不然就是大海捞针。6.1 一条命令一个Pod走完验证链路标准验证路径我推荐按这个顺序走每一步都过一遍再往后可以省掉大量无效排错时间# 第1步宿主机Driver与固件 npu-smi info # 第2步宿主机CANN环境 source /usr/local/Ascend/ascend-toolkit/set_env.sh python3 -c import torch; import torch_npu; print(torch.npu.device_count()) # 第3步Docker Runtime透传 docker run --rm --runtimeascend \ -e ASCEND_VISIBLE_DEVICES0 \ ascend镜像 npu-smi info # 第4步K8s侧探测Pod cat EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: ascend-npu-test spec: nodeSelector: accelerator: huawei-Ascend910 containers: - name: ascend-test image: ascendhub.huawei.com/public-ascend/ascend-mindspore:latest command: [npu-smi, info] resources: limits: huawei.com/Ascend910: 1 EOF kubectl logs ascend-npu-test这个探针Pod如果能在日志中输出NPU信息说明从驱动到Runtime再到K8s资源绑定整条链路已经全部打通。下一步再去跑真实训练任务把环境变量和模型代码逐渐带进来。6.2 我遇到的高频报错与排查思路这里整理几个我实战中遇到、也比较有代表性的问题现象大概率原因处理方式容器内ls /dev/davinci0没有设备容器没使用ascend runtime或containerd未配置runtime检查daemon.json和containerd配置重启对应服务Pod一直Pending节点没有打NPU标签或device-plugin没上报资源kubectl describe node查看资源确认标签和DaemonSet状态device-plugin启动CrashLoopBackOff驱动版本和插件版本不匹配或设备节点权限不足看插件日志确认真实错误码对照版本兼容列表宿主机npu-smi正常容器内npu-smi报SO库缺失驱动或dcmi目录没有挂载进容器检查Runtime版本是否支持完整注入或手动补挂载模型初始化报E开头错误码CANN版本和驱动版本不匹配按官方兼容性表重新安装配套版本排查的逻辑不难先确认宿主机有没有问题再确认Runtime有没有生效最后看K8s调度和资源上报。用这个顺序基本能覆盖九成以上的场景。6.3 一些部署细节的个人建议最后分享几个我自己的习惯这几个小点看起来无关痛痒但实际部署中帮我省了非常多时间。第一整个部署过程的所有版本信息包括操作系统内核、HDK驱动版本、固件版本、CANN Toolkit版本、Ascend Docker Runtime版本、device-plugin版本全部记录到同一个表格里。昇腾这套软件栈对版本配套非常敏感任何一项升级都可能引起连锁反应。有了这张表排查时先对照一遍能排除一大片问题。第二集群里如果混用Docker和containerd建议你先把所有节点的容器运行时统一再接入NPU。混合环境下即使功能能用排查时也会因为“这台机器用Docker、那台用containerd”导致问题复现困难。第三如果集群规模不大可以先把device-plugin部署到一个节点上做完整验证再通过节点标签逐步扩大到全集群。不要一口气全量部署否则一个错误配置就会让整个集群的节点上报异常排查面瞬间扩大好几倍。昇腾NPU接入Kubernetes说难不难但每一步都必须走得扎实。驱动装完先验证宿主机Runtime配完先验证单容器device-plugin部署完先验证单节点调度层层打通之后再上生产任务我就这么干下来整套集群稳定性一直都不错。
返回列表