本文详细介绍如何在IBM Spectrum LSF中提交、监控和管理作业,涵盖批处理作业、交互式任务和作业控制的各个方面。

LSF作业运行概述

LSF提供两种基本的作业执行方式:

  1. 批处理作业(Batch Jobs):通过bsub命令提交到调度队列,由LSF根据策略自动调度
  2. 交互式任务(Interactive Tasks):通过lsrun命令在最佳可用主机上直接执行

对于大多数作业,你只需在原有命令前添加bsublsrun,无需修改可执行程序或脚本。

批处理作业提交:bsub命令

基本用法

最简单的作业提交:

1
bsub ./my_simulation

LSF会:

  1. 分配唯一的作业ID
  2. 根据队列策略选择合适的主机
  3. 在选定主机上执行作业
  4. 捕获标准输出和错误输出

常用提交选项

指定队列

1
bsub -q normal ./my_job.sh

不同队列可能有不同的优先级、资源限制和运行时长控制。

请求资源

1
2
3
4
5
6
7
8
# 请求4个CPU核心
bsub -n 4 ./parallel_app

# 请求特定内存
bsub -R "rusage[mem=8000]" ./memory_intensive_app

# 组合资源请求
bsub -n 16 -R "rusage[mem=4000] span[ptile=8]" ./mpi_job

资源请求字符串(-R)语法:

  • rusage[mem=X]:每个进程需要X MB内存
  • span[ptile=N]:每台主机分配N个进程
  • span[hosts=1]:所有进程在同一主机

指定作业名称

1
bsub -J "simulation_run1" ./sim.sh

方便识别和管理作业。

输出文件重定向

1
2
3
4
5
6
# 标准输出和错误输出
bsub -o output.%J -e error.%J ./my_job

# %J 会被替换为作业ID
# 合并输出和错误到同一文件
bsub -oo combined.%J ./my_job

设置通知

1
2
# 作业完成时发送邮件
bsub -u user@example.com -N ./long_running_job

作业依赖

1
2
3
4
5
6
7
8
# 提交第一个作业
bsub -J job1 ./step1.sh

# 提交依赖于job1的作业
bsub -w "done(job1)" -J job2 ./step2.sh

# 或使用作业ID
bsub -w "done(12345)" ./next_step.sh

支持的依赖条件:

  • done(job_name):作业完成(无论成功或失败)
  • ended(job_name):作业结束
  • exit(job_name):作业成功退出
  • exit(job_name,!=0):作业以非零状态退出

作业阵列(Job Arrays)

批量提交参数化作业:

1
2
3
4
5
6
7
# 提交100个相似作业
bsub -J "param_sweep[1-100]" ./run_simulation.sh

# 在脚本中使用 $LSB_JOBINDEX
#!/bin/bash
echo "Processing task $LSB_JOBINDEX"
./process_data input_$LSB_JOBINDEX.dat

作业阵列特点:

  • 一次提交管理多个作业
  • 共享作业ID,用索引区分
  • 可以限制同时运行的数量:-J "array[1-100]%10"(最多10个并发)

并行作业

MPI作业

1
2
3
4
5
# OpenMPI
bsub -n 64 mpirun -np 64 ./mpi_app

# Intel MPI
bsub -n 32 -R "span[ptile=16]" mpirun ./mpi_program

OpenMP作业

1
2
# 设置线程数
bsub -n 8 -R "span[hosts=1]" -env "OMP_NUM_THREADS=8" ./openmp_app

混合MPI+OpenMP

1
2
3
4
# 4个MPI进程,每个8线程
bsub -n 32 -R "span[ptile=4]" \
  -env "OMP_NUM_THREADS=8" \
  mpirun -np 4 ./hybrid_app

交互式批处理作业

1
2
3
4
5
6
7
8
# 分配交互式shell
bsub -Is bash

# 带X11转发的交互式会话
bsub -Is -XF xterm

# 交互式运行程序
bsub -Is python

-Is选项使bsub等待作业开始,然后将标准输入/输出连接到终端。

查看作业状态:bjobs命令

基本查询

1
2
3
4
5
6
7
8
9
10
11
# 查看所有挂起和运行中的作业
bjobs

# 查看所有作业(包括已完成)
bjobs -a

# 查看特定作业
bjobs 12345

# 查看特定用户的作业
bjobs -u username

输出示例

1
2
3
JOBID   USER    STAT  QUEUE      FROM_HOST   EXEC_HOST   JOB_NAME   SUBMIT_TIME
12345   john    RUN   normal     login01     compute05   sim_job    Dec 17 14:23
12346   john    PEND  normal     login01                 analysis   Dec 17 14:25

作业状态(STAT):

  • PEND:挂起等待调度
  • RUN:正在运行
  • DONE:成功完成
  • EXIT:异常退出
  • SSUSP:系统挂起
  • USUSP:用户挂起

详细信息

1
2
3
4
5
# 查看作业详细信息
bjobs -l 12345

# 仅查看挂起原因
bjobs -p 12346

挂起原因示例:

  • Job's requirements not satisfied:资源不满足
  • Queue closed:队列已关闭
  • User/group job limit reached:达到作业数限制
  • Not enough processors:CPU不足

格式化输出

1
2
3
4
5
6
7
8
# 自定义输出格式
bjobs -o "jobid user stat queue slots exec_host"

# 更紧凑的输出
bjobs -w

# JSON格式输出
bjobs -json

作业控制命令

暂停作业:bstop

1
2
3
4
5
6
7
8
# 暂停单个作业
bstop 12345

