三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Slurm集群作业管理实战:从提交到监控的完整命令指南

Slurm集群作业管理实战:从提交到监控的完整命令指南

1. 项目概述:Slurm集群作业管理的核心武器

如果你正在或即将使用高性能计算集群,那么Slurm这个名字你一定不陌生。它不是什么美味佳肴,而是当今学术界和工业界最主流的开源集群管理和作业调度系统。想象一下,一个拥有成百上千个计算节点的庞大机器,如何公平、高效地把计算任务分配给每个“工人”,并管理好他们的工作状态和产出?这就是Slurm的职责。而作为用户,我们与这个庞大系统交互的唯一方式,就是通过一系列命令行指令。今天,我们就来彻底拆解这些命令,从作业的“生”(提交)到“死”(结束),覆盖你日常使用中90%以上的场景。无论你是刚接触集群的新手,还是想系统梳理知识的老手,这篇基于实战经验的命令指南,都能让你对Slurm作业的管理游刃有余。

很多朋友刚上手时,面对srunsbatchsqueue这些命令可能会感到困惑,参数繁多,输出信息复杂。其实,它们的核心逻辑非常清晰:提交作业、查询状态、控制作业生命周期。掌握了这些,你就掌握了在集群上开展计算工作的主动权。本文将不仅列出命令,更会深入解释每个常用参数背后的逻辑、不同命令的适用场景,以及我在多年使用中踩过的坑和总结的技巧。比如,如何优雅地指定GPU资源?作业卡住了怎么办?如何修改一个已经提交但尚未运行的作业?这些实战问题,我们都会一一找到答案。

2. Slurm作业生命周期与命令全景图

在深入每个命令之前,我们需要建立一个宏观视角,理解一个作业在Slurm系统中经历的完整生命周期,以及每个阶段对应的核心管理命令。这就像理解一个产品的流水线,知道了各个环节,操作起来才能心中有数。

2.1 作业生命周期的五个关键阶段

一个典型的Slurm作业,通常会经历以下五个阶段:

  1. 提交:用户将计算任务(脚本)提交给Slurm调度器。此时,作业进入队列,等待被调度。
  2. 排队:作业在队列中等待满足其资源需求(如CPU、内存、GPU、节点数)的计算节点空闲出来。
  3. 运行:调度器为作业分配了资源,作业开始在计算节点上执行。
  4. 完成/终止:作业正常执行完毕,或因错误、被用户手动终止而结束。
  5. 后处理:用户查看作业的输出、错误日志以及效率统计信息。

2.2 对应各阶段的核心命令家族

围绕这个生命周期,Slurm提供了一套完整的命令集,我们可以将其分为几个家族:

  • 提交家族:负责创建和递交作业。

    • sbatch:最常用的批处理作业提交命令。你编写一个Shell脚本,在其中通过#SBATCH指令指定资源需求,然后用sbatch提交。作业会在后台运行,与你当前终端会话解耦。
    • srun:用于交互式地运行作业。它会分配资源并立即在分配的资源上执行一个命令。通常用于测试、调试,或者作为sbatch脚本内部用于启动并行任务的命令。
    • salloc:分配一个资源分配(如几个节点),并获取一个交互式的Shell。在这个Shell中,你可以直接运行srun命令,而无需再指定资源参数,因为它们已经被salloc分配好了。适合需要交互式探索的场景。
  • 查询家族:负责监控作业和集群状态。

    • squeue:查看作业队列状态的核心命令。可以查看所有作业或自己作业的排队、运行等情况。
    • sinfo:查看集群节点状态。可以知道哪些节点空闲、哪些节点正在工作、哪些节点下线,对于理解为什么作业在排队非常有帮助。
    • scontrol:一个功能强大的管理命令,可以查看作业、节点、分区等非常详细的信息(show子命令),也可以用于修改作业参数(update子命令)。
  • 控制家族:负责干预作业的运行。

    • scancel:终止作业。可以终止单个、多个或符合特定条件的所有作业。
    • scontrol:除了查询,还能用于挂起、恢复作业。
  • 历史与诊断家族:负责查看已完成作业的信息。

    • sacct:查看已完成作业的会计信息。这是squeue的互补命令,squeue看活着的作业,sacct看死去的作业。可以查看作业的运行时间、消耗的CPU时间、内存使用量、退出状态等,对于性能分析和计费至关重要。
    • seff:查看指定作业ID的资源使用效率报告,如CPU和内存的使用率,非常直观。

理解这个全景图后,我们再深入每个命令的细节,就会感觉脉络清晰,不再是一盘散沙。接下来,我们从最核心的作业提交开始。

