Slurm入門:srun・sbatchでジョブを実行する方法とよく使うコマンドまとめ

インフラ・システム

Slurm(スラーム)は、大学や研究機関の計算クラスタで広く使われているジョブスケジューラです。ログインノード上で直接重い処理を動かすのではなく、必要なCPU・GPU・メモリ・実行時間をSlurmに申請し、割り当てられた計算ノード上でプログラムを実行します。

この記事では、初めてSlurmを使う人が迷いやすいsrunsbatchの違いから、GPUジョブの実行、ジョブ状態の確認、ログの読み方、キャンセル、失敗時の調査までを、実際に使えるコマンド例とともにまとめます。

注意:パーティション名、アカウント名、GPUの指定方法、利用可能な最大実行時間などはクラスタごとに異なります。この記事のP09osk-gpu77は例なので、利用環境の案内に置き換えてください。

Slurmで最初に覚える4つのコマンド

コマンド用途
srunリソースを確保してコマンドを実行する。対話型作業や短いテストに向く
sbatchジョブスクリプトをキューへ投入する。長時間処理や本番実行に向く
squeue待機中・実行中のジョブを確認する
scancelジョブを停止する

加えて、ノード状態を見るsinfo、終了済みジョブを調べるsacct、ジョブの詳細を見るscontrolも頻繁に使います。

srunとsbatchの違い

srun:対話しながら動作確認したいとき

srunは、確保したリソース上でコマンドを実行するためのコマンドです。対話型シェルを起動すれば、計算ノードに入った状態でPythonの起動、GPUの確認、パッケージのテストなどを行えます。

sbatch:長時間処理を自動実行したいとき

sbatchは、実行内容を書いたシェルスクリプトをSlurmへ投入します。投入後は端末を閉じてもジョブが継続するため、学習、推論、データ処理などの本番実行では原則としてsbatchを使います。

  • 環境確認や短いデバッグ:srun
  • 数十分〜数日かかる処理:sbatch
  • 複数回の実験やパラメータ探索:sbatchまたはジョブ配列

対話型ジョブをsrunで起動する

GPUを1枚、1ノード、13時間確保して対話型bashを起動する例です。

srun \
  --partition=P09 \
  --nodes=1 \
  --ntasks=1 \
  --cpus-per-task=4 \
  --gpus-per-node=1 \
  --mem=32G \
  --time=13:00:00 \
  --pty bash -i

割り当て後、プロンプトが計算ノード側に切り替わります。次のように確認できます。

hostname
nvidia-smi
echo "$SLURM_JOB_ID"
echo "$SLURM_JOB_NODELIST"
python3 --version

作業を終えるときはexitを実行します。シェルを抜けると、確保していたリソースも解放されます。

特定ノードを指定する場合

srun \
  --partition=P09 \
  --nodes=1 \
  --nodelist=osk-gpu77 \
  --ntasks=1 \
  --gpus-per-node=1 \
  --time=01:00:00 \
  --pty bash -i

--nodelistを指定すると、対象ノードが空くまで待たされる可能性があります。特定ノードでなければ動かない理由がない限り、通常は指定しない方が早く実行されます。

主要オプションの意味

オプション意味
--partition=P09投入先のパーティション
--account=ACCOUNT計算資源を利用するアカウント。環境によって必須
--nodes=1使用するノード数
--ntasks=1起動するタスク数。単一Pythonプロセスなら通常1
--cpus-per-task=41タスクへ割り当てるCPU数
--mem=32Gジョブ全体で要求するメモリ量
--mem-per-cpu=8GCPU 1個あたりのメモリ量。通常は--memとどちらか一方
--gpus-per-node=11ノードあたりのGPU数
--gres=gpu:1GPUをGRESとして1枚要求する指定。クラスタによってはこちらを使う
--time=01:00:00実行時間の上限。超過するとジョブは停止する
--nodelist=NODE実行ノードを固定する
--exclude=NODE使用したくないノードを除外する

GPU指定はクラスタのSlurm設定によって異なります。--gpus-per-node=1が通らない場合は、管理者の案内を確認し、--gres=gpu:1などを使用してください。

sbatchでPythonジョブを投入する

次の内容をrun_python.shとして保存します。

#!/usr/bin/env bash
#SBATCH --job-name=python-train
#SBATCH --partition=P09
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=4
#SBATCH --gpus-per-node=1
#SBATCH --mem=32G
#SBATCH --time=13:00:00
#SBATCH --output=logs/%x-%j.out
#SBATCH --error=logs/%x-%j.err

set -euo pipefail

# sbatchを実行したディレクトリへ移動
cd "${SLURM_SUBMIT_DIR}"

# ログ保存先を作成
mkdir -p logs

# 必要に応じて環境を読み込む
# module purge
# module load cuda
# source ~/miniconda3/etc/profile.d/conda.sh
# conda activate myenv