# 暂停作业阵列中的特定元素
bstop "12345[10-20]"

# 暂停所有自己的作业
bstop -u $USER 0

暂停的作业:

  • 保留资源分配
  • 进程被SIGSTOP信号挂起
  • 状态变为USUSP

恢复作业:bresume

1
2
3
4
5
# 恢复单个作业
bresume 12345

# 恢复所有被暂停的作业
bresume -u $USER 0

终止作业:bkill

1
2
3
4
5
6
7
8
9
10
11
# 终止单个作业
bkill 12345

# 强制终止
bkill -s 9 12345

# 终止作业阵列的部分元素
bkill "12345[50-100]"

# 终止队列中的所有作业
bkill -q normal 0

交互式任务:lsrun和lsgrun

lsrun - 单主机任务

lsrun在最佳可用主机上运行命令,基于实时负载信息:

1
2
3
4
5
6
7
8
# 在最佳主机上运行
lsrun ./calculate.sh

# 指定主机类型
lsrun -m "linux" ./linux_only_app

# 指定资源需求
lsrun -R "mem>8000" ./mem_app

lsgrun - 多主机任务组

lsgrun在一组主机上并行运行相同命令:

1
2
3
4
5
# 在5台主机上运行
lsgrun -m "host1 host2 host3 host4 host5" hostname

# 在所有Linux主机上运行
lsgrun -m "linux" uptime

作业历史和会计

查看历史作业:bhist

1
2
3
4
5
6
7
8
# 查看最近的作业历史
bhist

# 查看特定时间范围
bhist -S "2025/12/01/00:00" -E "2025/12/17/23:59"

# 查看特定作业的完整历史
bhist -l 12345

作业会计信息:bacct

1
2
3
4
5
6
7
8
# 查看作业资源使用
bacct -l 12345

# 特定用户的资源统计
bacct -u username

# 按项目统计
bacct -P project_name

输出信息包括:

  • CPU时间
  • 内存使用峰值
  • 运行时长
  • 等待时长

应用集成

包装脚本

创建应用专用的提交脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
# submit_simulation.sh

# 解析参数
INPUT=$1
CORES=${2:-4}

# 提交作业
bsub -n $CORES \
     -J "sim_$(basename $INPUT)" \
     -o logs/sim_%J.out \
     -e logs/sim_%J.err \
     ./run_simulation.py $INPUT

用户使用:

1
./submit_simulation.sh input_data.txt 16

作业提交Portal

通过LSF Application Center,可以为用户提供Web界面:

  • 图形化表单填写作业参数
  • 无需学习LSF命令
  • 预定义的作业模板
  • 实时作业监控

高级作业管理

Checkpoint和Restart

对于长时间运行的作业,支持Checkpoint:

1
2
3
4
5
# 提交支持checkpoint的作业
bsub -k "checkpoint_dir" ./long_job

# 从checkpoint重启作业
brestart checkpoint_dir/job_id

作业迁移

1
2
# 迁移作业到其他主机
bmig 12345 target_host

资源预留

确保重要作业在特定时间运行:

1
2
# 预留资源
bsub -U "SLA_guarantee" -J critical_job ./important_task

最佳实践

1. 合理设置资源请求

不足的后果:作业因资源不足被杀 过量的后果:浪费资源,降低调度优先级

建议:

  • 基于实际需求申请资源
  • 使用-R "rusage[mem=X]"限制内存
  • 通过bhist -l查看历史作业的实际资源使用

2. 使用作业依赖管理工作流

1
2
3
4
# 复杂工作流示例
bsub -J preprocess ./preprocess.sh
bsub -w "done(preprocess)" -J "simulation[1-10]" ./simulate.sh
bsub -w "done(simulation)" -J postprocess ./analyze.sh

3. 合理使用作业阵列

对于参数扫描、蒙特卡洛模拟等场景:

  • 使用作业阵列而非单独提交
  • 控制并发数避免过载集群
  • 利用$LSB_JOBINDEX参数化

4. 监控和日志

1
2
3
4
5
# 实时查看作业输出
bpeek 12345

# 定期检查作业状态
watch -n 10 "bjobs -u $USER"

5. 错误处理

在作业脚本中:

1
2
3
4
5
6
7
8
#!/bin/bash
set -e  # 遇到错误立即退出
set -u  # 使用未定义变量时报错

# 错误处理函数
trap 'echo "作业失败于第$LINENO行"' ERR

# 你的代码...

常见问题排查

作业一直挂起(PEND)

1
2
3
4
# 查看挂起原因
bjobs -p 12345

# 常见原因和解决方案
挂起原因 解决方案
资源不足 降低资源请求或等待资源释放
队列关闭 联系管理员或使用其他队列
达到限额 等待其他作业完成
许可证不足 等待许可证释放

作业失败(EXIT)

1
2
3
4
5
# 查看退出状态
bhist -l 12345

# 检查错误日志
cat error.12345

作业被终止(KILLED)

常见原因:

  • 超过内存限制(OOM)
  • 超过运行时长限制
  • 节点故障

检查:

1
bacct -l 12345  # 查看资源使用

总结

LSF的作业管理系统为用户提供了强大而灵活的工具集。通过bsub提交作业、bjobs监控状态、bkill/bstop/bresume控制执行,用户可以高效管理从简单脚本到复杂并行应用的各类工作负载。

掌握资源请求、作业依赖和作业阵列等高级特性,可以进一步优化资源利用率和作业吞吐量,充分发挥LSF集群的性能优势。


参考资源