news 2026/10/16 3:18:14

昇腾NPU接入Volcano:从资源建模到调度高可用的实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
昇腾NPU接入Volcano:从资源建模到调度高可用的实战指南

去年年底,我在帮一个客户搭大模型训练平台,底层是十几台Atlas 800服务器,每台插了8张昇腾910 NPU。平台选型上,Kubernetes负责资源抽象,Volcano作为批处理调度器跑AI训练和推理任务。当时我天真地以为,只要把华为的Ascend Device Plugin装上,让节点上报出NPU资源,事情就结束了。结果正式提任务那天,作业全部卡在Queued状态,NPU节点却显示资源充足、无人认领。从那次开始,我才认真把华为Ascend与Volcano集成时涉及到的设备使能、资源建模、调度代码路径和高可用配置完整梳理了一遍。这篇文章就是那次实战的系统性复盘,适合正在搭建AI算力平台、想用Volcano管理昇腾异构算力的朋友参考。

1. 为什么要把 Ascend 接进 Volcano:从“设备可用了”到“调度器认得它”

1.1 症状:作业全部 Queued,NPU 资源却白白闲置

第一次看到这套组合的时候,我犯了一个很典型的错误:以为设备插件上报完资源,调度器就会自动用得起来。实际现象是,kubectl get node已经能看到huawei.com/Ascend910: 8,但Volcano Job提交后一直处于Pending状态,事件里报的是task group not ready。我手动把Pod模板拿出来kubectl describe,才看到更具体的提示:集群里所有节点都检查过了,但没有一个节点同时满足huawei.com/Ascend910的数量要求。

这时最先怀疑的是Device Plugin没上报对数量,于是挨个节点检查/var/lib/kubelet/device-plugins/kubelet_internal_checkpoint,发现资源数量都在。真正的问题出在Volcano调度器这一层:它不认识huawei.com/Ascend910这个资源名,也不知道它属于NPU这一类设备。对一个批处理调度器来说,如果资源无法被正确归类,后续的配额计算、优先级排序、节点打分全部会出偏差,最直接的表现就是“资源明明在眼前,调度器却绕着走”。

这类问题其实非常有代表性。昇腾的使能不是装好驱动就完事,而是要打通一条完整链路:硬件驱动、固件、容器运行时、Device Plugin、Kubernetes的Extended Resource,最后再到调度器的资源模型。只要中间一个环节对不上,硬件再好也白搭。

1.2 两个调度器的错位:Extended Resource 与真正的异构资源调度

Kubernetes默认的 kube-scheduler 并不是不能调度昇腾资源,Extended Resource 足够让它知道每个节点有几张卡,也可以按数量做基础过滤。但AI训练场景里,我们需要的远不止“数卡”:任务可能要成组调度、要保证多卡任务不被拆散、要按队列分账、要在一张卡损坏后自动把任务挪走。这些都是Volcano这类批处理调度器的核心能力。

Volcano对异构资源的处理方式,是在底层资源模型里把资源分成几大类:CPU、内存、GPU、NPU等。它不只是看“有多少”,还会按照 vendor 配置把这些资源归到对应的类型里。默认情况下,如果一个资源名不在vendor映射里,调度器可能把它当作普通扩展资源,也可能统一归到GPU类型里。这就带来两个问题:一是比例制队列计算额度时,NPU资源的权重不对;二是节点打分和访问亲和性判断时,无法体现昇腾卡的特性。

“系统使能”这四个字,在做昇腾与Volcano集成时,实际上应该理解成:驱动和Device Plugin解决了“设备能不能用”,vendor映射和调度插件解决的是“设备怎么分”。前者是硬件接入,后者是算力编排。二者缺一不可。理解了这一点,接下来所有的配置和代码走读就都有了方向。

2. 让 Volcano 识别昇腾资源:vendor 映射与资源模型

2.1 先确认节点上的资源名,再谈配置

做集成时第一件事不是改配置文件,而是跑到节点上,把真实的资源名确认下来。不同版本的Ascend Device Plugin上报的资源名并不完全一致,我在现场至少见过三种写法:huawei.com/Ascend910、ascend.com/mindx、ascend.com/ascend310p。这三种写法对应不同设备插件版本和硬件形态。

可以用这条命令确认:

kubectl describe node atlas800-01 | tail -20

重点关注Capacity和Allocatable两段,里面会明确列出当前节点已经上报的扩展资源名。常见的情况整理成了一张表:

资源名对应设备常见来源
huawei.com/Ascend910Ascend 910 训练卡华为 Ascend Device Plugin 5.x 早期版本
ascend.com/mindxAscend 310P / 910 通用资源MindX Device Plugin 及新版插件
ascend.com/ascend310pAscend 310P 推理卡Ascend 310P 专用 Device Plugin