echo "Job ID       : ${SLURM_JOB_ID}"
echo "Job name     : ${SLURM_JOB_NAME}"
echo "Node list    : ${SLURM_JOB_NODELIST}"
echo "Submit dir   : ${SLURM_SUBMIT_DIR}"
echo "Started at   : $(date)"

nvidia-smi

srun python3 high_level_math_en.py

echo "Finished at  : $(date)"

ジョブを投入します。

mkdir -p logs
sbatch run_python.sh

成功すると、次のようにジョブIDが返ります。

Submitted batch job 12345

sbatchはジョブをキューに登録した時点で終了します。プログラムが即座に開始するとは限らず、リソースが空くまでPENDING状態で待機する場合があります。

#SBATCHを書くときの注意

  • #SBATCH行は、実行コマンドより前にまとめて書く
  • #SBATCH --output=logs/%x-%j.out%xはジョブ名、%jはジョブID
  • #SBATCH内では通常のシェル変数展開を期待しない
  • ログ用ディレクトリはジョブ投入前に作っておく方が安全
  • シェルスクリプトにはset -euo pipefailを入れると、途中の失敗を見逃しにくい

コマンド1行だけをsbatchで実行する

短いコマンドなら--wrapを使えます。

sbatch \
  --job-name=test \
  --partition=P09 \
  --gpus-per-node=1 \
  --time=00:10:00 \
  --output=slurm-%j.out \
  --wrap="python3 test.py"

ただし、環境構築や複数コマンドを含む処理では、後から再現しやすいジョブスクリプトを作る方が適切です。

ジョブの状態を確認する

自分のジョブを見る

squeue -u "$USER"

見たい列を指定すると、状態を把握しやすくなります。

squeue -u "$USER" \
  --format="%.18i %.12P %.30j %.10T %.12M %.12l %.6D %R"

特定ジョブの詳細を見る

scontrol show job 12345

要求したCPU・GPU・メモリ、実行ノード、投入時刻、開始時刻、待機理由などを確認できます。

実行開始予定時刻を見る

squeue --start -j 12345

表示される開始時刻は予測値であり、他ジョブの終了状況や優先度によって変化します。

よく見るジョブ状態と待機理由

状態意味
PD / PENDING実行待ち
R / RUNNING実行中
CG / COMPLETING終了処理中
CD / COMPLETED正常終了
F / FAILED異常終了
CA / CANCELLEDキャンセル済み
TO / TIMEOUT制限時間超過
OOM / OUT_OF_MEMORYメモリ不足
待機理由意味
Resources要求したリソースが現在空いていない
Priority他ジョブの方が優先度が高い
Dependency依存先ジョブの終了待ち
QOSMaxJobsPerUserLimitユーザー単位の同時実行数制限
AssocGrpGRESなどアカウントやグループ単位のGPU利用上限

クラスタとノードの状態を見る

sinfo

パーティション、ノード数、制限時間、ノード状態を確認できます。

# パーティションを指定
sinfo -p P09

# ノード単位で表示
sinfo -N -l

# 状態とGRESを見やすく表示
sinfo -N -o "%N %P %t %c %m %G"

代表的なノード状態は、空きがあるidle、割り当て済みのalloc、一部使用中のmix、利用停止中のdowndrainです。

ジョブをキャンセルする

# 特定のジョブを停止
scancel 12345

# 自分の全ジョブを停止
scancel -u "$USER"

# 自分のPENDINGジョブだけ停止
scancel -u "$USER" --state=PENDING

# ジョブ名を指定して停止
scancel --name=python-train

共有環境では、ジョブIDを確認してから実行してください。環境固有のラッパースクリプトが提供されている場合は、管理者の指示を優先します。

終了したジョブをsacctで調べる

squeueに表示されるのは、基本的に待機中または実行中のジョブです。終了後の状態、終了コード、使用メモリ、実行時間はsacctで確認します。

sacct -j 12345 \
  --format=JobID,JobName,Partition,State,ExitCode,Elapsed,AllocCPUS,ReqMem,MaxRSS,NodeList

正常終了の目安はState=COMPLETEDかつExitCode=0:0です。FAILEDOUT_OF_MEMORYTIMEOUTの場合は、標準エラーと要求リソースを見直します。

# 今日実行した自分のジョブを一覧表示
sacct -u "$USER" -S today \
  --format=JobID,JobName%30,State,ExitCode,Elapsed,MaxRSS

ログをリアルタイムで確認する

tail -f logs/python-train-12345.out

標準エラーを別ファイルにしている場合はこちらを確認します。

tail -f logs/python-train-12345.err

Pythonの出力がなかなかログへ現れない場合は、バッファリングが原因のことがあります。python3 -uを使うか、環境変数PYTHONUNBUFFERED=1を設定します。

srun python3 -u high_level_math_en.py

nohupではなくsbatchを使うべき理由

通常のLinuxサーバーでは、次のようなnohup実行が使われます。

nohup python3 high_level_math_en.py > script_output.log 2>&1 &