3. 作业提交:从脚本编写到资源请求

提交作业是万里长征的第一步,也是最容易出错的一步。资源请求不合理,可能导致作业永远排不到队,或者一运行就因内存不足被“杀”。sbatch是这里的主角。

3.1 编写一个规范的sbatch脚本

一个典型的sbatch脚本包含两部分:以#SBATCH开头的Slurm指令和你要执行的常规Shell命令。

#!/bin/bash #SBATCH --job-name=my_test_job # 作业名称,方便在队列中识别 #SBATCH --output=slurm-%j.out # 标准输出重定向到文件,%j会被替换为作业ID #SBATCH --error=slurm-%j.err # 标准错误重定向到文件 #SBATCH --partition=compute # 指定分区(队列名) #SBATCH --nodes=2 # 请求的节点数 #SBATCH --ntasks-per-node=4 # 每个节点上启动的任务数(通常对应MPI进程数) #SBATCH --cpus-per-task=2 # 每个任务分配的CPU核心数 #SBATCH --mem-per-cpu=4G # 每个CPU核心分配的内存 #SBATCH --time=01:00:00 # 作业运行的最大时间(时:分:秒) #SBATCH --gres=gpu:2 # 请求通用资源,这里是每个节点2块GPU # 加载必要的环境模块(根据集群配置) module load cuda/11.7 module load gcc/9.3.0 # 打印一些环境信息,便于调试 echo "Starting job on host: $(hostname)" echo "Job ID: $SLURM_JOB_ID" echo "Allocated nodes: $SLURM_JOB_NODELIST" # 这里是你的实际计算命令 # 例如,运行一个MPI程序 srun ./my_mpi_program input.data # 或者运行一个非MPI的并行任务 # python my_script.py

注意#SBATCH指令必须放在脚本开头,在所有可执行命令之前。Slurm在解析脚本时,会读取这些指令,然后才将脚本交给Shell执行。

3.2 关键资源参数详解与选型逻辑

为什么这么指定参数?背后的考量是什么?

  1. --partition:分区是集群管理员根据节点硬件或用途划分的逻辑组。比如可能有debug(短时间测试)、compute(通用计算)、gpu(GPU节点)、bigmem(大内存节点)。选型逻辑:根据作业需求选择。短测试用debug,需要GPU选gpu,需要超大内存选bigmem。选错分区可能导致作业无法调度。
  2. --nodes--ntasks-per-node--cpus-per-task:这三个参数共同定义了并行计算资源。
    • MPI作业:通常--ntasks-per-node指定每个节点的进程数,总进程数 =--nodes*--ntasks-per-node--cpus-per-task为每个MPI进程绑定CPU核心,提升缓存亲和性。
    • OpenMP/多线程作业:可能只需要1个任务(--ntasks=1),但需要多个CPU核心(--cpus-per-task=16)。
    • 混合MPI+OpenMP--nodes--ntasks-per-node定义MPI进程网格,--cpus-per-task定义每个MPI进程内部的OpenMP线程数。
    • 选型逻辑:明确你的程序是哪种并行模式。最保险的方法是阅读程序文档或咨询开发者。
  3. --mem--mem-per-cpu:指定内存。--mem指定每个节点总内存,--mem-per-cpu指定每个CPU核心的内存。二选一,不要同时指定。选型逻辑:如果你的程序内存需求与核心数线性相关,用--mem-per-cpu更灵活。如果程序有固定的基础内存开销,用--mem更直观。务必预留buffer,不要卡着程序理论最小值申请,否则可能因内存超限被Slurm强制终止。
  4. --time极其重要。这是作业运行时间上限。超时后,作业会被强制终止。选型逻辑:根据历史运行经验估算,并加上一定的安全余量(如20%)。在debug分区,时间限制通常很短(如30分钟),用于快速测试。
  5. --gres:请求通用资源,最常见的是GPU。--gres=gpu:2表示请求2块GPU(类型默认)。更精确的请求可以是--gres=gpu:v100:2(请求2块V100 GPU)。选型逻辑:确认你的代码支持GPU加速,并加载了对应的CUDA环境。

3.3 提交作业与srun交互模式

编写好脚本(假设名为run.slurm)后,使用以下命令提交:

sbatch run.slurm

提交成功后,会返回一个作业ID,例如Submitted batch job 1234567。这个ID是后续查询、控制作业的唯一凭证。

交互式作业srun:当你需要快速测试一个命令,或者进行调试时,可以使用srun

# 请求一个节点的一个核心,运行10分钟,运行一个交互式bash srun --pty --nodes=1 --ntasks=1 --cpus-per-task=1 --time=00:10:00 /bin/bash

