简介:这份《国科大超算中心Slurm作业调度系统使用指南》面向高性能计算初学者与科研人员,聚焦超算集群中作业提交与资源调度的实际问题,帮助读者快速掌握Slurm调度系统的核心用法。资源为1个PDF文件,压缩包约595KB,篇幅紧凑、便于随查随用。内容从基本概念与三种模式区别讲起,依次覆盖sinfo查看队列与节点、squeue查看作业、scontrol show partition/node/job查看详细分区、节点与作业信息,并系统讲解srun交互式提交、sbatch批处理提交、salloc分配式提交三类作业方式,涵盖串行、OpenMP共享内存、MPI并行及GPU作业的提交示例,还涉及IO重定向、环境变量、sbcast文件同步与sattach作业步吸附等进阶操作。目前已有3353人学习,适合需要在中国科大超算或同类Slurm集群上跑通并行程序的用户对照查阅,目录结构清晰,可作为日常提交作业与排错的实用手册。
1. 从登录节点到计算节点:一份被低估的 Slurm 实战手册
很多刚接触高性能计算的人,第一次拿到超算账号后最容易犯的错,就是在登录节点上直接敲./run.sh。程序确实跑起来了,但整个集群的登录响应瞬间变慢,管理员找上门只是时间问题。这份《Slurm 作业调度系统使用指南》解决的正是这个断层——它不讲调度器源码,而是把sinfo、squeue、sbatch、srun、salloc这些命令拆成可查、可抄、可排错的条目。它适合三类人:刚拿到超算账号、需要把本地程序搬上集群的研究生;从 PBS/Torque 迁移过来、需要快速对照命令差异的工程师;以及需要给团队写内部上机规范的技术负责人。手册基于 Slurm 19.05.5 编写,覆盖队列查询、作业提交、环境变量、记账与作业管理,是一份能直接放在手边翻的速查资料。
2. 三种提交模式怎么选:sbatch、srun 与 salloc 的边界
2.1 批处理、交互式与分配式的本质差异
Slurm 把作业提交分成三条路径,选错模式是新手最常见的翻车点。手册里把三者并列讲,但实际使用时判断逻辑很简单:看你的任务需不需要人在终端前盯着。
批处理模式用sbatch提交脚本,命令立即返回,作业进入队列等待调度。脚本被调度后,在分配到的首个节点上执行。关键特性是提交终端的生命周期与作业无关——你关掉 SSH,作业照跑。这是生产环境跑长任务的标准姿势。
交互式模式用srun,资源分配和任务加载两步合一。你在登录 shell 里敲下srun,它会先向slurmctld申请资源,拿到后再在分配节点上启动任务。这个过程中你的终端被占住,任务不结束你回不到提示符。更麻烦的是,如果提交时那个终端断了,任务直接终止。所以它只适合短时间调试,比如验证一个 MPI 程序能不能正常初始化。
分配式模式用salloc,本质是前两者的融合。你先用salloc申请资源,Slurm 把资源分配给你后,在提交节点上执行你指定的命令——通常就是启动一个 shell。然后你在这个 shell 里用srun跑并行任务。salloc和sbatch最核心的区别在于任务执行位置:salloc在提交作业的节点执行,sbatch在分配的第一个计算节点执行。这个差异决定了salloc适合需要指定运行节点、边调边改的场景。
提示:登录节点只做编译和轻量文件操作。任何计算任务,哪怕只跑三秒,也走提交命令。
2.2 用 sinfo 摸清家底再提交
选模式之前得先知道集群有什么。sinfo是查看队列和节点状态的第一命令,但很多人只用默认输出,漏掉了关键信息。
# 查看所有队列的详细状态,包括不可用队列 sinfo -a -l # 只看 GPU 队列,按节点名排序 sinfo -p GPU-V100 -N -S +N # 持续每 5 秒刷新,观察节点状态变化 sinfo -i 5 # 只看宕机或失去活力的节点,并显示原因 sinfo -R第一行-a会显示隐藏队列,有些集群把特定队列配置为对部分用户组隐藏,不加这个参数你根本看不到。-l输出长格式,包含TIMELIMIT、JOB_SIZE、NODES等字段。第二行-N让每个节点占一行,-S +N按节点名升序排列,排查特定节点时比默认的聚合视图有用得多。第三行-i 5进入轮询模式,适合盯着一个刚提交的作业看它什么时候从idle变allocated。第四行-R显示不可用节点的原因,比如Not responding或Low disk space,比只看状态码直观。
输出里的STATE字段值得单独记:idle表示空闲可接新作业,mixed表示节点在跑作业但还有空闲核,allocated表示全部分配完,down和drained都不可用但原因不同——down通常是硬件或服务挂了,drained是管理员主动排空。如果状态后面带*,说明节点没响应,这时候提交上去也是白等。
2.3 squeue 看作业卡在哪
作业提交后不运行,第一反应是squeue看状态。默认输出只有JOBID、PARTITION、NAME、USER、ST、TIME、NODES、NODELIST(REASON)几列,但REASON字段才是排错入口。
# 查看当前用户的所有作业,显示完整原因 squeue -u $USER -o "%.10i %.9P %.20j %.8u %.2t %.10M %.6D %R" # 只看排队中的作业,按提交时间排序 squeue -t PD -S +i # 查看特定作业号的详细信息 squeue -j 12345 --start第一行用-o自定义输出格式,%R展开为完整的NODELIST(REASON)。常见的REASON有:Resources表示资源不够,等就完了;Priority表示有更高优先级的作业在前面;Dependency表示在等依赖作业结束;QOSMaxCpuPerUserLimit表示你用的 QOS 限制了单用户 CPU 数,这时候加节点也没用,得换队列或等。第二行-t PD过滤出排队中的作业,-S +i按提交时间升序,能看出是不是有作业被插队。第三行--start会估算一个预计开始时间,虽然不一定准,但能判断是等几分钟还是等几天。
注意:
squeue默认只显示当前用户的作业。要看全队列用squeue -a,但输出会很长,建议配合-p指定队列。
3. 从 sbatch 脚本到环境变量:把作业写对
3.1 一个能跑的 sbatch 脚本长什么样
手册里把串行、OpenMP、MPI、GPU 作业分开讲,但实际写脚本时结构是统一的。下面这个模板覆盖了最常用的指令块,可以直接改。
#!/bin/bash #SBATCH --job-name=my_sim # 作业名,squeue 里显示 #SBATCH --partition=CPU-Large # 队列名,用 sinfo 查到的 #SBATCH --nodes=2 # 节点数 #SBATCH --ntasks-per-node=32 # 每节点任务数,通常等于 CPU 核数 #SBATCH --time=24:00:00 # 墙上时间限制,超时会被杀 #SBATCH --output=%j.out # 标准输出,%j 自动替换为作业号 #SBATCH --error=%j.err # 标准错误 #SBATCH --mem=64G # 每节点内存 module load intel/2019 # 加载编译器和 MPI module load openmpi/4.0.1 cd $SLURM_SUBMIT_DIR # 回到提交目录,默认就在这但显式写更稳 srun ./my_mpi_program # 用 srun 启动并行任务#SBATCH开头的行是给调度器的指令,不是 shell 注释。--nodes和--ntasks-per-node的乘积是总任务数,MPI 程序里MPI_Comm_size拿到的就是这个值。--time格式是天-小时:分钟:秒,只写24:00:00表示 24 小时。--output和--error里的%j是占位符,Slurm 会替换成实际作业号,避免多次提交互相覆盖。$SLURM_SUBMIT_DIR是 Slurm 注入的环境变量,指向你敲sbatch时所在的目录,脚本里cd过去能保证相对路径正确。
提交命令就是sbatch run.sh,返回一行Submitted batch job 12345。之后用squeue -j 12345看状态,用scancel 12345取消。
3.2 环境变量:Slurm 注入的与用户导出的
Slurm 在作业运行时注入一批环境变量,脚本里可以直接用。手册里列了输入和输出两类,实际高频使用的有这些:
| 变量名 | 含义 | 典型用途 |
|---|---|---|
SLURM_JOB_ID | 作业号 | 日志文件名、结果目录命名 |
SLURM_JOB_NODELIST | 分配的节点列表 | 传给 MPI 启动器或自定义脚本 |
SLURM_NTASKS | 总任务数 | 校验 MPI 进程数是否符合预期 |
SLURM_CPUS_PER_TASK | 每任务 CPU 核数 | OpenMP 设置OMP_NUM_THREADS |
SLURM_SUBMIT_DIR | 提交目录 | 脚本开头cd过去 |
SLURM_JOB_PARTITION | 队列名 | 条件判断走不同分支 |
用户自己导出的变量默认不会传给计算节点,除非用--export显式指定。比如你在登录节点export MY_DATA=/home/user/data,脚本里直接$MY_DATA会拿到空值。正确做法是在脚本里重新export,或者提交时加--export=ALL,MY_DATA。手册里还提到--export=NONE可以清空所有环境变量,适合需要干净环境的场景,但用之前确认脚本不依赖任何外部变量。
3.3 srun 与 sbatch 的配合方式
sbatch脚本里可以嵌srun,这是 MPI 作业的标准写法。srun在作业分配的资源范围内启动任务,Slurm 负责把进程分布到各节点。一个容易踩的坑是:脚本里写mpirun -np 64 ./prog也能跑,但绕过了 Slurm 的进程管理,作业结束时可能残留孤儿进程。用srun ./prog则由 Slurm 统一管理生命周期,scancel时能干净地杀掉所有 rank。
如果作业里有多个可执行文件需要按顺序跑,可以写多个srun,每个srun是一个 job step。squeue默认只显示 job 级别,加-s才能看到 step。scontrol show job <jobid>会列出该作业下所有 step 的状态和资源占用。
4. 避坑与排查:那些手册不会明说的翻车点
4.1 作业一直 PD,REASON 显示 Resources
现象:squeue看到作业状态PD,NODELIST(REASON)显示Resources,等了很久不动。
原因:请求的资源超过了队列当前空闲量。比如队列只有 10 个空闲节点,你申请 20 个,调度器不会给你排队等,而是直接标记资源不足。另一个可能是--mem设得太大,单节点内存不够。
解决:先用sinfo -p <队列名>看空闲节点数和单节点配置。如果确实资源不够,要么减少--nodes或--ntasks,要么换到资源更充裕的队列。如果内存是瓶颈,用sinfo -o "%n %m %e"看各节点总内存和空闲内存,调整--mem到合理值。别盲目加--time,时间限制和资源可用性无关。
4.2 作业运行中突然消失,没有输出文件
现象:squeue里作业不见了,--output指定的文件不存在或为空,sacct显示状态FAILED或NODE_FAIL。
原因:常见有三种。一是脚本第一行#!/bin/bash写错或缺失,Slurm 用默认 shell 执行导致语法错误。二是cd $SLURM_SUBMIT_DIR之前就引用了相对路径的文件,而作业的工作目录不是提交目录。三是节点故障,NODE_FAIL表示分配到的节点在运行中挂了。
解决:先看sacct -j <jobid> --format=JobID,State,ExitCode,DerivedExitCode拿到退出码。ExitCode非零说明程序自身报错,去.err文件里找堆栈。DerivedExitCode能区分是程序退出还是被信号杀死。如果是NODE_FAIL,重新提交即可,Slurm 会把作业重新调度到健康节点。脚本开头加set -e让错误立即暴露,别让脚本带着错误往下跑。
4.3 GPU 作业提交后报错找不到设备
现象:sbatch脚本里写了#SBATCH --gres=gpu:1,但程序启动时报CUDA error: no CUDA-capable device is detected。
原因:--gres只是向调度器申请 GPU 资源,不会自动设置CUDA_VISIBLE_DEVICES。如果集群的 GPU 节点上有多块卡,Slurm 会通过环境变量告诉进程该用哪块,但前提是程序能读到这个变量。有些老版本 MPI 或自定义启动脚本会覆盖这个变量。
解决:在脚本里显式打印echo $CUDA_VISIBLE_DEVICES确认值是否被设置。如果为空,检查sbatch提交时是否带了--gres,以及队列是否配置了 GPU 资源。如果值正确但程序仍报错,在srun前加export CUDA_VISIBLE_DEVICES=$CUDA_VISIBLE_DEVICES强制传递。手册里 GPU 作业部分提到--gres=gpu:1的写法,但没展开环境变量传递链,这是实际使用中最容易卡住的地方。
4.4 scancel 杀不掉作业,进程残留
现象:scancel <jobid>执行后squeue里作业消失,但ssh到计算节点ps aux | grep 用户名还能看到进程在跑。
原因:scancel默认发送SIGTERM,程序如果捕获了这个信号但不退出,或者 MPI 进程之间的通信层没正确传播信号,就会残留。另一种情况是作业脚本里用了&后台启动进程,Slurm 只管理前台进程。
解决:用scancel --signal=KILL <jobid>强制发SIGKILL。如果还不行,scontrol show job <jobid>拿到节点列表,逐个ssh上去手动kill -9。预防措施是在脚本里用trap捕获SIGTERM并清理子进程,或者避免在sbatch脚本里用&启动需要随作业结束而终止的进程。
5. 进阶:用 sacct 做记账分析与作业调优
作业跑完不是终点。sacct是 Slurm 的记账命令,能拿到每个作业的实际资源消耗,用来调优下一轮提交。默认sacct只显示当天作业,加-S指定开始时间才能查历史。
# 查看指定作业的详细资源使用 sacct -j 12345 --format=JobID,JobName,Partition,AllocCPUS,ReqMem,MaxRSS,Elapsed,State # 查看某天之后所有失败作业 sacct -S 2024-01-01 -s FAILED --format=JobID,JobName,State,ExitCode # 按队列统计作业数量和总核时 sacct -S 2024-01-01 -o Partition,AllocCPUS,Elapsed --parsable | awk -F'|' '{print $1, $2, $3}'第一行里MaxRSS是作业实际使用的最大常驻内存,单位通常是 KB 或 MB。如果你申请了--mem=64G但MaxRSS只有8G,下次可以降到16G,减少资源浪费,也更容易排到队。Elapsed是实际运行时间,对比--time限制能看出是不是经常超时。第二行-s FAILED过滤失败作业,ExitCode字段格式是退出码:信号,比如1:0表示程序返回 1,0:9表示被信号 9 杀死。第三行用--parsable输出管道分隔的格式,方便awk做统计。
我自己的习惯是每个作业跑完后跑一遍sacct -j <jobid> --format=JobID,MaxRSS,Elapsed,State,把MaxRSS和申请值对比。如果实际内存不到申请的一半,下次提交就砍掉冗余;如果Elapsed接近--time限制,说明时间设紧了,下次加 20% 余量。这个动作花不到十秒,但能显著提高排队效率。
还有一个容易忽略的点:sacct默认只显示父作业,不显示 job step。加-X可以只看父作业汇总,加-a显示所有 step。排查 MPI 作业里哪个 rank 先挂时,sacct -j <jobid> -a --format=JobID,State,ExitCode能定位到具体 step。
从那以后我每次提交长作业前,都会先用sinfo确认目标队列的空闲节点和内存,再跑一个短时间的srun测试脚本验证环境变量和模块加载,最后才sbatch正式提交。这套流程走下来,因为资源申请不当或环境问题导致的失败少了一大半。希望帮到你。
本文还有配套的精品资源,点击获取