这里要特别提醒一句:不要凭记忆写资源名,一定要以节点实际上报结果为准。同一个集群里如果混装了不同版本的Device Plugin,甚至可能出现同一批硬件、两种资源名的情况,这种情况在后面的避坑部分会详细展开。

2.2 vendor 配置:把资源分到 cpu、gpu、npu 三个桶里

确认资源名之后,Volcano侧需要做的核心配置是vendor映射。Volcano在调度配置文件里维护了一个资源分类逻辑,把各种资源名划归到对应类型。代码风格上类似这样:

kind: ConfigMap metadata: name: volcano-scheduler-configmap namespace: volcano-system data: volcano-scheduler.conf: | actions: "enqueue, allocate, backfill" tiers: - plugins: - name: priority - name: gang - name: conformance - name: proportion - plugins: - name: predicates - name: nodeorder vendor: - cpu: cpu - memory: cpu - huawei.com/Ascend910: npu - ascend.com/mindx: npu

这段配置里,cpu、memory是基础资源,不需要额外解释。重点在最后两行:把huawei.com/Ascend910和ascend.com/mindx都映射成npu类型。一旦归为npu类型,Volcano在做proportion队列额度计算、binpack节点打分时,都会把这类资源按NPU来处理,而不是当成普通标量。

不同Volcano版本的vendor字段位置可能有变化。早期版本放在插件参数里,高版本提到调度配置顶层,升级后记得检查原有配置是否被保留。如果vendor配置缺失,调度器也不是完全不能用昇腾卡,但会出现资源计量混乱、多卡任务亲和性差、队列配额不准等一系列隐性故障。

2.3 Queue、PodGroup、Job:三层模型怎么支撑算力分账

真正用Volcano跑昇腾任务时,通常会涉及三层概念:Queue是资源池,Job是业务作业,PodGroup是Job内部的调度单元。简单类比一下:Queue像是工厂里的车间配额,Job是一张生产工单,PodGroup是工单里承诺“必须同时开工”的一组工人。

一个典型的Volcano Job长这样:

apiVersion: batch.volcano.sh/v1alpha1 kind: Job metadata: name: bert-train namespace: default spec: schedulerName: volcano minAvailable: 2 queue: ai-training policies: - event: PodEvicted action: RestartJob tasks: - name: worker replicas: 2 template: spec: containers: - name: trainer image: ascend-hub:5000/bert-train:v1.0 resources: limits: huawei.com/Ascend910: 1

queue指定作业进入哪个队列,minAvailable表示这批任务至少要有多少个Pod能被同时调度,否则整个作业都不会启动。对昇腾多卡训练任务来说,这个字段特别关键:如果训练脚本本身就需要4卡并行,调度器却先只放行了2个Pod,剩下的2个Pod占着资源又跑不起来,整个队列都会被“死锁”掉。gang调度机制解决的就是这个问题,保证同类任务要么一起启动,要么一个都不启动。

policies里的PodEvicted -> RestartJob则是高可用的入口,等讲到故障自愈时再展开。这一层结构理解了,后面看调度代码路径会轻松很多。

3. 调度代码关键路径走读:从 Pod 请求到 NPU 绑定

3.1 扩展资源为什么只认 limits,不认 requests

昇腾卡这类设备资源在Kubernetes里属于Extended Resource,它有一个非常重要的限制:只能写在limits里,不能写在requests里。这不是Volcano规定的,而是Kubernetes扩展资源本身的规则。因为Device Plugin上报的是“可分配数量”,kubelet无法像CPU内存那样按request做静态配额,所以只允许Pod声明上限。

正确的写法是把卡数放到limits:

resources: limits: huawei.com/Ascend910: 1

如果图省事写成:

resources: requests: huawei.com/Ascend910: 1

调度阶段可能用配置文件绕过去,但kubelet在创建容器时会直接报错,原因是扩展资源不支持request声明。实际排障时,这个问题会表现为“调度已经分配了卡,Pod启动却失败”,特别容易误导人去查Device Plugin。

Volcano在解析Pod资源时,会把容器limits里的昇腾卡数量提取出来,转换成Task的ResourceRequest。这一步完成后,调度器内部才有了一份统一的数据结构,用来做后续的Filter和Score。如果这里的数据源没有统一,等于给后面所有逻辑埋雷。

3.2 Filter 阶段:调度器如何判断节点能不能承载任务

