抢占

Slurm 支持作业抢占,即“停止”一个或多个“低优先级”作业,以便让“高优先级”作业运行。 作业抢占作为 Slurm 的 群组调度 逻辑的一种变体实现。 当一个可以抢占其他作业的作业被分配到已经分配给一个或多个可以被抢占的作业的资源时, 这些可抢占的作业将被抢占。 根据配置,被抢占的作业可以被取消,或者可以重新排队并使用其他资源启动,或者可以挂起并在抢占作业完成后恢复,甚至可以与抢占作业共享资源,使用群组调度。

作业的分区的优先级层级(PriorityTier)或其服务质量(Quality Of Service,QOS)可以用来识别哪些作业可以抢占或被其他作业抢占。 Slurm 提供了按分区或按 QOS 配置抢占机制的能力。 例如,低优先级队列中的作业可能会被重新排队,而中优先级队列中的作业可能会被挂起。

配置

有几个与抢占相关的重要配置参数:

  • SelectType: Slurm 作业抢占逻辑支持通过 select/linear 插件分配的节点,以及通过 select/cons_tres 插件分配的插槽/核心/CPU 资源。
  • SelectTypeParameter: 由于资源可能会被作业过度分配(挂起的作业仍然保留在内存中),资源选择插件应配置为跟踪每个作业使用的内存量,以确保不会发生内存页面交换。 当选择 select/linear 时,我们建议设置 SelectTypeParameter=CR_Memory。 当选择 select/cons_tres 时,我们建议 将内存作为资源包含(例如 SelectTypeParameter=CR_Core_Memory)。
    注意: 除非 PreemptMode=SUSPEND,GANG,否则这些内存管理参数并不关键。
  • DefMemPerCPU: 由于作业请求可能未明确指定内存需求,我们还建议配置 DefMemPerCPU(每个分配 CPU 的默认内存)或 DefMemPerNode(每个分配节点的默认内存)。 还可能希望在 slurm.conf 中配置 MaxMemPerCPU(每个分配 CPU 的最大内存)或 MaxMemPerNode(每个分配节点的最大内存)。 用户可以在作业提交时使用 --mem--mem-per-cpu 选项来指定其内存需求。
    注意: 除非 PreemptMode=SUSPEND,GANG,否则这些内存管理参数并不关键。
  • GraceTime: 指定作业在被选中抢占后执行的时间段。此选项可以通过分区或 QOS 使用 slurm.conf 文件或数据库分别指定。 GraceTime 以秒为单位指定,默认值为零,这意味着没有抢占延迟。 一旦作业被选中进行抢占,其结束时间被设置为当前时间加上 GraceTime。该作业会立即发送 SIGCONT 和 SIGTERM 信号,以通知其即将终止。 在达到其新的结束时间后,随后会发送 SIGCONT、SIGTERM 和 SIGKILL 信号序列。
    注意: 当配置了 PreemptMode=SUSPEND 或使用 scontrol suspend 挂起作业时,此参数不被使用。有关在这些情况下设置抢占宽限时间,请参见 suspend_grace_time
  • JobAcctGatherType 和 JobAcctGatherFrequency: 将为每个作业配置“最大数据段大小”和“最大虚拟内存大小”系统限制,以确保作业不会超过其请求的内存量。 如果希望启用额外的内存限制强制执行,请使用 JobAcctGatherTypeJobAcctGatherFrequency 参数配置作业计费。当启用计费且作业超过其配置的内存限制时,将取消该作业,以防止其对共享相同资源的其他作业产生不利影响。
    注意: 除非 PreemptMode=SUSPEND,GANG,否则这些内存管理参数并不关键。
  • PreemptMode: 用于抢占作业或启用群组调度的机制。 当 PreemptType 参数设置为启用抢占时,slurm.conf 主节中的 PreemptMode 选择用于抢占集群中可抢占作业的默认机制。
    PreemptMode 可以按分区指定,以覆盖此默认值,如果 PreemptType=preempt/partition_prio。或者,如果 PreemptType=preempt/qos,则可以按 QOS 指定。 在任何情况下,当启用抢占时,必须为整个集群指定有效的默认 PreemptMode 值。
    GANG 选项用于启用群组调度,无论是否启用抢占(即独立于 PreemptType)。 它可以与其他 PreemptMode 设置一起指定,两者用逗号分隔(例如 PreemptMode=SUSPEND,GANG)。
    • OFF: 是默认值,禁用作业抢占和群组调度。它仅与 PreemptType=preempt/none 兼容。
    • CANCEL: 被抢占的作业将被取消。
    • GANG: 启用同一分区中作业的群组调度(时间切片),并允许恢复挂起的作业。为了使用群组调度,必须在集群级别指定 GANG 选项。
      注意: 如果使用 PreemptType=preempt/partition_prio 启用 GANG 调度,控制器将忽略 PreemptExemptTime 和以下 PreemptParametersreorder_countstrict_orderyoungest_first
      注意: 群组调度是针对每个分区独立执行的,因此如果只希望通过 OverSubscribe 进行时间切片,而不进行任何抢占,则不建议配置具有重叠节点的分区。 另一方面,如果希望使用 PreemptType=preempt/partition_prio 允许来自更高优先级层级分区的作业挂起来自较低优先级层级分区的作业,则需要重叠分区,并且需要 PreemptMode=SUSPEND,GANG 来使用群组调度器恢复挂起的作业。 在任何情况下,不同分区之间的作业不会发生时间切片。
    • REQUEUE: 通过重新排队(如果可能)或取消作业来抢占作业。要重新排队作业,必须设置“--requeue” sbatch 选项,或者在 slurm.conf 中设置全局 JobRequeue 参数为 1。
    • SUSPEND: 被抢占的作业将被挂起,随后群组调度器将恢复它们。因此,SUSPEND 抢占模式始终需要在集群级别指定 GANG 选项。 此外,由于挂起的作业仍将在分配的节点上使用内存,Slurm 需要能够跟踪内存资源以能够挂起作业。
      注意: 由于群组调度是针对每个分区独立执行的,如果使用 PreemptType=preempt/partition_prio,则更高优先级层级分区中的作业将挂起较低优先级层级分区中的作业,以便在释放的资源上运行。只有当抢占作业结束时,挂起的作业才会被群组调度器恢复。 如果配置了 PreemptType=preempt/qos,并且被抢占的作业和抢占作业在同一分区中,则它们将与群组调度器共享资源(时间切片)。如果不在同一分区(即被抢占作业和抢占作业在不同分区中),则被抢占的作业将保持挂起,直到抢占作业结束。
  • PreemptType: 指定用于识别哪些作业可以被抢占以启动待处理作业的插件。
    • preempt/none: 禁用作业抢占(默认)。
    • preempt/partition_prio: 作业抢占基于分区的 PriorityTier。在更高优先级层级分区中的作业可以抢占来自较低优先级层级分区的作业。这与 PreemptMode=OFF 不兼容。
    • preempt/qos: 作业抢占规则由 Slurm 数据库中的服务质量(QOS)规范指定。在 PreemptMode=SUSPEND 的情况下,抢占作业需要提交到具有更高优先级层级的分区或同一分区。 此选项与 PreemptMode=OFF 不兼容。 PreemptMode=SUSPEND 的配置仅支持 SelectType=select/cons_tres 插件。 请参见 sacctmgr 手册页 配置 preempt/qos 的选项。
  • PreemptExemptTime: 指定作业在被考虑抢占之前的最小运行时间。仅在 PreemptMode 设置为 REQUEUECANCEL 时才会遵守。它以时间字符串指定: 时间为 -1 禁用该选项,相当于 0。可接受的时间格式包括“分钟”、“分钟:秒”、“小时:分钟:秒”、“天-小时”、“天-小时:分钟”和“天-小时:分钟:秒”。 PreemptEligibleTime 在 “scontrol show job <job id>” 的输出中显示。
  • PriorityTier: 配置分区的 PriorityTier 设置相对于其他分区,以控制在 PreemptType=preempt/partition_prio 时的抢占行为。 如果来自两个不同分区的两个作业被分配到相同的资源,则在优先级层级值更大的分区中的作业将抢占优先级层级值较小的分区中的作业。如果两个分区的 PriorityTier 值相等,则不会发生抢占。默认 PriorityTier 值为 1。
    注意: 除了用于基于分区的抢占外,PriorityTier 还会影响调度。调度器将在优先级层级最高的分区中评估作业,然后再评估其他分区中的作业,无论哪个作业具有最高优先级。调度器在评估同一优先级层级的分区中的作业时,将考虑作业优先级。
  • OverSubscribe: 将分区的 OverSubscribe 设置配置为 FORCE,适用于所有使用挂起/恢复机制进行作业抢占的分区。 FORCE 选项支持一个额外参数,用于控制可以超额分配给计算资源的作业数量(FORCE[:max_share])。默认情况下,max_share 值为 4。为了抢占作业(而不是群组调度它们),始终将 max_share 设置为 1。要允许来自该分区的最多 2 个作业分配给公共资源(并进行群组调度),请设置 OverSubscribe=FORCE:2
    注意: PreemptType=preempt/qos 将允许在因作业抢占而启动的情况下在分区上运行一个额外的作业。例如,OverSubscribe=FORCE:1 的配置通常只允许每个资源一个作业,但如果通过基于 QOS 的抢占启动,则可以启动第二个作业。
  • ExclusiveUser: 在 ExclusiveUser=YES 的分区中,作业将被禁止抢占或被任何其他用户的作业抢占。唯一的例外是这些 ExclusiveUser 作业将能够抢占(但不会被抢占)来自其他用户的完全“--exclusive”作业。 这是因为“--exclusive=user”阻止抢占,但此分区级别设置只能通过使作业完全独占来覆盖。
  • MCSParameters: 如果在作业上设置了 MCS 标签,抢占将限制为具有相同 MCS 标签的其他作业。如果此参数配置为使用 enforced,select,则默认情况下将在作业上设置 MCS 标签,从而导致此限制普遍适用。

