Claude Media
Claude Codeでsacct出力からSlurmのOOMとTIMEOUTを切り分ける

Claude Codeでsacct出力からSlurmのOOMとTIMEOUTを切り分ける

Slurmで落ちたジョブをsacctのState・Elapsed・Timelimit・MaxRSS・ReqMemで切り分け、claude -pに表を渡してsbatchのリソース指定を直す手順です。

Slurmのジョブが落ちたとき、手がかりが一番まとまっているのは sacct の出力です。State が OUT_OF_MEMORY なのか TIMEOUT なのか NODE_FAIL なのかで、直す場所が変わります。メモリなら --mem、時間なら --time、ノード障害ならジョブ側は直さずに再投入です。

Claude Codeに任せるなら、sacct の表を claude -p に渡して「どの型か」と「次の sbatch 指定」を聞く形が扱いやすい構成です。この記事は、渡す列の選び方、型ごとの見分け方、プロンプトの組み方の順に並べます。

sacctは「どの型で落ちたか」をStateで答える

State 列は、ジョブの状態を表します。落ちたジョブで見るのは次の4つです。

State意味直す場所
OUT_OF_MEMORY意味ジョブがメモリ不足のエラーを起こした直す場所メモリ要求
TIMEOUT意味制限時間に達して終了した直す場所時間要求、または処理の速度
NODE_FAIL意味割り当てノードの障害で終了した直す場所ジョブは直さず再投入
FAILED意味0以外の終了コードなどで終了した直す場所コードかスクリプト

DEADLINE(締め切りで終了)や CANCELLED(ユーザーか管理者が取り消した)も別の状態として並びます。CANCELLED は「誰が」取り消したかの情報が + 付きで続くことがあり、幅が足りなければ %NUMBER の書式で State 列を広げられます。

ここで注意したいのは、FAILED を「メモリ不足かもしれない」と読まないことです。メモリ不足を明示する状態が OUT_OF_MEMORY として別にあるので、FAILED の原因は終了コードとログに戻って探します。

落とし穴

-Xを付けるとMaxRSSが0になる

-X(--allocations)はジョブ全体の行だけを出すオプションです。ステップを数えないため、使用量の統計は0として報告されます。MaxRSS を見たいときは -X を外します。

判別に使う列を5〜6本に絞る

sacct -o に渡す列は絞るほどClaudeが読み違えにくくなります。型の判別には次の列があれば足ります。

  • JobID: ジョブとステップを区別する(1234、1234.batch、1234.0)
  • State: 上の4型の判定
  • Elapsed: 実際の実行時間
  • Timelimit: 要求した制限時間
  • ReqMem: 要求したメモリ。末尾に M や G が付く
  • MaxRSS: ジョブ内の全タスクの最大常駐メモリ
  • ExitCode: 終了コード:シグナル番号 の形式
  • NodeList と FailedNode: ノード障害の切り分け

ReqMem は注意が要ります。この値はジョブの割り当てから取られ、ステップのものではありません。MaxRSS はステップ行に出ます。つまり、同じジョブの ReqMem と MaxRSS は別の行に分かれて表示され、比べるには両方の行を並べて読む必要があります。

sacct -j 1234,1235,1236 -P \
  -o JobID,State,Elapsed,Timelimit,ReqMem,MaxRSS,ExitCode,NodeList

-P(--parsable2)は列を | 区切りで、行末の | なしで出します。空白で揃えた表よりも、プロンプトに渡したときに列がずれません。-n を足すと見出し行が消えますが、Claudeに渡すときは見出しを残すほうが列の意味を取り違えません。

-j を指定すると、--starttime の既定が最古(Epoch 0)になります。-j も --state も付けない場合は、既定の検索範囲が当日の0時から現在までです。昨日落ちたジョブを -j なしで探すと、何も出ません。その場合は -S で開始時刻を指定します。

出力の読み方: 型ごとの見分け

次は、読み方を示すために作った出力の例です(実在のクラスタの出力ではありません)。

JobID|State|Elapsed|Timelimit|ReqMem|MaxRSS|ExitCode|NodeList
1234|OUT_OF_MEMORY|00:41:12|04:00:00|16G||0:0|node012
1234.batch|OUT_OF_MEMORY|00:41:12|||15980M|0:0|node012
1235|TIMEOUT|04:00:00|04:00:00|16G||0:0|node031
1235.batch|CANCELLED|04:00:03|||6210M|0:0|node031
1236|NODE_FAIL|01:12:44|04:00:00|16G||0:0|node044

1234は MaxRSS が ReqMem に近い値で終わっています。OUT_OF_MEMORY の状態と合わせて読み、メモリ要求を増やす候補になります。