Volcano调度器在allocate阶段,会对每个待调度Pod执行一系列filter插件,其中核心是资源判断:当前节点的空闲资源是否满足Task的Request。我在读Volcano v1.9源码时,把这条路径简化成了下面这个思路:

// 简化自 volcano.sh/scheduler/api/resource_info.go 中 Resource.Match func (r *Resource) Match(res *Resource) bool { if res == nil { return true } for name, quantity := range res.Milli() { if r.Get(name) < quantity { return false } } return true }

res.Milli()会遍历Task请求的所有资源维度。如果节点空闲的huawei.com/Ascend910数量不小于Task请求的卡数,这个维度就通过。表面上看,这个逻辑即使不配置vendor也能正常工作。但问题在于,Volcano内部还有一套资源维度归一化的逻辑,它会根据vendor把资源归类。如果vendor缺失,huawei.com/Ascend910可能被当成未知资源类型,进而在proportion插件计算队列已使用额度时漏掉,导致一个队列的NPU配额统计失真。

所以Filter这一段,真正的关键不在“数量够不够”的比较,而在于“同一类资源是否被统一建模”。资源名不统一或者vendor缺失,filter也许不会直接报错,但它会严重干扰队列层面的容量判断,最终表现为作业排队时间异常长。

3.3 Score 阶段:binpack 如何把多卡任务压到尽量少的节点

Filter通过之后,节点会进入打分阶段。对昇腾任务来说,默认的spread策略并不理想。多卡训练任务如果被拆到多个节点,跨机通信开销会显著上升。更合理的策略是binpack:尽量把任务集中到少数节点,让空闲资源聚合成更大的“卡块”。

Volcano里的binpack插件会在nodeorder阶段起作用,它会计算节点已经分配的资源和剩余资源的关系,剩余资源越少、碎片越小的节点得分越高。实际配置时可以给NPU这类资源更高的权重:

- name: nodeorder arguments: plugins: - name: binpack arguments: binpack.weight: 0.5

binpack.weight控制的是整体打包倾向,数值越大调度越“紧凑”。这个参数不能盲目调大,否则会出现一个物理节点上堆了太多任务,导致卡间通信争抢HBM带宽的问题。我在实测集群里通常会把权重调到0.3到0.6之间,具体数值还要结合单卡显存占用和业务卡间通信模式来定。

打分阶段还有一个值得注意的细节:binpack插件对NPU资源维度的感知,依赖vendor把资源正确归类为npu。如果你只配置了CPU和内存,没有显式声明昇腾资源类型,binpack里NPU维度的权重就计算不上来,最终节点排序依旧和随机差不多。

4. 高可用闭环:设备故障检测与任务自愈

4.1 昇腾节点常见的故障模式

生产环境里,昇腾服务器不是只有宕机才算故障。我见过很多次“机器活着、卡却没法用”的情况,这类问题最容易引发调度层混乱。整理一下常见的几种故障模式:

故障类型现象Kubernetes 里的表现
单卡AI Core错误训练进程报错、设备PID失效Device Plugin 将故障卡从可分配列表移除
驱动或固件异常dmesg 报 Npu 相关错误插件健康检查失败,节点可能变成 NotReady
Device Plugin 崩溃无法上报资源,kubelet 心跳异常节点资源数量消失,新任务无法调度
HBM 温度过高npu-smi 显示温度超限设备降频或不可用,训练性能骤降

这些故障如果只靠监控页面人工处理,运维压力会非常大。高可用设计要解决的,就是把“卡故障”自动转换成“调度事件”,再把“调度事件”转换成“任务重试”。

4.2 Device Plugin 健康检查与驱逐机制

华为的Ascend Device Plugin会周期性向昇腾管理接口查询设备健康状态,并通过ListAndWatch机制把状态同步给kubelet。它上报给kubelet的健康状态一旦变化,kubelet会更新节点的可分配资源数量。如果有一张卡被判定不健康,这张卡会从节点的Allocatable列表中移除。

已经在运行这张卡上的Pod会怎样?取决于故障类型。如果是设备热度异常或者驱动瞬时抖动,pod运行中的容器可能仍然存在,但新的Pod已经无法再申请到这张卡。如果是卡彻底不可用,kubelet会杀掉相关容器,Pod进入Failed或重启状态。

这里最容易被忽略的是:被kubelet杀掉Pod之后,如果这个Pod是由Volcano Job创建的,Volcano需要感知到这个事件,并决定要不要重建、什么时候重建、还能不能重试。如果不配置任何策略,Job有可能一直停在失败状态,资源也会在队列里被白白扣着。

4.3 Volcano Job 的重调度策略配置