要在进行上述配置更改后启用抢占,如果 Slurm 已在运行,则需要重新启动 Slurm。对 Slurm 中插件设置的任何更改都需要完全重新启动守护进程。如果只是更改分区的 PriorityTierOverSubscribe 设置,可以使用 scontrol reconfig 更新。

如果作业请求通过使用“--exclusive=user”或“--exclusive=mcs”作业选项限制了 Slurm 在节点上运行来自多个用户或帐户的作业的能力,则该作业将被禁止抢占或被任何不匹配用户或 MCS 的作业抢占。唯一的例外是这些 exclusive=user 作业将能够抢占(但不会被抢占)来自其他用户的完全“--exclusive”作业。如果使用抢占,通常建议通过使用作业提交插件禁用“--exclusive=user”和“--exclusive=mcs”作业选项(将“job_desc.shared”的值设置为“NO_VAL16”)。

为了使异构作业被考虑进行抢占,所有组件必须符合抢占条件。当异构作业被抢占时,具有最高抢占模式顺序的第一个识别组件(SUSPEND(最高)、REQUEUECANCEL(最低))将用于设置所有组件的抢占模式。异构作业每个组件的 GraceTime 和用户警告信号保持唯一。

由于在作业挂起时许可证不会被释放,因此使用由更高优先级作业请求的许可证的作业仅在 PreemptMode 为 REQUEUECANCEL 并且 PreemptParameters=reclaim_licenses 设置时才会被抢占。

