资讯动态

LSF多队列抢占与公平调度实战:从bsub到队列配置的平衡术

发布时间:2026/10/2 17:42:05 来源:尧图企业网站定制
LSF在数据中心和高性能计算集群里的地位不需要我多说。真正让运维头疼的从来不是bsub怎么提交作业而是集群一忙起来生产任务被长周期任务堵死老板催结果业务方互相甩锅这时候才意识到多队列的资源抢占和公平调度不是搭几套队列就能糊弄过去的。我接手过的集群不少几乎每个团队对调度的诉求都惊人地相似——高优作业要能“挤进去”但低优作业也不能饿死。这两个目标天然存在张力本文就用LSF最核心的bsub命令和配套的队列配置把这条平衡木怎么走完完整整讲清楚。1. 先把调度问题说透抢占和公平不是同一个维度的事1.1 一个典型的混乱场景生产作业被长任务堵死想象这样一个画面周五下午数据分析组往集群里丢了200个基因比对任务平均跑8小时。与此同时生产环境的核心计算任务也在排队但队列前面的长作业把CPU占得满满当当。你打开bjobs一看生产任务的排队时长已经超过5小时业务方电话一个接一个。这时候哪怕你把生产任务的优先级调到100它还是在排队因为正在跑的作业不会被新提交的几个任务自动挤掉。很多人第一次接触LSF时脑子里默认了一个错误的模型认为高优先级队列的任务跑起来后会自动让低优先级队列的任务让路。实际上LSF的默认行为非常保守没有主动抢占机制时正在运行的作业根本不会收到任何干扰信号。你调再高的优先级它也只影响排队顺序不影响已经在跑的作业。这就是生产事故的源头——你这边升了优先级那边该堵还是堵。1.2 LSF队列的本质不是“分区”而是一套策略容器要理解抢占和公平必须先打破“队列就是分区”的思维定式。很多用过Slurm或PBS的工程师常把队列类比成不同的资源池但在LSF里队列queue更像一个装载调度策略的容器。它声明了哪些用户能用、优先级多高、能占多少CPU和内存、作业允许跑多久、作业被抢占后怎么处理等等。同一个队列里的作业可以跑在完全不同的主机上不同队列的作业也能共享同一批机器。所以真正决定“多队列抢占与公平调度”效果的不是建了几个队列而是每个队列身上挂着的策略参数。你在lsb.queues里定义队列时如果只写了QNAME和PRIORITY那这个队列其实和默认队列没有本质区别。后面我会详细拆解每个关键参数的意义这里先记住一个总原则队列是策略集合资源和优先级只是策略的输入项。1.3 抢占与公平的对立统一为什么两者必须同时考虑高优先级任务抢资源靠的是PREEMPTION机制低优先级任务不被饿死靠的是FAIRSHARE和队列权重。前者像“特权通道”后者像“最低生活保障”。只做抢占不做公平结果是低优作业永远起不来集群有效利用率反而下降——因为大家都在提交高优作业最终所有人的优先级一起通胀。只做公平不做抢占生产任务的关键时效性又无法保证。这两件事必须放在同一个配置体系里联动设计。这个道理听着简单但实际配置时大部分人容易走极端。我见过一个团队把所有队列的PREEMPTION开到了最高档结果低优作业每跑20分钟就被挂起一次检查点机制又没做好最后低优作业全在跑重复工作总吞吐量掉了30%。所以不要上来就调抢占参数先得把整个多队列模型想清楚。2. 队列结构设计金字塔模型与预留容量2.1 三级队列划分生产、试跑、批量的典型配置实战里我习惯把队列设计成三层金字塔结构。顶层是生产队列承载时效性最强的任务优先级最高允许抢占低层作业但队列本身的并发数要控制住防止生产作业自己互相踩踏。中间层是试跑和短任务队列适用于代码调试、小规模测试优先级适中不抢占但也尽量不被频繁打断。底层是批量计算队列跑大规模、长周期、可断点的作业优先级最低被抢占后自动挂起或重排队。下面是一份我常用的lsb.queues配置骨架Begin Queue QUEUEproduction PRIORITY400 NICE20 USERSprod_user_group HOSTSall PREEMPTIONSUSPEND RES_REQspan[hosts1] rusage[mem4096] RESUME_CONDrusage[mem3072] RUNLIMIT720 DESCRIPTIONProduction compute tasks End Queue Begin Queue QUEUEnormal PRIORITY200 NICE10 HOSTSall RES_REQrusage[mem2048] RUNLIMIT1440 DESCRIPTIONInteractive short jobs End Queue Begin Queue QUEUEbatch PRIORITY50 NICE0 HOSTSall PREEMPTIONREQUEUE RES_REQrusage[mem1024] RUNLIMIT4320 DESCRIPTIONLong-running batch jobs End Queue这里有几个关键点。PRIORITY决定了排队顺序差值拉开到倍数级时高优作业才能稳定排在前面。NICE用于影响Linux内核级别的CPU份额生产队列给20批量队列给0意思是即便批量作业在跑生产队列的任务在同机器上也更容易抢到CPU时间片。这一点常被忽略但实践中很有效果。2.2 SPEC限制防止高优作业吃掉全部资源高优队列最危险的地方在于它会无限抢占直到整个集群只剩它在跑。为了避免这个问题必须给每个队列设置资源使用上限。LSF里最实用的手段是QMAX和QLIMIT。QMAX限制队列的最大作业槽位数防止同时运行的作业数失控。QLIMIT限制队列每用户或每项目最大槽位数避免单一用户填满队列。我的经验是生产队列的QMAX不要超过集群总槽位的30%。你可能会问高优队列不应该能无限伸展吗实际生产中高优作业往往对特定软件许可证或数据宿主有依赖并发上去了反而互相等IO吞吐量不会线性增长。留出配额给低优作业对生产任务本身也是一种保护。2.3 预留槽位给“紧急任务”留后门除了QMAX之外绑定主机和预留槽位也是一个有效做法。可以单独用一个高优队列绑定几台专用机器并在其上设置所有其他队列不可用。这样紧急任务永远有“硬预留”的资源可跑完全不需要抢占零干扰。抢占机制再快也需要几秒到几分钟的响应时间而硬预留是实时可用的。Begin ResourceMap RESOURCE/GLOBAL_HOSTS pool_prod RESOURCE/GLOBAL_HOSTS pool_batch End ResourceMap配合队列的HOSTS字段将队列绑定到对应资源池。不过硬预留会闲置资源所以只适合业务量波动大的场景集群长期满载的团队不建议大量预留。3. bsub提交背后的调度链路提交参数如何影响排队、抢占和公平3.1 基础提交参数队列、项目、应用名bsub看起来就是交个作业但它往调度器传的每个字段都会参与决策。三个最基础的参数是bsub -q production -P M123 -app dowork ./run.sh这里-q指定队列-P指定项目账号-app指定应用配置文件。项目账号是fairshare计算的最小单位之一如果你不指定LSF会默认归到当前用户的名下fairshare统计就会混乱。应用配置lsb.applications里可以定义CPU、内存、临时目录等环境也会影响调度器对作业资源需求的判断。按我踩过的坑最容易被忽略的是-P。很多用户从脚本模板复制命令压根不知道有项目账号这回事结果fairshare统计出来的全是各用户根目录下的“no_project”优先级计算失去意义。所以如果你要搞多队列公平调度第一步不是调参数而是要求所有业务方在bsub里显式指定项目账号。3.2 资源约束参数-R和-EXT如何影响排队决策作业对资源的需求直接决定了调度器把它放到哪台机器、允许它和什么作业共处。核心参数是-R它有两种常见用法# 硬性资源约束限定内存和CPU bsub -q batch -R rusage[mem8192] span[hosts1] ./longjob.sh # 按资源类型过滤只在有GPU的机器上跑 bsub -q batch -R typegpu ./train.pyrusage[mem8192]的意思是为该作业预留8GB内存。调度器看到这个需求后只会把作业放到能凑齐8GB空闲内存的机器上。这个预留值写太大作业排队时间变长写太小作业运行时OS内存超限会被Cgroup杀掉。所以要结合lsload和bhosts输出的实际机器空闲内存来定。另外一个不太常被提到但极其实用的参数是-EXT它可以给调度器传递外部资源需求例如许可证数量bsub -q production -EXT license_matlab10 ./run_matlab.sh如果许可证不足任务会一直排队而不会启动后干等。从调度公平的角度看这比作业起来后再等许可证要高效得多。3.3 抢占相关选项提交方不能直接指定“我要抢占”有一个新手容易误解的点bsub没有“抢占”参数。抢占行为完全由队列配置里的PREEMPTION字段决定用户提交作业时只能决定进哪个队列进而间接决定自己被抢占还是抢占别人。这样做是合理的否则任何用户都可以给自己的作业标签上“必须立刻运行”公平就无从谈起了。用户真正能影响抢占体验的参数是当作业被抢占时的策略切换也就是从“作业不可中断”变成“支持重跑”。比如提交时可加bsub -q batch -k ./checkpoint_job.sh-k表示作业在收到SIGUSR2时应该执行检查点机制保存状态后自行退出。这样配合队列的PREEMPTIONREQUEUE作业被抢占时能干净地进入重排队流程而不是从零开始跑。生产队列抢占批量作业时也不会因为强杀导致批量作业的数据损坏。4. 抢占策略配置与生产环境踩坑记录4.1 PREEMPTION的三种模式SUSPEND、REQUEUE、KILL怎么选LSF的PREEMPTION字段支持三种动作选择的依据是作业类型和业务容忍度模式动作适用场景风险SUSPEND暂停低优作业保留内存和进程上下文交互式、短作业暂停太久占着内存不放REQUEUE向低优作业发信号作业自行退出重新排队有检查点、可断点续算的作业作业不支持信号时会数据丢失KILL直接杀掉低优作业资源立即释放完全不重要的试探性任务强杀导致脏数据和任务失败SUSPEND看上去最温柔但有个隐藏问题被挂起的作业进程虽然不占CPU但内存仍然被占用。如果批量作业全都SUSPEND在那里内存被占满新任务根本调度不进来等于资源被冻结了。所以用SUSPEND时必须设置RESUME_COND让作业在低优队列资源宽裕时自动恢复。我给生产队列的配置里专门加了RESUME_CONDrusage[mem3072]意思是当机器还有3GB以上空闲内存时恢复被挂起的低优作业避免长时间冻结。REQUEUE是我在长周期批量计算里最推荐的方案前提是业务方做好检查点。LSF会在发送信号后等待一段时间由REQUEUE_EXIT_TIME控制如果作业没按计划退出它会升级处理。这里要提醒所有用户REQUEUE不是万能的它依赖作业对SIGUSR2信号的响应不接受信号的任务会以失败告终。KILL适合清洁任务比如临时生成的脚本和一次性计算。但生产环境慎用因为KILL掉的作业在LSF记录里是“任务失败”不会自动重排需要外部流程兜底。4.2 抢占阈值别让高优作业频繁打断低优作业如果你让生产队列对任何batch作业都立即抢集群会陷入“跑5分钟-抢-重排-再跑5分钟”的循环。这时候需要设置抢占阈值。常用参数是PREEMPTION条件字段和RUNLIMIT的配合Begin Queue QUEUEproduction PREEMPTIONSUSPEND START_CONDrusage[mem2048] defined(info_cpu) SLOTS128 End Queue此外我通常会在lsb.params里打开抢占频率控制Begin Parameters PREEMPT_INTERVAL30 PREEMPT_MIN_RUN_TIME120 End ParametersPREEMPT_MIN_RUN_TIME120的意思是低优作业至少连续运行120秒后才允许被抢占。这个参数极大减少了任务频繁重启的问题。很多长任务初始化阶段就要消耗几十秒加载数据如果刚跑10秒就被抢那整个集群的有效计算量基本为零。这个参数是我觉得最值得调的抢占参数没有之一。4.3 一次真实的抢占“踩坑”排查抢占后作业卡死在BYEXIT有一次用户反馈批量作业被生产作业抢了之后状态变成EXIT但主机槽位一直被占用新作业调度不进来。我查了bhosts发现该机器仍有槽位在跑bjobs -l显示作业状态为BYEXIT进程实际已经不存在。问题出在抢占信号结束后作业没有正确回收。排查链路是这样的先看bhist -l job_id确认作业事件序列从RUN到SIGUSR2再到EXIT。再看lsb.acct里该作业的最后一个事件job finished还是job exited。然后用bjob -d查daemon日志发现sbatchd在回收进程时因为作业申请了超大共享内存段清理超时导致槽位释放失败。最终定位是作业本身的问题它申请了一片巨大的内存文件映射信号处理完成后没有自动释放内核回收卡在了page cache刷新上。解决方案有两层一是让作业脚本在收到信号后主动清理共享内存再退出二是在队列配置里给这类作业戴上RES_REQ限制不允许申请无上限的共享内存段。这类问题做一次排查就能积累很多经验以后再遇到基本15分钟内能定位。5. fairshare调优怎么让两个队列“吵架”时还能维持公平5.1 fairshare计算原理用户、项目、历史的三角关系公平调度在LSF里叫fairshare它并不是简单的“人人有份”而是基于使用历史动态调整优先级。核心参数在lsb.params里Begin Parameters FAIRSHAREYES SHARE_USERSALL SHARE_QUEUESALL FS_INTERVAL30 FS_CUSTOM_POLICYNO End Parametersfairshare计算的基本单位有三个层级用户、项目、队列。调度器记录每个层级在最近一段窗口内的CPU消耗量结合管理员设定的份额比例算出一个“欠债”程度。用得多的实体后续排队时的优先级权重会被压低用得少的实体权重升高。这就是所谓的“动态公平”而不是简单的轮询队列。举个例子如果两个用户各分到50%的份额用户A今天上午用掉了60%的资源用户B只用40%那么下午的调度中B的作业排队时会比A的同类作业更快被调度。注意这是独立于队列优先级之外的另一个影响因子两个用户提交到同一个普通优先级队列时fairshare会发挥主要调节作用。5.2 队列权重和fairshare的协同优先级差多少才合理队列优先级PRIORITY和fairshare权重SHARE是两套体系。优先级决定的是“先来后到”中的先后份额决定的是不同用户/项目之间的“资源配额”。如果队列优先级差太大低优队列里的fairshare调控基本失效因为调度器在优先级排序阶段就把低优队列的任务挤到后面了。我在生产环境给的常规参数是三个队列的优先级差保持在2~4倍以内同时给低优队列较大的fairshare份额这样能保证它在整体排队中不被彻底遗忘Begin Parameters SHARESuser[user_a:20, user_b:20, user_c:15, user_d:15, others:30] End Parameters只有当低优队列的作业在队列内进行了fairshare调整后仍然因为总资源不足而排不上才说明集群是真的满了。这时候要做的是扩容或限流而不是继续调高优先级。5.3 调整fairshare参数后的验证方法调完参数不能只看配置必须观察一段时间的行为。我的验证方法是先用bqueues -l确认当前各个队列的调度顺序再用bfairshare查看每个用户的公平份额状态bfairshare -u user_a这个命令会输出该用户的DEMAND、USED和SHARE其中USED/SHARE的比值超过1.5就说明他正在“透支”资源调度器会主动降低他后续作业的优先级。观察一周的数据后再根据各业务的完成情况微调份额比例。有一回我把某项目份额调到了30%结果它还在继续大量提交作业其他项目的作业排队时间暴涨。查了bfairshare才发现那个项目用的是另一个未在SHARES里定义的用户名它的份额被划归到others里而others总共占30%等于该项目事实上拿走了30%的份额。后来我把所有业务账号和项目账号的映射关系做了一次全面核对确保配置里提到的每个名字都对应真实存在的实体。6. 调度健康度检查从日志里看懂LSF的实际决策6.1 bjobs和bqueues的“信息差”怎么判断作业是否健康排队很多运维看bjobs -u all时只知道作业在排队PEND但搞不清为什么排队。其实LSF在bjobs -l里会把调度器拒排的原因写得很清楚比如“no host selected”或“job is not eligible for execution”bjobs -l 123456输出里有一段Scheduling Reason如果显示not eligible多半是RUNLIMIT到了或者作业的需求超出了任何主机的空闲资源如果显示waiting for fairshare那就是fairshare正在限制你说明调度器认为你这个用户/项目已经“用多了”。基于我的经验发现PEND状态超过一小时不要只看队列长度先跑一遍bjobs -p。它会直接列出排队原因比翻日志高效得多。bjobs -pbjobs -p的输出中可以看到pending原因字段例如NEW_JOB、RESOURCES、FAIRSHARE、EXCLUSIVE等。每种原因对应一种处理方案处理好再谈调度调优。6.2 事件日志与lsb.acct掌握抢占和公平的真实轨迹LSF的完整调度决策日志分散在几个文件里关键是lsb.acct、lsb.events和lsb.msg。排查抢占问题最直接的是lsb.eventsls -t $LSF_TOP/logging/*/lsb.events这个文件记录每一个作业从提交到结束的完整事件序列。搜到目标作业ID后能看到PREEMPTED事件和SIGNAL事件能精确到是哪台主机、哪个调度器进程发起的抢占。lsb.acct则记录作业的最终会计数据包含CPU时间、内存峰值、结束码、退出的主机和原因。我惯用的排查方式是双管齐下先用bhist -l job_id看作业生命周期再用grep job_id lsb.events找抢占事件。如果两者对不上比如bhist显示作业被抢占但lsb.events里没有对应记录那通常说明日志轮转把数据截断了或者是多集群环境里作业跨集群迁移导致的记录分裂。6.3 监控指标哪些数字值得每天看调度健康度不用天天盯这些日志太累。我在日常监控里只保留几个核心指标阈值报警即可pending_time_avg各队列平均排队时长超过30分钟报警。preempt_count每天抢占事件总数超过100次说明抢占阈值设置太低。fairshare_util_ratioused_share / target_share超过2.0说明某个用户/项目严重透支。queue_slot_utilization各队列槽位利用率长期低于60%的高优队列要关注是否资源池过大。zombie_job_count僵尸作业数量这个值飙升时基本都在并发清理信号处理的环节出问题。这些指标可以用bacct每天拉一次摘要也可以自己写个小脚本定时执行bqueues和bfairshare把输出灌进监控系统。最初我靠人工观察后来干脆写了个20行的Shell脚本每天早晨9点发一份调度健康邮件里面有各队列的PEND/RUN数量和fairshare透支用户排名。这个习惯坚持下来后很多调度恶化在萌芽阶段就被发现了不用等用户投诉才知道集群有问题。7. 最后再分享一个关于“文档化”的建议调完这套抢占与公平体系后最容易被忽略的是没人把规则写明白。业务方不知道批量作业会被抢占也不知道生产队列有QMAX限制他们只会在任务失败后不断提工单。我后来养成的习惯是每次调整队列参数后立刻把变更记录在集群wiki里包括修改的参数、原因、预期效果。同时给业务方出一份简短的提交规范告诉他们什么时候用哪个队列什么样的作业建议加检查点生产作业的许可证申请怎么写。这个动作看起来和“技术”无关但实际对调度公平和集群稳定性的贡献不亚于任何参数调优。毕竟调度器再聪明也架不住不按规则出牌的人。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