1235は Elapsed が Timelimit と一致しています。TIMEOUT の典型で、MaxRSS は ReqMem に遠いので、メモリは原因ではありません。例のようにステップ行が CANCELLED と出ることもあるので、判定はジョブ行の State で行います。

1236は NODE_FAIL で、Elapsed は制限時間に達していません。ジョブ側の問題ではないので、FailedNode 列で障害ノードを確かめ、そのまま再投入する判断になります。

切り分け

落ちたジョブの切り分け順

  1. QState は OUT_OF_MEMORY ですか?
    答えが次へ

    MaxRSS と ReqMem を比べ、--mem を増やします。

    答えがはい
  2. QState は TIMEOUT ですか?
    答えが次へ

    Elapsed と Timelimit が同じなら、--time を延ばすか、処理を速くします。

    答えがはい
  3. QState は NODE_FAIL ですか?
    答えが次へ

    FailedNode を控えてそのまま再投入します。

    答えがはい

上のどれでもない

FAILED や CANCELLED は ExitCode とジョブのログに戻って探します。

claude -pに表を渡して判定させる

表は短いので、標準入力から渡せます。claude -p は標準入力を読み、パイプで渡したデータに対して答えます。

sacct -j 1234,1235,1236 -P \
  -o JobID,State,Elapsed,Timelimit,ReqMem,MaxRSS,ExitCode \
  | claude -p --bare "$(cat slurm-triage-prompt.txt)"

ここで --bare を付ける点に意味があります。--bare は、フックやスキル、MCPサーバー、自動メモリー、CLAUDE.md の自動読み込みを飛ばして起動を速くし、スクリプト用途で推奨されるモードです。逆に言えば、CLAUDE.md に書いた判定ルールはこの呼び出しでは読まれません。ルールは slurm-triage-prompt.txt のようにプロンプト側へ置きます。

プロンプトの中身は、判定の規則と出力形式だけにします。

標準入力は sacct -P の出力です。ジョブ(JobID に . が無い行)ごとに、
次のどれか1つに分類してください。
- OOM: State が OUT_OF_MEMORY。MaxRSS と ReqMem を比べて増やす量を示す
- TIMEOUT: State が TIMEOUT。Elapsed と Timelimit を示す
- NODE_FAIL: State が NODE_FAIL。再投入でよいと書く
- OTHER: 上のどれでもない。ExitCode だけ示す
表に無い値は推測せず「不明」と書く。出力は1ジョブ1行。

「表に無い値は推測しない」の一文は、MaxRSS が空のジョブ行を見たときの暴走を防ぎます。-X で取った表を渡すと MaxRSS が0になるので、Claudeが「メモリは使っていない」と読み違える危険があります。上の見出しの注意と同じ理由で、-X なしで渡します。

結果をスクリプトで受ける

分類結果を後続の処理に流すなら、--output-format json が使えます。応答に result フィールドが入り、jq -r '.result' で本文だけを取り出せます。コストは応答の total_cost_usd に含まれます(クライアント側の推定値で、請求額とずれることがあります)。

sacct -j 1234,1235,1236 -P \
  -o JobID,State,Elapsed,Timelimit,ReqMem,MaxRSS,ExitCode \
  | claude -p --bare --output-format json "$(cat slurm-triage-prompt.txt)" \
  | jq -r '.result'

標準入力の上限は10MBです。数千ジョブの sacct を丸ごと渡すより、-S と --state で対象を落ちたジョブに絞ってから渡すほうが、上限に収まり、判定も安定します。

sacct -S 2026-10-27 -E 2026-10-28 \
  --state=OUT_OF_MEMORY,TIMEOUT,NODE_FAIL -P \
  -o JobID,State,Elapsed,Timelimit,ReqMem,MaxRSS,ExitCode

検索期間のずれと、sacctを呼ぶ回数

sacct は、-j と --state のどちらを付けるかで既定の検索期間が変わります。

指定開始時刻の既定終了時刻の既定
どちらも無し開始時刻の既定当日の0時終了時刻の既定現在
-j のみ開始時刻の既定最古(Epoch 0)終了時刻の既定現在
--state のみ開始時刻の既定現在終了時刻の既定開始時刻(開始も無ければ現在)
両方開始時刻の既定最古(Epoch 0)終了時刻の既定開始時刻(開始も無ければ現在)

--state だけを付けて -S を忘れると、開始が現在になって何も返りません。「落ちたジョブが無い」と読ませてしまうと誤判定になります。-v を付けると、実際に使われた時間窓が表示されます。