抢占设计与操作

SelectType 插件将识别待处理作业可以开始执行的资源。当 PreemptMode 配置为 CANCELSUSPENDREQUEUE 时,选择插件还会根据需要抢占正在运行的作业,以启动待处理作业。当 PreemptMode=SUSPEND,GANG 时,选择插件将启动待处理作业,并依赖群组调度逻辑执行作业挂起和恢复,如下所述。

选择插件会传递一个可抢占作业的有序列表,以考虑每个待处理作业的启动候选。 该列表按以下方式排序:

  1. QOS 优先级,
  2. 分区优先级和作业大小(以优先抢占较小的作业),或
  3. 作业启动时间(使用 PreemptParameters=youngest_first)。

选择插件将确定待处理作业是否可以在不抢占任何作业的情况下启动,如果可以,则使用可用资源启动该作业。 否则,选择插件将模拟优先级有序列表中每个作业的抢占,并测试在每次抢占后是否可以启动该作业。 一旦可以启动该作业,抢占队列中的更高优先级作业将不再考虑,但原始列表中要被抢占的作业可能是次优的。 例如,要启动一个 8 节点作业,优先级有序的抢占候选可能是 2 节点、4 节点和 8 节点。 抢占这三个作业将允许待处理作业启动,但通过重新排序抢占候选,可以在仅抢占一个作业后启动待处理作业。 为了解决这个问题,抢占候选被重新排序,最终需要抢占的作业放在列表的第一位,所有其他要被抢占的作业按其分配的节点数量排序,这些节点与待处理作业选择的资源重叠。 在上述示例中,8 节点作业将被移动到列表的第一位置。 然后将重复模拟优先级有序列表中每个作业的抢占的过程,以最终决定抢占哪些作业。 这个两阶段过程可能会抢占不严格按抢占优先级顺序排列的作业,但抢占的作业数量将少于其他情况下所需的作业数量。 请参见 reorder_countstrict_order 的抢占调优参数的 PreemptParameters 配置参数选项。

