引言:什么是Slurm与sbatch?
在高性能计算(HPC)环境中,Slurm(Simple Linux Utility for Resource Management)是最常用的作业调度系统。它负责管理集群资源,确保多个用户可以高效、公平地共享计算节点。sbatch 是Slurm系统中用于提交批处理作业的核心命令。通过编写一个脚本并使用sbatch提交,用户可以将任务排队,等待系统分配资源后自动执行。
对于初学者来说,面对复杂的集群环境和报错信息往往感到无从下手。本攻略将从最基础的概念讲起,逐步深入到高级技巧和排错指南,帮助你彻底掌握sbatch的使用。
第一部分:新手入门——编写并提交你的第一个作业
1.1 理解作业脚本的结构
sbatch 作业本质上是一个Shell脚本(通常是Bash),其中包含两部分内容:
- Slurm指令(Directives):以
#SBATCH开头的注释,用于告诉调度器你需要多少资源(如CPU、内存、时间等)。 - 执行命令(Body):实际要运行的程序或脚本命令。
1.2 一个最基础的作业脚本示例
假设我们要运行一个简单的Python脚本 hello.py,内容如下:
# hello.py
import time
print("Hello, Slurm!")
time.sleep(10)
print("Job Finished.")
对应的作业提交脚本 job_submit.sh 应该这样写:
#!/bin/bash
#SBATCH --job-name=hello_test # 作业名称,方便在队列中识别
#SBATCH --output=hello_%j.out # 标准输出文件,%j会被替换为作业ID
#SBATCH --error=hello_%j.err # 标准错误文件
#SBATCH --ntasks=1 # 运行1个任务(进程)
#SBATCH --cpus-per-task=1 # 每个任务使用1个CPU核心
#SBATCH --mem=1G # 申请1GB内存
#SBATCH --time=00:05:00 # 最大运行时间:5分钟
# 加载必要的环境模块(视集群配置而定)
module load python/3.8
# 运行你的程序
echo "Starting job..."
python hello.py
echo "Job done."
1.3 提交与查看状态
在终端中,使用以下命令提交:
sbatch job_submit.sh
提交成功后,系统会返回一个作业ID,例如 Submitted batch job 123456。
常用查看命令:
squeue -u <你的用户名>:查看你的作业在队列中的状态(PD表示等待,R表示运行)。sinfo:查看各个节点的状态(idle表示空闲,alloc表示占用)。scontrol show job <作业ID>:查看作业的详细信息。
第二部分:核心参数详解——精准控制资源申请
要精通sbatch,必须理解每一个核心参数的含义。错误的资源申请不仅会导致作业排队时间过长,还可能被系统拒绝或杀掉。
2.1 任务与核心分配 (--ntasks vs --cpus-per-task)
这是最容易混淆的概念。
--ntasks=<number>:指定要运行的任务总数。通常用于MPI程序。--cpus-per-task=<number>:指定每个任务需要的CPU核心数。通常用于多线程程序(如OpenMP、PyTorch多进程)。
场景举例: 如果你要运行一个使用4个节点,每个节点跑1个MPI进程,且每个进程使用16线程的程序:
#SBATCH --nodes=4
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=16
总CPU数 = 4节点 * 1任务/节点 * 16核心/任务 = 64核心。
2.2 内存管理 (--mem vs --mem-per-cpu)
内存申请至关重要。
--mem=<size>:申请每个节点的总内存(单位:M, G, T)。--mem-per-cpu=<size>:申请每个CPU核心的内存。
最佳实践:
除非你非常清楚每个核心的负载,否则通常建议使用 --mem 直接指定节点总内存,这样更直观。
#SBATCH --mem=50G # 申请节点上50GB内存
注意:如果你申请的内存超过了节点物理内存,作业将无法启动(Pending)。
2.3 时间限制 (--time)
格式为 DD-HH:MM:SS。
--time=0-01:30:00:1小时30分钟。--time=1-00:00:00:24小时。
技巧: 初次运行时,建议设置比预估时间稍长一点(例如预估1小时,设置1.5小时),以防数据量突变导致超时被杀。但也不要设置过长,否则会降低排队优先级。
2.4 分区选择 (--partition)
集群通常将节点划分为不同的分区(Partition),如 debug(调试,限时短)、normal(普通)、bigmem(大内存)、gpu(显卡节点)。
#SBATCH --partition=bigmem
第三部分:进阶技巧——数组作业与依赖提交
3.1 作业数组 (--array)
当你需要对大量数据运行相同的脚本时(例如处理100个不同的输入文件),手动提交100个作业太繁琐。作业数组允许你一次提交多个相似作业。
示例: 处理 data_1.txt 到 data_100.txt。
#!/bin/bash
#SBATCH --array=1-100
#SBATCH --ntasks=1
#SBATCH --mem=2G
# SLURM_ARRAY_TASK_ID 是系统自动分配的任务ID,范围在1到100之间
INPUT_FILE="data_${SLURM_ARRAY_TASK_ID}.txt"
OUTPUT_FILE="result_${SLURM_ARRAY_TASK_ID}.out"
echo "Processing $INPUT_FILE"
./my_program < $INPUT_FILE > $OUTPUT_FILE
限制数组数量: 如果你只想运行第1, 5, 9到20号任务:
#SBATCH --array=1,5,9-20
并发限制:
为了避免数组作业占满整个集群,可以使用 %<数字> 限制同时运行的数量:
#SBATCH --array=1-100%10 # 最多同时运行10个,其他的排队
3.2 作业依赖 (--dependency)
有时你需要等一个作业成功完成后,才运行下一个作业(例如:先跑数据清洗,清洗成功后再跑模型训练)。
语法:
sbatch --dependency=afterok:<作业ID1>:<作业ID2> next_job.sh
实际应用场景(脚本内嵌套):
# 1. 提交第一步任务,获取ID
JOB1_ID=$(sbatch --parsable step1.sh)
# 2. 提交第二步任务,依赖第一步成功
JOB2_ID=$(sbatch --parsable --dependency=afterok:$JOB1_ID step2.sh)
# 3. 提交第三步任务,依赖第二步成功
sbatch --parsable --dependency=afterok:$JOB2_ID step3.sh
注:--parsable 参数让 sbatch 仅输出作业ID,方便在脚本中捕获。
第四部分:高级应用——GPU作业与环境配置
4.1 申请GPU资源
在深度学习等场景下,你需要显卡资源。
基本配置:
#SBATCH --partition=gpu
#SBATCH --gres=gpu:1 # 申请1块GPU卡
#SBATCH --mem=10G
#SBATCH --cpus-per-task=4
指定GPU型号(如果集群有多种卡):
#SBATCH --gres=gpu:v100:1 # 申请1块V100显卡
多GPU并行:
#SBATCH --gres=gpu:4 # 申请4块卡(通常意味着在同一个节点上)
4.2 环境加载与虚拟环境
集群通常使用 module 来管理软件环境。在脚本中显式加载模块可以保证代码的可复现性。
# 加载编译器和CUDA
module load gcc/9.3.0
module load cuda/11.1
# 激活Python虚拟环境
source /path/to/your/venv/bin/activate
# 或者使用Conda
# conda activate my_env
python train.py
第五部分:常见问题解决方案 (Troubleshooting)
新手最常遇到的报错及解决方法:
5.1 问题:作业一直处于 PENDING 状态
原因分析:
- 资源不足:你申请的资源(如大内存、特定分区、GPU)目前没有空闲。
- 优先级:你的账户优先级较低,正在排队。
- 时间限制:你申请的时间超过了分区允许的最大时间。
解决方案:
- 使用
squeue -u <用户名>查看。 - 使用
sinfo -N -l查看节点详细状态。 - 使用
scontrol show job <作业ID>查看具体原因(Reason列),常见原因有Resources(资源不足)或Priority(优先级)。
5.2 问题:作业运行几秒后变成 CG (Completing) 或直接消失
原因分析: 通常是脚本写错了,导致立即退出,或者申请的资源与实际需求不符。
- Exit code 1:脚本语法错误或命令不存在。
- Exit code 137:内存不足(OOM),被系统杀掉。
- Exit code 143:超时(Time Limit)。
解决方案:
查看输出文件(hello_123456.out)和错误文件(hello_123456.err)。
- 如果是内存问题,增加
--mem。 - 如果是超时,增加
--time。
5.3 问题:sbatch: command not found
原因分析: 你的终端环境没有加载Slurm客户端,或者你不在登录节点上。
解决方案:
- 确保你在登录节点(Login Node)上,而不是计算节点(Compute Node)。
- 检查是否安装了Slurm:
which sbatch。
5.4 问题:作业卡在 Dependency 状态
原因分析:
使用了依赖提交,但前置作业失败了(非 0 退出码)。
解决方案:
- 检查前置作业的日志,修复错误。
- 如果前置作业是被手动取消的(
scancel),依赖作业也会永远卡住,需要手动取消并重新提交。
第六部分:最佳实践与性能优化
6.1 避免在登录节点运行繁重任务
严禁在登录节点直接运行耗时长、占CPU高的脚本。这会导致整个集群变慢,影响其他用户。必须使用 sbatch 提交。
6.2 监控作业资源使用
提交作业后,你可能想知道实际用了多少资源,以便优化下次申请。
seff <作业ID>:查看作业的效率报告(CPU利用率、内存使用率)。sacct -j <作业ID> --format=JobID,MaxRSS,Elapsed:查看内存峰值和运行时间。
示例输出分析:
如果 seff 显示内存使用率只有 20%,说明你申请了过多内存,下次可以适当减少以提高排队速度。
6.3 使用 sbatch 的交互式调试
如果你不确定需要多少资源,可以先申请一个交互式作业进行测试:
salloc --ntasks=1 --cpus-per-task=2 --mem=4G --time=01:00:00
这会给你分配一个Shell,你在里面运行命令,测试完毕后输入 exit 释放资源。
6.4 善用临时目录 ($TMPDIR)
在计算节点上,通常有一个高速的本地临时目录(/tmp 或 $TMPDIR)。处理大量小文件读写时,先将数据拷贝到 $TMPDIR,处理完再移回共享存储,能极大提升IO性能。
示例代码:
#!/bin/bash
#SBATCH --ntasks=1
#SBATCH --mem=10G
# 创建临时目录变量
TMP_DIR=$TMPDIR
# 将数据拷贝到本地
cp -r /shared/data/input $TMP_DIR/
cd $TMP_DIR/input
# 运行程序
./my_program
# 将结果拷贝回共享存储
cp -r results /shared/data/output/
# 清理(可选,退出时会自动清理)
rm -rf $TMP_DIR/input
结语
掌握 sbatch 不仅仅是学会一条命令,更是理解高性能计算的资源管理哲学。通过合理地评估资源、利用数组作业处理批量任务、以及建立完善的错误处理机制,你可以将集群的威力发挥到极致。
记住:“先测试,再大规模提交” 是避免浪费计算资源和排队时间的黄金法则。希望这篇攻略能成为你探索科学计算之路的得力助手!