进入交互式Shell后,你就可以像在登录节点一样操作,但实际是在计算节点上。退出Shell,作业即结束。

资源分配salloc:它介于sbatchsrun之间。

# 分配2个节点,每个节点4个任务,分配1小时 salloc --nodes=2 --ntasks-per-node=4 --time=01:00:00

命令执行后,你的终端会“附着”到这个新分配的资源上,命令行提示符通常会变化。在此终端中,后续的srun命令会直接使用已分配的资源。

# 此时运行srun,无需再指定节点、任务数等参数 srun ./my_mpi_program

使用exit命令退出,资源释放。

4. 作业查询与监控:掌握集群动态

作业提交后,你不能干等着。你需要知道它是在排队、在运行,还是失败了。squeuesinfo是你的“监控大屏”。

4.1 使用squeue洞察作业状态

squeue是最常用的查询命令。不加任何参数,它会列出所有用户的所有作业,信息量巨大。

squeue

输出示例:

JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON) 123456 compute my_test_jo alice PD 0:00 2 (Resources) 123457 gpu train bob R 2:34 1 gpu-node-03 123458 debug quick_test carl R 0:05 1 compute-01

关键列解析:

  • ST(状态):这是最重要的列。
    • PD:排队中。NODELIST(REASON)列会显示排队原因,如(Resources)(等待资源)、(Priority)(优先级低)、(Dependency)(等待依赖作业)。
    • R:运行中。
    • CG:正在完成(作业已结束,Slurm在进行收尾工作)。
    • F:失败。
    • CA:已取消。
  • TIME:作业已运行时间或排队时间(对于PD状态)。
  • NODELIST(REASON):对于运行中的作业,显示使用的节点列表;对于排队作业,显示排队原因。

常用过滤选项:

  • squeue -u $USER:只看自己的作业。
  • squeue -j 123456:查看特定作业ID的详细信息。
  • squeue --start非常有用。显示排队作业的预计开始时间(如果调度器能估算的话)。
  • squeue -o “%.18i %.9P %.30j %.8u %.2t %.10M %.6D %.20R”:自定义输出格式。例如,这里显示了更长的作业名和节点原因信息。

4.2 使用sinfo诊断集群资源

如果你的作业一直PD且原因是(Resources),就该用sinfo看看集群到底怎么了。

sinfo

输出示例:

PARTITION AVAIL TIMELIMIT NODES STATE NODELIST compute* up infinite 2 idle compute-[01-02] compute* up infinite 1 alloc compute-03 gpu up 2-00:00:00 4 idle gpu-[01-04] gpu up 2-00:00:00 2 alloc gpu-[05-06] debug up 00:30:00 2 idle debug-[01-02]

关键列解析:

  • STATE(节点状态)
    • idle:节点空闲,可用。
    • alloc:节点已被分配,正在运行作业。
    • mix:节点部分资源被分配,部分空闲。
    • drain:节点正在被排干(管理员可能在进行维护),不接受新作业。
    • down:节点宕机,不可用。
  • NODELIST:处于该状态的节点列表。

常用选项:

  • sinfo -N:以节点为单位显示信息,更清晰。
  • sinfo -p compute:只看compute分区的信息。
  • sinfo -R:显示节点不可用的原因(如果状态是draindown)。

通过结合squeuesinfo,你就能清晰地知道:我的作业在等什么?是资源不够,还是节点坏了?从而决定是继续等待,还是调整作业资源请求。

4.3 使用scontrol挖掘详细信息

scontrol show job <jobid>可以让你看到作业的一切详细信息,远比squeue丰富。这对于调试复杂问题至关重要。

scontrol show job 123456

输出信息包括:提交时间、开始时间、运行限制、资源请求详情、使用的节点列表、工作目录、标准输出/错误路径、依赖关系等等。当作业行为异常时,这是第一手的诊断资料。

5. 作业控制与修改:动态管理你的计算任务

计划赶不上变化。你可能需要取消一个错误的作业,或者修改一个还在排队中的作业的资源需求。scancelscontrol update是应对这些情况的工具。

5.1 安全终止作业:scancel的多种用法

scancel用于向作业发送终止信号。

  • 取消单个作业scancel 123456
  • 取消自己所有作业scancel -u $USER慎用!
  • 取消某个分区所有作业scancel -p compute
  • 取消所有排队中的作业scancel -t PD
  • 强制终止:如果作业不响应普通的终止信号,可以加--signal=KILL-9选项:scancel --signal=KILL 123456