当启用时,群组调度逻辑(也支持作业抢占)会跟踪分配给所有作业的资源。 对于每个分区,维护一个“活动位图”,跟踪 Slurm 集群中所有并发运行的作业。 每个分区还维护该分区的作业列表和“影子”作业列表。 “影子”作业是高优先级作业分配,它们在低优先级作业的活动位图上“投下阴影”。 被这些“阴影”捕获的作业将被抢占。

每当新作业被分配到分区中的资源并开始运行时,群组调度器会将该作业的“影子”添加到所有低优先级分区。 然后重建这些低优先级分区的活动位图,首先添加影子作业。 任何被一个或多个“影子”作业替换的现有作业将被挂起(抢占)。相反,当高优先级运行作业完成时,其“影子”消失,低优先级分区的活动位图将重建,以查看是否可以恢复任何挂起的作业。

群组调度器插件旨在对“选择”插件做出的资源分配决策进行 反应式。 “选择”插件已增强为识别何时配置了作业抢占,并在选择作业资源时考虑每个分区的优先级。 在为每个作业选择资源时,选择器避免使用其他作业正在使用的资源(除非已配置共享,在这种情况下,它会进行负载平衡)。 但是,当启用作业抢占时,选择插件可能会选择已经被低优先级设置的分区中的作业使用的资源,即使在这些分区中禁用了共享。

这使得群组调度器负责控制哪些作业应该在过度分配的资源上运行。 如果 PreemptMode=SUSPEND,作业将使用支持 scontrol suspendscontrol resume 的相同内部函数被挂起。 观察群组调度器操作的好方法是在终端窗口中运行 squeue -i<time>

抢占在回填调度中的限制

出于性能原因,回填调度器为作业保留整个节点,而不是部分节点。如果在回填调度期间,作业抢占一个或多个其他作业,则这些被抢占作业的整个节点将为抢占作业保留,即使抢占作业请求的资源少于此。这些保留的节点在该回填周期内对其他作业不可用,即使其他作业可以适应这些节点。因此,作业在单个回填迭代中可能会抢占比它们请求的更多资源。

一个简单的例子

以下示例配置为 select/linearPreemptMode=SUSPEND,GANG。 该示例发生在一个 5 节点的集群上:

[user@n16 ~]$ sinfo
PARTITION AVAIL  TIMELIMIT NODES  STATE NODELIST
active*      up   infinite     5   idle n[12-16]
hipri        up   infinite     5   idle n[12-16]

以下是分区设置:

[user@n16 ~]$ grep PartitionName /shared/slurm/slurm.conf
PartitionName=DEFAULT OverSubscribe=FORCE:1 Nodes=n[12-16]
PartitionName=active PriorityTier=1 Default=YES
PartitionName=hipri  PriorityTier=2

runit.pl 脚本启动一个简单的负载生成应用程序,运行指定的秒数。提交 5 个单节点 runit.pl 作业在所有节点上运行:

[user@n16 ~]$ sbatch -N1 ./runit.pl 300
sbatch: 提交批处理作业 485
[user@n16 ~]$ sbatch -N1 ./runit.pl 300
sbatch: 提交批处理作业 486
[user@n16 ~]$ sbatch -N1 ./runit.pl 300
sbatch: 提交批处理作业 487
[user@n16 ~]$ sbatch -N1 ./runit.pl 300
sbatch: 提交批处理作业 488
[user@n16 ~]$ sbatch -N1 ./runit.pl 300
sbatch: 提交批处理作业 489
[user@n16 ~]$ squeue -Si
JOBID PARTITION     NAME   USER  ST   TIME  NODES NODELIST
  485    active runit.pl   user   R   0:06      1 n12
  486    active runit.pl   user   R   0:06      1 n13
  487    active runit.pl   user   R   0:05      1 n14
  488    active runit.pl   user   R   0:05      1 n15
  489    active runit.pl   user   R   0:04      1 n16

现在向 hipri 分区提交一个短时间运行的 3 节点作业:

[user@n16 ~]$ sbatch -N3 -p hipri ./runit.pl 30
sbatch: 提交批处理作业 490
[user@n16 ~]$ squeue -Si
JOBID PARTITION     NAME   USER  ST   TIME  NODES NODELIST
  485    active runit.pl   user   S   0:27      1 n12
  486    active runit.pl   user   S   0:27      1 n13
  487    active runit.pl   user   S   0:26      1 n14
  488    active runit.pl   user   R   0:29      1 n15
  489    active runit.pl   user   R   0:28      1 n16
  490     hipri runit.pl   user   R   0:03      3 n[12-14]

hipri 分区中的作业 490 抢占了来自 active 分区的作业 485、486 和 487。active 分区中的作业 488 和 489 仍在运行。