もう1つ、sacctのマニュアルは、シェルスクリプトのループから何度も呼ばないよう求めています。呼び出しはslurmdbdへのRPCになり、集中すると性能低下やサービス拒否に至る可能性があるためです。ジョブごとに sacct を呼んでClaudeに渡す書き方は避け、-j にIDのカンマ区切りを渡して1回で取ります。

ReqMem と MaxRSS の単位がそろわないときは、--units=G で表示を統一できます。ReqMem の末尾には M や G の単位文字が付くため、列ごとに単位が違うまま渡すと、比較のときに桁を読み違える元になります。

ジョブのログも読ませるときの権限

sacct の表だけなら、Claudeにツールを渡す必要はありません。標準入力で全部足ります。ジョブの標準出力ファイルまで読ませたいなら、--allowedTools "Read" で読み取りだけを許します。

claude -p --bare --allowedTools "Read" \
  "slurm-1234.out の末尾を読み、メモリ不足の兆候があるか答えて"

Bash や Edit まで許すと、Claudeが sbatch スクリプトを書き換えたり、sbatch を実行して再投入したりできる状態になります。再投入は共有クラスタの割り当てを消費する操作です。提案までをClaudeに任せ、実行は人が確かめてから行う分担にすると、取り返しのつかない操作を避けられます。

無人で回す場合は --permission-prompts none を付けると、許可の確認が必要な呼び出しは拒否され、Claudeは再試行しません(v2.1.259以降)。

直し方の目安: 値を決めるのは実測

OOMの修正で、--mem を倍にして終わらせたくなる場面があります。ただし判断材料は表にあります。

  • MaxRSS が ReqMem にほぼ届いている: 要求が足りない。実測値に余裕を足した量を要求する
  • MaxRSS が ReqMem よりずっと小さいのに OUT_OF_MEMORY: 表だけでは説明がつかない。計測の仕組みはクラスタの設定に依存するので、管理者に確かめる

2つ目はsacctの仕様ではなく、表を読むときの注意点です。Claudeにも「説明がつかないときは不明と答える」と指示しておくと、無理な数字の提案を避けられます。

例の1234と1235をsbatchの指定に直す

先ほどの例を sbatch のスクリプトに戻すと、書き換える行は次の2か所です。値はこの例の数字から作った一例で、余裕の幅はジョブの性質とクラスタの上限で決まります。

前と後

OOMの1234: 要求メモリを実測の上へ

BEFORE

修正前(ReqMem 16G、MaxRSS 15980M)

#SBATCH --mem=16G
AFTER

修正後(実測に余裕を足した例)

#SBATCH --mem=24G

MaxRSS の15980Mは16Gのほぼ全量で、要求が足りなかったことを示します。実測の少し上ではなく、データ量が増えたときの伸びも見込める値にするかは、ジョブごとに判断します。

前と後

TIMEOUTの1235: 制限時間を延ばす場合

BEFORE

修正前(Timelimit 04:00:00、Elapsed 04:00:00)

#SBATCH --time=04:00:00
AFTER

修正後(延ばした例)

#SBATCH --time=06:00:00

時間を延ばすのは、ログの進捗行で「終わりに近づいていた」と確かめられたときの選択肢です。進捗が止まっていたなら、--time を延ばしても同じ時間だけ無駄に走ります。この場合はジョブの中身を直します。

Claudeに修正案まで出させるときは、プロンプトの末尾に「#SBATCH --mem または --time の変更案を1行で添え、根拠にした列名を書く」と足します。根拠の列を書かせると、MaxRSS が空の行から数字を作る誤りを見つけやすくなります。

MaxRSS と ReqMem の単位は、--units=G を足すとそろえて表示できます。

sacct -j 1234 -P --units=G \
  -o JobID,State,Elapsed,Timelimit,ReqMem,MaxRSS

この表では、ステップ行(1234.batch)の MaxRSS と親行の ReqMem を同じ単位で並べて比べられます。

TIMEOUTも同じで、Elapsed が Timelimit と一致していても、「あと少しで終わる処理」と「無限に回っている処理」は区別できません。ログの進捗行を一緒に読ませて、判断してもらいます。同じ考え方は、分散学習のNCCL timeoutでランクごとのログの最終行を並べる手順にも出てきます。Claude Code torchrun NCCLのtimeoutを追うは、止まった原因を作った側を探す流れです。

コンテナ上のメモリ不足なら、SIGKILLで終わる exit code 137 の見分け方が近い題材です。Claude Codeでexit code 137のOOMを切り分ける手順では、docker inspect の OOMKilled とカーネルログを突き合わせます。Slurmでは State の OUT_OF_MEMORY がその役を担います。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/headless
  2. slurm.schedmd.com/sacct.html
この記事を共有:XはてブLinkedIn