Volcano原生的高可用主要依托Job的policies机制。事件对应动作,动作决定调度器下一步做什么。一个比较完整的配置是这样的:

spec: schedulerName: volcano minAvailable: 2 maxRetry: 3 policies: - event: PodEvicted action: RestartJob - event: PodFailed action: RestartJob - event: TaskCompleted action: CompleteJob

maxRetry限制了Job最多重试几次,防止一个永远无法启动的任务反复消耗调度资源和队列额度。PodEvicted对应节点驱逐,PodFailed对应容器启动失败或运行失败。这两类事件触发时,Volcano会把相关Pod从PodGroup中清理出去,重新构建Task副本,再次进入调度队列。

这里还要注意一个联动点:Job重试时,原Pod占用的资源需要先释放,否则队列额度不够,重试任务又会卡住。Volcano在清理Pod后会更新队列的已用资源,但如果Pod是ForceDelete,资源释放可能存在时间差。我在实际操作中会给Pod设置较短的terminationGracePeriodSeconds,比如30秒,避免故障卡上的容器一直收不到SIGTERM,拖慢整个重调度闭环。

对于使用Device Plugin的场景,插件健康上报和Volcano的pod事件是异步的。也就是说,从卡故障到Job重启,中间可能会间隔几十秒。这个时间延迟可以接受,但监控上一定要能区分“调度器正常、底层卡故障”和“调度器故障”,否则告警会被淹没。

5. 实测案例:昇腾集群分配率从 62% 到 92% 的调度调优

5.1 测试环境与负载类型

前面那套方案验证稳定后,我在一套测试集群上做过一轮系统的调度调优。集群一共40个节点,其中32个是Ascend 910A节点,每节点8卡,合计256张昇腾卡,另外8个是CPU节点。Volcano版本是v1.8,Ascend Device Plugin版本是5.0.RC2,Kubernetes版本1.24。

负载主要分三类:BERT大规模预训练、GPT类模型微调、CV检测训练。单任务需要的卡数从2卡到8卡不等,大部分任务要求8卡。整个集群分成两个队列:online-train占队列容量60%,offline-tune占40%。最初一段时间,集群的平均NPU分配率只有62%左右,很多节点明明有卡,却因为任务没被正确调度而空转。

5.2 三步调整,解决调度空转

第一步,统一资源名。把所有昇腾节点的Device Plugin统一到同一个版本,确认资源名统一为huawei.com/Ascend910,并在Volcano配置中显式映射为npu类型。这一步做完,调度器对资源类型的识别就稳定了。

第二步,开启gang和binpack。给多卡任务设置合理的minAvailable值,让8卡任务必须凑齐8个Pod才启动。同时在nodeorder插件中开启binpack,把昇腾卡维度的打包权重调到0.4。这一步显著减少了跨节点任务的数量。

第三步,调整队列容量和优先级。online-train队列允许在offline-tune空闲时借用资源,但设置了一个硬阈值,保证离线任务不会被无限饿死。配合proportion插件,队列之间的额度波动明显减小。

配置上大概是这样:

queue: - name: online-train capability: 153 # 60% of 256 reclaimable: true - name: offline-tune capability: 102 # 40% of 256 reclaimable: false

5.3 前后对比与观察

调整跑了七天后,我拉了一次调度数据:

指标调整前调整后
NPU 平均分配率62%92%
任务排队时间中位数8.2 分钟3.5 分钟
跨节点任务占比45%18%
节点碎片率(空闲少于4卡的节点)31%11%

binpack生效后,跨节点任务占比大幅下降,卡间通信效率明显好转,训练任务的平均吞吐也提升了大约17%。代价是单次调度耗时从几十毫秒涨到一两百毫秒,这个量级对批处理任务完全可接受。如果集群规模继续扩大,可以考虑给Volcano开调度结果缓存,减少重复计算。

有一点需要说明:以上数字是这一轮测试集群的实测结果,不代表所有昇腾集群都能复现,因为调度效率和业务负载形态强相关。但调整方向是通用的:资源名统一、vendor映射完整、gang与binpack合理组合,这三条基本能让大多数昇腾集群的分配率上一个大台阶。

6. 三个最容易翻车的坑

6.1 资源名不统一导致“同一批卡,两种命”

很多昇腾集群不是一次性建成的,早期用老版本Device Plugin上报了huawei.com/Ascend910,后面扩容节点用了MindX插件上报ascend.com/mindx。如果你在Volcano vendor里只映射了其中一种,那么另一种资源名的节点就会在调度打分和队列额度计算中被漏掉。