注意:对于运行中的作业,scancel会先发送一个软终止信号(SIGTERM),允许程序进行清理工作。如果一段时间后作业仍未结束,Slurm会发送强制终止信号(SIGKILL)。直接使用-9会跳过软终止阶段,可能导致程序产生垃圾文件或数据损坏。

5.2 动态修改排队中的作业:scontrol update

这是一个非常强大但容易被忽略的功能。如果你的作业还在排队(PD状态),你可以修改它的部分参数,而无需取消后重新提交。

可以修改的常见参数包括

  • --time:增加或减少运行时间限制。
  • --partition:切换到另一个分区。
  • --qos:修改服务质量(如从普通QoS切换到高优先级QoS)。
  • --dependency:修改作业依赖关系。
  • --mail-user:修改邮件通知地址。

修改命令格式

scontrol update jobid=123456 time=02:00:00 partition=gpu

这条命令将作业123456的运行时间限制改为2小时,并将其从当前分区移动到gpu分区。

重要限制

  1. 只能修改排队中的作业。运行中的作业无法修改核心参数。
  2. 修改分区或QoS可能会导致作业重新排队,因为调度器需要根据新条件重新评估。
  3. 无法修改核心资源请求(如节点数、CPU数、内存、GPU数),因为这些是调度决策的基础。要修改这些,通常需要取消后重新提交。

5.3 挂起与恢复作业

在某些集群配置下,管理员或拥有特定权限的用户可以挂起和恢复作业。

# 挂起作业 scontrol suspend 123456 # 恢复作业 scontrol resume 123456

挂起后,作业会释放其占用的CPU资源,但内存状态会被保留在节点上。这通常用于临时给更高优先级的作业让路。普通用户通常没有此权限。

6. 历史分析与效率评估:从已完成作业中学习

作业运行结束后,故事并没有结束。分析作业的运行效率、资源使用情况,对于优化代码和资源请求至关重要。sacctseff是这方面的利器。

6.1 使用sacct查看会计信息

sacct是查看历史作业的瑞士军刀,功能极其强大。默认显示最近一天的自己作业。

sacct

输出示例:

JobID JobName Partition Account AllocCPUS State ExitCode ------------ ---------- ---------- ---------- ---------- ---------- -------- 123456 my_test_j+ compute alice 16 COMPLETED 0:0 123456.batch batch alice 16 COMPLETED 0:0 123456.0 my_mpi_prog alice 16 COMPLETED 0:0

常用选项组合

  • sacct -j 123456:查看特定作业的详细信息。
  • sacct --starttime=2024-01-01 --endtime=2024-01-02:查看指定时间段的作业。
  • sacct -o JobID,JobName,Partition,AllocCPUS,State,Elapsed,MaxRSS,TotalCPU:自定义输出字段。
    • Elapsed:实际运行时间。
    • MaxRSS:最大常驻内存集,即作业使用的最大物理内存。这是判断你申请的内存是否合理的关键指标!
    • TotalCPU:作业消耗的总CPU时间(核心数*时间)。
  • sacct -X:只显示作业主体,不显示每个步骤(如.batch.0)。

一个实用的分析命令

sacct -j 123456 -o JobID,JobName,AllocCPUS,ReqMem,MaxRSS,State,Elapsed,TotalCPU --units=G

这条命令可以清晰地看到作业123456申请的内存(ReqMem)、实际使用的最大内存(MaxRSS)、运行状态、耗时和总CPU消耗,并且内存单位是G。如果MaxRSS远小于ReqMem,说明你申请了过多内存,浪费了资源,下次可以适当减少请求。

6.2 使用seff快速获取效率报告

sacct功能强大但输出可能不够直观。seff命令提供了一个简洁明了的资源效率报告。

seff 123456

输出示例:

Job ID: 123456 Cluster: mycluster User/Group: alice/alice State: COMPLETED (exit code 0) Nodes: 2 Cores per node: 8 CPU Utilized: 1-12:34:56 CPU Efficiency: 85.7% of 1-16:00:00 core-walltime Job Wall-clock time: 1-02:00:00 Memory Utilized: 12.5 GB Memory Efficiency: 31.25% of 40.00 GB

这份报告一目了然:

  • CPU Efficiency:CPU利用率。85.7%是相当不错的水平。如果这个值很低(如<50%),说明你的程序可能不是CPU密集型,或者存在大量I/O等待、同步等待,需要优化。
  • Memory Efficiency:内存利用率。31.25%意味着你申请了40GB内存,但只用了12.5GB。这是严重的资源浪费!下次提交时,应该将内存请求降低到16GB或20GB左右,留出一些buffer即可。这样你的作业会更容易被调度,也为其他用户释放了资源。

