ARTICLE DETAIL

资讯详情

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

HPC资源调度实战:从SLURM选型到GPU共享与回填调优

HPC资源调度实战:从SLURM选型到GPU共享与回填调优 1. 先说清楚HPC资源调度到底在调度什么以及为什么难“集群80%的卡都在吃灰跑任务却要排队一周。”这句话我在不少支撑部门都听过。一边是资源闲置一边是作业排队长到用户骂人问题十有八九出在资源调度上。高性能计算资源调度简单说就是把计算节点、GPU、CPU核心、内存这些硬件资源按任务的需求和时间窗口合理分配给提交上来的作业。它解决的核心问题是在同一时刻一批人抢同一批机器谁先跑、跑多少、用什么策略跑既不能饿死小众任务也不能让大作业霸占整台机器。我最初接触这件事时也以为调度就是维护一个排队系统——先来后到跑完一个再启动下一个。真上手才发现事情远没有那么简单不同作业的时长差异极大有的跑十分钟就结束有的要挂着跑两周有的作业对内存带宽敏感有的对GPU算力敏感有的用户要的是“死等资源”的可靠性有的只要能抢到碎片时间验证代码就行。你还要考虑公平性、优先级、资源利用率、容错恢复……这套调度系统做得好不好直接决定了整个集群的吞吐量和用户体验。这篇内容就是把我做HPC资源调度这些年积累的东西整理成文从调度器选型、分区设计、QoS限制到绑核、GPU共享、回填调优再到AI时代新负载带来的变化。适合两类人来读一类是刚接手集群管理、被slurm.conf和作业排队搞得焦头烂额的运维工程师另一类是在集群上跑任务、但总搞不清楚“为什么我的作业排不上”的算法工程师。你会对整个调度体系的运作逻辑有一个完整认知也能拿到可以直接落地的配置思路和排障方法。2. 调度器选型判断SLURM、PBS与LSF的真实差距2.1 三大主流调度器的核心差别调度器是HPC集群的“大脑”。市面上的选择主要就三款SLURM、PBS系列包括Torque/OpenPBS/PBS Pro和IBM Spectrum LSF。说句实话每款都有人用但体验差异相当大。SLURM现在是开源领域的事实标准绝大多数学术超算中心和新搭建的AI集群都选它。原因很直接开源免费、社区活跃、文档齐全API设计对二次开发友好。它的架构由slurmctld控制守护进程、slurmd节点守护进程和slurmdbd记账数据库守护进程组成作业状态管理和资源拓扑感知做得非常细尤其对NUMA架构、GPU绑定这类细节支持得特别好。对我这种动手运维的人来说SLURM最舒服的一点是配置文件逻辑清楚排错时看日志、看状态命令就能定位问题。PBS的历史更长尤其在传统制造业、生命科学领域有一批存量用户。语法层面和SLURM有不少相似之处但如果要在今天的AI集群上重头搭建我不太推荐老版本Torque——它的问题在于对新硬件的感知能力弱GPU调度是后补的MIG支持更是很多人自己打补丁。PBS Pro的商业版做得不错但企业版的价格和授权模式已经劝退了不少预算有限的团队。LSF是商业调度器的典型代表很多大企业生产环境在用。它的优势在于管理界面成熟、配套工具链完整、多集群联邦调度能力非常强政府企业的IT团队往往很看重这些“看得见”的后台功能。但它同样有商业软件的毛病闭源、按core计费、出了问题只能提工单等回复而且对现代容器化工作负载的支持节奏偏慢。2.2 选型时的判断维度与我的实际结论如果你现在面临选型我给一套非常实用的判断维度照着打分就行预算开源SLURM基本零成本但需要自己投入人力学习维护商业方案省人力但费钱。熟悉度团队里有人熟哪套就优先用哪套重新学习调度器的隐性成本往往比想象中大得多。硬件类型新集群优先看调度器对GPU、MIG、NUMA、容器化的原生支持程度SLURM目前领先。联邦扩展未来如果要把多机房资源统一调度LSF的商业联邦方案成熟度较高但SLURM也能通过 federation 模式实现。定制需求AI团队常有自定义调度策略、感知网络拓扑的需求开源方案能直接改商业方案要等厂商排期。我自己的结论很明确2025年以后新建的集群没有特殊理由直接选SLURM。这不是因为SLURM完美而是它在开源生态、硬件适配深度和社区发展速度上形成的合力已经让其他选项难以追赶。现在很多GPU厂商官方给的调度层参考方案就是SLURM这一点已经说明了趋势。做一个形象的类比选择调度器很像选择开源操作系统——有人用稳定胜出的老牌发行版有人用持续迭代的社区发行版。如果你要经常改内核参数、装新硬件驱动、加自定义模块那社区活跃、源码公开的方案永远是最省心的。3. 分区、QoS与抢占把一台机器变成井然有序的“超级工厂”3.1 分区设计算力、GPU、高内存与短任务各归其位调度器选定之后第一个核心设计决策就是分区partition/queue。分区不是随便分几组就完事它决定了资源怎么被切分、哪些任务能去哪里、优先级怎么流转。我见过不少集群没做分区所有机器混在一起所有作业排同一队列。结果大内存任务把普通计算节点占满了8卡GPU任务被零散的1卡作业堵住谁都跑不爽。一个成熟的分区设计至少要覆盖这几种类型计算分区compute通用CPU节点给绝大多数普通作业用。高内存分区highmem内存特别大的节点给基因组组装、大数据处理这类一个进程就要吞几百GB内存的任务。GPU分区gpu或gpu-a100/gpu-a800按GPU型号进一步细分避免不同代际的卡混在一个分区里导致任务调度错乱。短任务分区short限制最长运行时间比如2小时专门给调试、交互式任务用用户能快速恢复排队。独占分区exclusive一般保留给关键生产作业节点独占避免资源争抢。我实际维护的一个集群把GPU节点按型号分成了gpu-a100和gpu-rtx4090两个分区又在gpu-a100下按QoS区分了debug30分钟batch7天和long14天。这样做最直观的好处是跑调试任务的人不用等大批量训练作业的空隙小请求能迅速在碎片资源上跑起来而正规的训练作业则有明确的最大运行时限调度器可以按这个上限做更激进的回填安排。分区是否需要设定的核心原则是资源性质差异越大越应该分开。调度器最怕的是把一个“512GB内存的请求”放到“64GB内存节点”的分区里排队等到天荒地老也不知道其实另一个分区有资源分区能帮助调度器快速锁定匹配的节点子集减少等待时间。3.2 QoS与资源限制别让一个任务拖垮整个集群分区管的是“去哪”QoS管的则是“能占多少”。QoS全称Quality of Service在调度语境下就是一组资源上限和优先级组合。不设置QoS的集群等于一个人可以把自己的作业优先级拉到无限高占满所有资源谁都没脾气。我在配置QoS时最看重几个参数MaxWall最大运行时长防止某些作业无限挂机常见设置是short分区MaxWall02:00:002小时batch168:00:007天。MaxTRES最大资源使用量限制单用户在同一时刻最多占多少个CPU核心、多少GPU卡。比如限定单个用户最多2张A100防止有人一次提交几百个单卡作业把集群资源全部占死。GrpTRES组资源配额按项目或课题组设置整体资源上限保证多个团队间的基本公平。Priority数字越高作业越优先。这里有给新手的建议千万不要只给约束不给可视化反馈。用户最烦“我的作业为什么又排队了”如果你能用sacct或qstat告诉他们“你们组当前已经用了3张A100QoS上限是4张所以第5个作业在排队”他们马上就能理解和接受。很多时候用户产生烂漫天飞的作业请求纯粹是因为他们根本不知道池子里有多挤、上限是多少。把规则透明化比任何强制限制都有效。3.3 抢占与回填用“插队”和“填空”换来整体效率调度不只是排队还有个很关键的技术叫回填backfill。基本原理是当队首的大作业因为资源不足无法启动时后面的小作业如果能在大作业预计的起始时间之前跑完并且使用的资源和大作业不冲突就可以先启动。这个功能看起来简单却是实际提升集群利用率的头号功臣。举例来说一个需要64张A100的大训练作业卡在队首后面排队的是十几个只需要1张卡、跑1小时的小作业。如果没有回填这些GPU全都空转到半夜打开了回填调度器会计算大作业什么时候能拿到资源然后把小作业合适地插入到前面的等待窗口。实际调优中小作业占空档后集群整体利用率能从不到50%提升到80%以上。抢占preemption则是优先级冲突时的手段。高优先级作业进来时允许调度器杀掉或挂起低优先级作业把资源让出来。这项功能对生产环境很关键但它是一把双刃剑——不合理的抢占策略会让低优先级作业反复被kill消耗大量算力在检查点和重启上。我常见的做法是只允许QoS为high的作业执行抢占并且被抢占作业自动保留排队位置。SLURM中的PreemptModeOFF是不启用PreemptModeREQUEUE是排挤后重新排队PreemptModeCANCEL则是直接取消具体选哪种要看作业本身是否支持断点续算。需要特别提醒回填和抢占都对调度器的“预估能力”有依赖。如果用户提交作业时不填写实际运行时长调度器只能按最大值估算回填窗口就会变得很保守。所以我在集群的使用规范里明确要求所有作业必须通过--time参数提交合理运行时长上限。仅这一项改进就能明显提升回填命中率。4. 作业级别的优化细节从CPU绑核到GPU共享的实战心得4.1 NUMA与CPU绑核性能能差出一倍很多用户觉得调度只负责把作业分配到节点上剩下的“算得快不快”是自己的代码问题。其实不然调度器给的资源粒度、亲和性设置对作业性能的影响非常大。最典型的坑就是NUMA非统一内存访问架构。现代服务器都是多路CPU每一路/每个NUMA节点都有自己直连的内存和PCIe设备。一个作业如果申请了16个核心但调度器没有给它绑定到同一个或者相邻的NUMA节点上运行时内存访问就有可能跨NUMA跳转延迟和带宽损失巨大。我曾经实测过同一份分子动力学模拟代码在同样16核的情况下跨NUMA调度比亲和调度慢了接近60%。这就是为什么SLURM中--cpu-bind绑核以及--mem在SLURM新版中是--mem-per-cpu或节点级内存请求策略的选择很重要。SLURM的--cpu-bindcores可以保证作业分配到指定数量的CPU核心--cpu-freq还可以控制CPU频率适合对能效有要求的批次作业。还有更细的技巧让每个MPI进程绑定到连续的核心但不超卖物理核打开SlurmctldParametersenable_numa_support会让调度器感知节点NUMA拓扑作业的性能会更稳定。配一个最常用的srun示例srun -N 2 --ntasks-per-node16 \ --cpu-bindcores --mem-per-cpu4G \ ./your_mpi_app这条命令指定2个节点、每节点16个进程每个进程绑定1个物理核配4GB内存。不要小看这个绑定参数对MPI类应用来说跑同样规模的作业绑核版和非绑核版的执行时间能差出肉眼可见的差距。你在做性能测试、性能比较报告的时候如果不说明绑核策略出来的数字就没有可比性。这个细节很多做AI训练的同学没意识到。4.2 GPU资源的申请与共享MIG、时间切片与分批调度GPU资源的申请是这几年HPC调度的重点。早年间看到集群上有人申请2张卡调度器只是保证给他物理GPU性能和隔离都由运行时的驱动去管。现在不一样了任务对GPU的需求粒度越来越细显存、带宽、算力都成了需要调度的资源。NVIDIA的MIGMulti-Instance GPU模式允许把一块A100物理卡切分成多个实例每个实例拥有独立的显存和计算核心分区。这在调度层的意义非常大——以前一个只占2GB显存的预处理任务也得独占80GB显卡现在能切成七份或更多相当于在不变硬件的前提下把GPU“变多了”。SLURM从20.11版本开始原生支持MIG通过GresTypesgpu,mps和针对MIG的配置可以让用户按“GPU实例”粒度提交作业。我实测过在A100上启用MIG后同一批推理服务可以压到原来的三分之一物理卡成本节省非常明显。GPU的时间切片则是另一种共享思路适合延迟容忍度高的推理和训练任务。相比MIG的硬件隔离时间切片是软件层面的分时复用多个任务轮流用同一张GPU。默认情况下驱动已经支持时间切片但调度器层面需要配置好GPU的并行度上限。否则极端情况下会出现多个作业同时抢同一张卡显存溢出直接hang住。反过来如果所有作业都用独占GPU粒度一张卡只能跑一个任务而任务只用了这张卡不到一半的算力这样的浪费在AI集群中也太常见了。关于GPU调度我这几年总结了一条建议别追求单一粒度吃遍天。把集群的GPU分区拆成两个层级——显存密集型任务走MIG独占实例吞吐密集型任务走时间切片共享或整卡独占。计划提交的任务能用MIG实例就跑MIG不可调度的紧急需求至少保证共享策略下不互相挤爆显存。这个“混合粒度”策略是我见过的大多数AI集群在资源利用率上拉开差距的关键。5. 调度策略的调优与运维复盘我的真实踩坑记录5.1 调度间隔与回填窗口实时性不是越大越好调度器通常不是“每次有作业提交就立刻调度一次”而是定期执行调度循环。SLURM中有一个控制这个频率的参数SchedulerTypesched/builtin和SchedulerParameters中的defer_batch等选项实际影响调度延迟的核心参数是MaxBatchTime、SchedulerInterval等。调度间隔太短调度器频繁扫描作业和节点CPU开销大间隔太长作业启动延迟明显用户在小作业上能明显感受到“卡”。我调过的一套参数把调度间隔控制在5到10秒同时在回填窗口方面默认的“只看队首作业”的保守策略改成了bf_window3600允许回填向前看1小时的小作业。这样既不会让调度循环过度消耗计算资源小作业的启动延迟也基本在十几秒以内。如果你发现集群上小作业排队时间动不动就几分钟很可能就是调度间隔设得过大去查一下调度日志中的每轮调度耗时和作业启动时间差就知道了。一个常见的误区是把回填窗口调得越大资源利用率越高。事实并非如此窗口过大调度器会为了预测未来数小时的资源释放不断进行更复杂的仿真计算调度循环耗时急剧上升同时还要大量用户的作业时长估计支撑——用户不那么写回填预测就会很差。回填策略的窗口长短要和作业时长、估算准确度匹配而不是盲目拉长参数。5.2 作业依赖与任务数组把调度器用到极致很多用户提交作业时只知道salloc和sbatch并不知道调度器还提供了非常强大的作业间编排能力。合理使用依赖关系dependency和任务数组job array能极大减少人工介入、提高调度效率。作业依赖最常见的场景是流水线先跑预处理完成后才跑训练再完成后才做评估。使用SLURM的依赖语法# 提交预处理作业 PRE_JOB$(sbatch --parsable preprocess.slurm) # 等待预处理完成后提交训练作业 sbatch --dependencyafterok:${PRE_JOB} train.slurm这样就可以让调度器自动按依赖关系依次启动作业而不是用户每步都手动盯着。依赖关系还可以是afterany无论前一作业成功失败都执行和afternotok前一作业失败才执行后者很适合做失败重跑逻辑。任务数组则是一个脚本批量跑上千个参数的推荐提交方式。假设你要跑1000组超参数实验#!/bin/bash #SBATCH --array1-1000%20 #SBATCH -p gpu-a100 python run_experiment.py --config config_${SLURM_ARRAY_TASK_ID}.yaml其中%20表示同一时刻最多并行20个任务防止超大规模数组瞬间把资源池打爆。这个功能看起来简单却能让代码侧的实验管理大幅简化不再需要自己在程序里维护一个复杂的工作队列调度器天然就是分布式工作队列。我踩过的一个坑是任务数组数量设得太大但不加%限制结果1024个数组任务一次性涌入每个作业启动都有资源分配、脚本解析、日志文件IO的开销调度器和共享文件系统瞬间被压到高负载。后来我在统一提交模板里强制加入并发上限这个问题再没出现过。5.3 从调度日志看集群健康值得关注的几个指标调度系统的日常运维不能光等用户投诉。学会从日志和数据里提前辨识问题是区分“救火型运维”和“体系建设型运维”的分界线。我每周都会固定看这几个维度的数据排队长度与等待时间分布如果排队作业数量持续上升、中位等待时间超过1小时说明资源供给或调度策略需要调整。节点利用率通过sreport查看CPU、GPU的总体利用率和空闲率。利用率太高但排队很长说明资源不够利用率很低但排队也很长基本就是调度策略有问题——资源碎片化、QoS限制过死或回填不生效。作业失败率可以用sacct -j jobid --formatState,ExitCode查看作业退出状态批量统计失败率。一个平时失败率5%的集群突然涨到20%大概率是软件环境、数据或节点硬件出了问题。调度器性能数据SLURM的sdiag命令能看到最近调度循环的耗时。如果调度循环平均耗时持续大于调度间隔说明调度器已经过载需要考虑优化配置或升级控制节点。我自己遇到的印象最深的一次故障集群突然出现大量作业等待启动但节点利用率和空闲CPU都显示异常平淡。用sdiag一看调度循环平均耗时竟然要40多秒原因是大量超短作业频繁提交调度器每轮都要重新扫描所有节点加上我当时还开启了复杂的资源权重计算。最后是给加上SchedulerParametersbf_continue和分组调度后调度循环耗时掉到5秒以内。这类问题不盯调度日志是真的发现不了。6. AI时代的调度新变化GPU弹性、容器化与分布式感知6.1 GPU共享与弹性调度新工作负载带来新思路传统HPC作业高度固定申请了资源跑完走人。但AI训练与推理负载没那么规矩——训练需要长期占用GPU但训练过程中GPU利用率经常波动推理服务则需要反复扩缩容流量高低潮明显。这对资源调度提出了“弹性”诉求。弹性的第一层是作业本身能够动态调整资源。SLURM在较新版本中引入了作业尺寸动态调整的能力允许作业运行中通过scontrol update job请求增加或释放GPU。我配合Singularity容器跑过一版训练框架的自适应扩缩容训练脚本定期监测GPU利用率发现利用率过低就主动释放几卡数据阻塞时反而需要临时加卡也能通过API即时扩容就像云上的自动伸缩组一样。弹性的第二层是推理服务的动态调度。现在的企业AI平台往往是在Kubernetes之上做推理服务编排而训练作业又跑在SLURM上两条路径并存。我团队当前的方案是训练类业务留在SLURM推理类业务交给K8s中间通过一套共享的GPU资源池做配额映射。SLURM提供GPU状态和分配查询APIK8s侧通过自定义调度器实时感知闲置GPU把推理容器调度过去。这样做能在不大改现有调度体系的前提下实现GPU在训练和推理之间的动态漂移。6.2 调度器与容器化平台的分工协作关于容器和HPC调度器的关系很多刚接触的人会搞混。容器Singularity/Apptainer、Docker/Podman解决的是“环境打包和隔离”问题调度器解决的是“资源分配和排队”问题。它们不是替代关系而是配合关系。实践中SLURM经常负责提供“裸机资源作业生命周期管理”容器则在作业内部为用户提供可复现的环境。sbatch提交作业时可以指定--container-image参数SLURM含异构容器支持的版本作业启动时自动拉取镜像并运行日志可以按容器内路径映射体验已经接近Kubernetes的便利性但底层的作业调度和优先级控制还是SLURM自己说了算。我在AI集群搭建中越来越深的感受是单纯“能跑起来”已经不算本事基于资损感知把作业调度到最匹配它的节点、在故障发生时自动转移、在节点碎片化时自动整合才是今天调度系统的核心价值。这也意味着调度策略本身需要高配置化和策略化——把常见的调度需求做成可选项让运维不用频繁改代码。AI平台的Multi-cluster调度即用一套控制面管理多个物理集群也是趋势。用户的开发环境和生产环境分别在不同集群调度器需要感知每个集群的实时负载和资源特征把作业机动分配。SLURM的federation模式或者第三方调度平台都能实现不过这类多集群联邦调度的复杂度会指数级上升建议先从单集群把调度策略打磨到完善再考虑横向扩展。最后分享一个小技巧无论是刚开始做资源调度还是已经运行多年的集群都不要忽略“调度策略的版本化管理”。把分区、QoS、优先级策略和回填参数抽象成可回滚的配置文件和文档每次调整都记录变更原因和效果数据。这个习惯帮我节省了无数次“改回来”和“为什么现在这么卡”的排查时间。资源调度没有绝对正确的配置只有不断试出来的最合适方案。
返回列表