排查方法很简单:写一个小脚本,把每个节点的Capacity全部打印出来,按资源名分组,看看有没有同一个物理型号却上报了多个资源名的情况。出现这种情况,优先统一Device Plugin版本,而不是在Volcano配置里堆一堆别名。堆别名只能让问题隐藏,不能解决问题。

6.2 忘填 schedulerName,导致 Pod 被默认调度器接管

Volcano Job创建出的Pod,如果Template里没有显式设置schedulerName: volcano,默认会由kube-scheduler调度。你可能觉得反正提交的是Volcano Job,Pod一定会进Volcano的视野。实际上Volcano确实能看到这些Pod,但它只处理指定调度器的Pod,默认调度器则会用自己的逻辑分配资源。结果两个调度器同时面对同一批空闲资源,一个按Volcano队列规则排队,一个按kube-scheduler默认策略调度,很容易出现端口抢占、资源超卖。

规避办法是在Volcano Job的Task模板里显式声明:

spec: schedulerName: volcano

如果团队里有很多人直接提交裸Pod,最好用admission webhook统一注入schedulerName,从入口拦掉这类错误。

6.3 设备插件误报导致的“幽灵驱逐”

Device Plugin的健康检查也不是永远可靠。昇腾卡在HBM温度瞬时波动、驱动升级、固件刷新的时候,健康检查可能误报失败。插件一旦上报设备不健康,kubelet会把这个设备从可分配列表里摘掉,正在运行的Pod也可能被驱逐。但过几秒设备恢复正常,于是Pod被反复重建,任务进度全部被打断。

这种问题不容易从K8s层面直接看出来,因为事件里只会记录Pod被Evicted。需要去Ascend Device Plugin的日志里找健康检查记录,同时看/usr/local/Ascend/driver下的运行日志,判断是误报还是真实故障。

应对措施有两个方向:一是适当调大健康检查的容忍周期,让设备连续N次检查失败才判定不可用;二是在Volcano侧对PodEvicted事件做延迟处理,比如设置较长的基础重试间隔,避免设备抖动瞬间任务立刻重启。生产环境里,我会更倾向于后者,因为硬件故障毕竟是小概率事件,而驱动升级和固件刷新的频率要高出不少。

整套昇腾与Volcano的集成方案跑稳之后,我最深的一个体会是:所谓系统使能,远不是把驱动和Device Plugin装好就算数,调度器层面的资源建模如果不跟上,硬件再好也体现不出来。把资源名、vendor、健康检查这条链路统一规范后,后续所有平台功能才有讨论基础。如果大家手头也在做昇腾集群,建议先从一个小队列做起,把Ascend卡的资源名和Volcano vendor关系验证清楚,再上大规模。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 3:17:51

2024年CSP-J复赛真题及题解: T4 接龙

2024年CSP-J复赛真题及题解: T4 接龙 题目描述 在玩惯了成语接龙之后,小 J 和他的朋友们发明了一个新的接龙规则。 总共有 n n n 个人参与这个接龙游戏,第 i i i 个人会获得一个整数序列 S i S_i

作者头像 李华
网站建设 2026/10/16 3:17:02

时空也只是意识| 赵杰 | 量子意识(第二卷)

作者&#xff1a;赵杰清华大学硕士、微美全息云科技&#xff08;NASDAQ&#xff1a;WIMI&#xff09;董事长、微算法科技&#xff08;NASDAQ&#xff1a;MLGO&#xff09;董事长、育杰奖学金创始人按人类意识的认知逻辑&#xff0c;宇宙空间始终与时间绑定成 “动态整体”&…

作者头像 李华
网站建设 2026/10/16 3:16:54

华工科技领投GEO赛道后,厦门企业如何核验服务商真实交付能力

一笔融资背后的赛道信号10月13日&#xff0c;深度云海宣布完成千万级天使轮融资&#xff0c;领投方是A股上市公司华工科技。这是产业资本首次领投GEO赛道。华工科技投资总监李钰的判断值得留意&#xff1a;GEO已从监测工具进化为优化工具&#xff0c;长期将走向可自动执行的Age…

作者头像 李华
网站建设 2026/10/16 3:14:29

Odoo模块继承核心套路:模型、视图、方法重写全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 3:14:22

惠构思 AI|数据分析不是算数字,教你写出导师认可的实证章节

✨阅读引导&#xff1a;很多同学误以为数据分析就是跑软件导出表格&#xff0c;把一堆统计结果粘贴到论文就完成任务。导师真正想看的&#xff0c;是数据如何支撑你的研究假设&#xff0c;发现了哪些规律。不少人的实证章节&#xff0c;只有表格堆砌&#xff0c;缺少逻辑解读。…

作者头像 李华