定期使用seff检查作业效率,是成为一个负责任、高效的集群用户的好习惯。

7. 高级技巧与实战避坑指南

掌握了基本命令后,一些高级技巧和实战中的“坑”能让你用得更顺手。

7.1 作业依赖:构建工作流

你可以让一个作业在另一个作业完成(或成功完成)后再开始运行。这对于多步骤的工作流非常有用。

# 作业B在作业A完成后开始 sbatch --dependency=afterany:123456 jobB.slurm # 作业B在作业A成功完成后开始(退出码为0) sbatch --dependency=afterok:123456 jobB.slurm # 作业B在作业A结束后开始(无论成功失败) sbatch --dependency=after:123456 jobB.slurm

依赖关系可以组合:--dependency=afterok:123456,afterok:123457(两个作业都成功后才开始)。

7.2 数组作业:处理参数扫描

如果你需要运行大量相似的任务(例如用不同的参数运行同一个程序),使用数组作业(Job Array)比提交几百个独立作业高效得多。

#!/bin/bash #SBATCH --job-name=array_test #SBATCH --output=slurm-%A_%a.out # %A是主作业ID,%a是数组索引 #SBATCH --array=1-100 # 创建索引从1到100的数组 # 根据数组索引设置不同的输入参数 INPUT_FILE=”input_${SLURM_ARRAY_TASK_ID}.dat” OUTPUT_FILE=”output_${SLURM_ARRAY_TASK_ID}.dat” ./my_program -i $INPUT_FILE -o $OUTPUT_FILE

提交后,Slurm会调度100个子任务。你可以用squeue看到它们(作业ID类似123456_[1-100])。可以用scancel 123456_[50]取消单个子任务,或用scancel 123456取消整个数组。

7.3 环境变量与工作目录

sbatch脚本中,Slurm会设置一系列有用的环境变量:

  • SLURM_JOB_ID:当前作业ID。
  • SLURM_SUBMIT_DIR:提交作业的目录。
  • SLURM_JOB_NODELIST:分配给作业的节点列表。
  • SLURM_ARRAY_TASK_ID:数组作业的当前索引。
  • SLURM_CPUS_PER_TASK:每个任务分配的CPU数。

一个常见的坑:你的程序可能依赖某些环境变量(如PATH,LD_LIBRARY_PATH),这些在登录节点设置好了,但计算节点可能没有。最佳实践是在脚本中使用module load命令显式加载所需环境,或者使用绝对路径调用程序和库。

7.4 输出与错误日志管理

  • #SBATCH --output#SBATCH --error:务必重定向。否则输出会混在一起,难以调试。
  • 对于长时间运行或输出量大的作业,可以考虑在脚本内部将输出重定向到文件,而不是完全依赖Slurm的重定向。
  • 使用tail -f slurm-123456.out可以实时跟踪运行中的作业输出(在登录节点执行)。

7.5 资源请求的黄金法则

  1. 时间:尽可能准确地估计,并加10-20%缓冲。申请时间过长会降低调度优先级,申请时间过短会被强制杀死。
  2. 内存:通过测试小规模任务,用seff估算MaxRSS,然后按比例放大到全规模,并增加20-30%的安全余量。不要盲目申请超大内存
  3. CPU/GPU:匹配你的程序并行能力。一个只能串行的程序申请16个核心,只会浪费15个核心。使用性能分析工具(如gprof,nvprof)了解你的程序。
  4. 分区:选择合适的队列。在debug队列做短测试,在gpu队列跑GPU任务。

7.6 当作业出问题时

  1. 作业一直PD:用squeue --start看预计时间。用scontrol show job看详细信息。用sinfo检查目标分区资源是否紧张或节点是否drain/down。考虑调整资源请求或换分区。
  2. 作业运行失败(状态为FAILED):首先检查错误日志文件(slurm-<jobid>.err)。常见原因:内存超限(Out Of Memory)、运行超时、依赖的软件模块未加载、输入文件路径错误、权限问题。
  3. 作业被终止(状态为CANCELLED):可能是你或他人用scancel终止了,也可能是系统管理员因维护需要终止的。检查邮件通知或联系管理员。
  4. 程序运行慢:登录计算节点(通过srun --pty bash),使用tophtopnvidia-smi(GPU作业)等命令查看资源实际使用情况。可能是I/O瓶颈、内存交换、或者程序本身并行效率低。

掌握这些命令和技巧,你就能从Slurm的“用户”进阶为“管理者”,从容应对集群上的各种计算任务。记住,清晰的资源请求、高效的代码和定期的效率分析,不仅是对自己负责,也是对共享集群资源的其他用户的尊重。

← 返回列表