这种状态持续到作业 490 完成,此时被抢占的作业被恢复:

[user@n16 ~]$ squeue
JOBID PARTITION     NAME   USER  ST   TIME  NODES NODELIST
  485    active runit.pl   user   R   0:30      1 n12
  486    active runit.pl   user   R   0:30      1 n13
  487    active runit.pl   user   R   0:29      1 n14
  488    active runit.pl   user   R   0:59      1 n15
  489    active runit.pl   user   R   0:58      1 n16

另一个示例

在此示例中,我们有三个不同的分区,使用三种不同的作业抢占机制。

# slurm.conf 摘录
PartitionName=low Nodes=linux Default=YES OverSubscribe=NO      PriorityTier=10 PreemptMode=requeue
PartitionName=med Nodes=linux Default=NO  OverSubscribe=FORCE:1 PriorityTier=20 PreemptMode=suspend
PartitionName=hi  Nodes=linux Default=NO  OverSubscribe=FORCE:1 PriorityTier=30 PreemptMode=off
$ sbatch tmp
提交批处理作业 94
$ sbatch -p med tmp
提交批处理作业 95
$ sbatch -p hi tmp
提交批处理作业 96
$ squeue
  JOBID PARTITION     NAME     USER  ST       TIME  NODES NODELIST(REASON)
     96        hi      tmp      moe   R       0:04      1 linux
     94       low      tmp      moe  PD       0:00      1 (资源)
     95       med      tmp      moe   S       0:02      1 linux
(作业 96 完成后)
$ squeue
  JOBID PARTITION     NAME     USER  ST       TIME  NODES NODELIST(REASON)
     94       low      tmp      moe  PD       0:00      1 (资源)
     95       med      tmp      moe   R       0:24      1 linux

另一个示例

在此示例中,我们有一个分区,在该分区中我们希望每次仅执行一个作业(例如核心),除非从高优先级服务质量(QOS)提交了作业。在这种情况下,我们希望启动第二个高优先级作业,并与其他作业在重叠资源上进行群组调度。

# slurm.conf 摘录
PreemptMode=Suspend,Gang
PreemptType=preempt/qos
PartitionName=normal Nodes=linux Default=NO  OverSubscribe=FORCE:1

未来的想法

选择插件中的更多智能: 此抢占实现依赖于 select 插件的智能作业放置。

以下是一个示例:

[user@n8 ~]$ sinfo
PARTITION AVAIL  TIMELIMIT NODES  STATE NODELIST
active*      up   infinite     5   idle n[1-5]
hipri        up   infinite     5   idle n[1-5]
[user@n8 ~]$ sbatch -N1 -n2 ./sleepme 60
sbatch: 提交批处理作业 17
[user@n8 ~]$ sbatch -N1 -n2 ./sleepme 60
sbatch: 提交批处理作业 18
[user@n8 ~]$ sbatch -N1 -n2 ./sleepme 60
sbatch: 提交批处理作业 19
[user@n8 ~]$ squeue
  JOBID PARTITION     NAME     USER  ST       TIME  NODES NODELIST(REASON)
     17    active  sleepme  cholmes   R       0:03      1 n1
     18    active  sleepme  cholmes   R       0:03      1 n2
     19    active  sleepme  cholmes   R       0:02      1 n3
[user@n8 ~]$ sbatch -N3 -n6 -p hipri ./sleepme 20
sbatch: 提交批处理作业 20
[user@n8 ~]$ squeue -Si
  JOBID PARTITION     NAME     USER  ST       TIME  NODES NODELIST(REASON)
     17    active  sleepme  cholmes   S       0:16      1 n1
     18    active  sleepme  cholmes   S       0:16      1 n2
     19    active  sleepme  cholmes   S       0:15      1 n3
     20     hipri  sleepme  cholmes   R       0:03      3 n[1-3]
[user@n8 ~]$ sinfo
PARTITION AVAIL  TIMELIMIT NODES  STATE NODELIST
active*      up   infinite     3  alloc n[1-3]
active*      up   infinite     2   idle n[4-5]
hipri        up   infinite     3  alloc n[1-3]
hipri        up   infinite     2   idle n[4-5]

如果“hipri”作业被放置在节点 n[3-5] 上,那将更理想,这将允许作业 17 和 18 继续运行。但是,一个新的“智能”算法必须包括作业大小和所需节点等因素,以支持理想的放置,这可能会迅速使设计复杂化。任何和所有的帮助在这里都是受欢迎的!

最后修改于 2025 年 6 月 20 日