しかしSlurmクラスタでは、ログインノード上でこのコマンドを実行しても、CPUやGPUをSlurmから割り当てられたことにはなりません。ログインノードへ負荷をかけ、運用ルール違反になる可能性があります。

長時間処理を端末切断後も継続したい場合は、nohupではなくsbatchを使うのが基本です。すでにsrun --ptyで計算ノードを確保している場合でも、その対話セッションを切断すれば処理が終了する可能性があるため、本番処理はジョブスクリプトへ移す方が安全です。

ジョブ配列で複数条件を並列実行する

異なる乱数シードやデータ分割を同じスクリプトで実行する場合は、ジョブ配列が便利です。

#!/usr/bin/env bash
#SBATCH --job-name=seed-test
#SBATCH --partition=P09
#SBATCH --array=0-4%2
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=4
#SBATCH --gpus-per-node=1
#SBATCH --mem=32G
#SBATCH --time=04:00:00
#SBATCH --output=logs/%x-%A_%a.out
#SBATCH --error=logs/%x-%A_%a.err

set -euo pipefail
cd "${SLURM_SUBMIT_DIR}"

SEEDS=(42 100 200 300 400)
SEED="${SEEDS[$SLURM_ARRAY_TASK_ID]}"

srun python3 train.py --seed "${SEED}"

--array=0-4%2は、タスク0〜4を作成し、同時実行数を最大2件に制限する指定です。%Aは親ジョブID、%aは配列インデックスです。

前のジョブが成功したら次を実行する

FIRST_JOB_ID=$(sbatch --parsable preprocess.sh)
SECOND_JOB_ID=$(sbatch --parsable --dependency=afterok:"${FIRST_JOB_ID}" train.sh)

echo "preprocess: ${FIRST_JOB_ID}"
echo "train     : ${SECOND_JOB_ID}"

afterokを使うと、前のジョブが正常終了した場合だけ次のジョブを開始できます。前処理、学習、評価を分離して管理すると、失敗箇所を特定しやすくなります。

よくある失敗と対処法

ジョブがずっとPENDINGのまま

squeue -j 12345 -o "%.18i %.10T %R"
scontrol show job 12345
  • Resources:要求GPU数、メモリ、時間を減らせないか確認
  • Priority:基本的には待つ。大量投入している場合は不要なジョブを整理
  • 特定ノード指定:--nodelistを外す
  • 長すぎる実行時間:短い時間枠の方が空きへ入りやすい場合がある

CUDA out of memory

これは多くの場合、システムRAMではなくGPUメモリ不足です。バッチサイズ、シーケンス長、モデルサイズを減らす、勾配蓄積や混合精度を使う、より大容量のGPUを指定する、といった対応が必要です。

SlurmのOUT_OF_MEMORYで終了する

--memで要求したシステムメモリを超えた可能性があります。sacctMaxRSSを確認し、必要量に少し余裕を持たせて再投入します。

TIMEOUTで終了する

--timeを超えています。必要時間を増やすだけでなく、途中保存と再開機能を実装しておくと、クラスタ障害や時間制限に強くなります。

コマンドが見つからない・環境が違う

which python3
python3 --version
env | sort
module list 2>&1
conda info --envs

ログインシェルとバッチジョブでは、環境変数や初期化ファイルの読み込み方が異なる場合があります。ジョブスクリプト内で必要なmodule loadconda activateを明示してください。

実行前チェックリスト

  • パーティション名とアカウント名は正しいか
  • CPU、GPU、メモリを過剰要求していないか
  • 実行時間に余裕があるか
  • ログ保存先ディレクトリを作成したか
  • ジョブスクリプト内で作業ディレクトリへ移動しているか
  • Python・CUDA・仮想環境を明示的に読み込んでいるか
  • 小さなデータで短時間テストを行ったか
  • 途中保存と再開ができるか

よく使うコマンド早見表

# 対話型シェルを起動
srun --partition=P09 --gpus-per-node=1 --time=01:00:00 --pty bash -i

# バッチジョブを投入
sbatch run_python.sh

# 自分のジョブを確認
squeue -u "$USER"

# ジョブ詳細を確認
scontrol show job 12345

# 開始予定時刻を確認
squeue --start -j 12345

# 終了済みジョブを確認
sacct -j 12345 --format=JobID,State,ExitCode,Elapsed,MaxRSS

# ノード状態を確認
sinfo -N -l

# ジョブを停止
scancel 12345

# ログを追跡
tail -f logs/python-train-12345.out

まとめ

Slurmでは、まず短い動作確認はsrun、長時間の本番処理はsbatchと覚えると整理しやすいです。ジョブを投入した後はsqueueで監視し、終了後はsacctで状態・終了コード・メモリ使用量を確認します。

特に重要なのは、ログインノード上でnohupを使って重い処理を直接動かさず、Slurmを通して適切な計算資源を確保することです。クラスタ固有のルールを確認しながら、最初は短時間・小規模なジョブでテストしてから本番投入すると、無駄な待ち時間や計算資源の浪費を減らせます。