Slurm / srun, sbatch
===
###### tags: `Slurm`
###### tags: `Slurm`, `HPC`, `Linux`, `Slinky`, `SlinkyProject`, `sbatch`
<br>
[TOC]
<br>
## srun 範例
- ### 最基本指令 -- 不限定特定 node
- ### 查詢主機名稱
```
$ srun hostname
esc8000a-1
```
- ### 查詢 GPU 狀態
```
$ srun nvidia-smi
```
- ### 進入節點互動
```
$ srun --pty bash -i
```
- ### 根據 GPU 需求配置節點
```
$ srun --gres='gpu:8' nvidia-smi -L
```
- ### 以容器方式執行
> https://github.com/SlinkyProject/slurm-operator/issues/41
```
# 不需要 GPU 情況:
$ srun --container docker://alpine:latest id
uid=0(root) gid=0(root) groups=0(root)
$ srun --container-image alpine:latest id
pyxis: importing docker image: alpine:latest
pyxis: imported docker image: alpine:latest
uid=0(root) gid=0(root) groups=0(root)
```
```
# 需要 GPU 情況:
$ srun --container-image nvcr.io/nvidia/pytorch:23.10-py3 --pty bash
```
- ### 指定 partition (分區的任意 node) + GPU 需求
```
$ srun -p debug --gres='gpu:8' nvidia-smi -L
```
```
$ srun -p tn --gres='gpu:8' bash -c "hostname & nvidia-smi -L"
esc8000a-1
GPU 0: NVIDIA A40 (UUID: GPU-35729da9-1b69-c8f6-9f22-************)
GPU 1: NVIDIA A40 (UUID: GPU-157a6555-49d1-07bc-bf97-************)
GPU 2: NVIDIA A40 (UUID: GPU-14c00e59-b533-e691-f568-************)
GPU 3: NVIDIA A40 (UUID: GPU-228d9110-2bf1-6707-1afe-************)
GPU 4: NVIDIA A40 (UUID: GPU-755ec026-8359-581c-b638-************)
GPU 5: NVIDIA A40 (UUID: GPU-960a7fde-5e36-47d9-64e0-************)
GPU 6: NVIDIA A40 (UUID: GPU-9c40d7e3-7322-d916-c6a9-************)
GPU 7: NVIDIA A40 (UUID: GPU-b42813b5-fbfe-100d-d062-************)
```
- ### 指定 node + GPU 需求
```
$ srun --nodelist=esc8000b-0 --gres='gpu:8' nvidia-smi -L
```
- ### 指定 partition + 限定 GPU 類型 + GPU 需求
```
$ srun -p debug --nodelist=esc8000b-0 --gres='gpu:8' nvidia-smi -L
```
<br>
## sbatch 範例
### `hello.sbatch`
```bash=
#!/bin/bash
#SBATCH --job-name=hello # 作業名稱(會出現在佇列與輸出檔)
#SBATCH --partition=debug # 指定要丟到哪個 Partition(依你的環境調整)
#SBATCH --ntasks=1 # 啟動 1 個任務(process)
#SBATCH --cpus-per-task=1 # 每個任務使用 1 顆 CPU
#SBATCH --output=slurm-%x-%j.out # 輸出檔名:slurm-<jobname>-<jobid>.out
##SBATCH --time=00:05:00 # (選用) 最長執行時間,超時會被 Slurm 終止
##SBATCH --gres=gpu:1 # (選用) 需要 1 張 GPU;需叢集支援 GPU 資源管理
echo "=== Job started on $(hostname) at $(date) ==="
echo "SLURM_JOB_ID=$SLURM_JOB_ID"
echo "SLURM_JOB_NODELIST=$SLURM_JOB_NODELIST"
# 方式 A:直接在批次環境中執行一個最簡單的 hello
srun bash -lc 'echo "Hello from $(hostname)"; sleep 1'
# 方式 B:(選用)在容器裡跑 nvidia-smi -L ,需叢集有 Pyxis/Enroot
# srun --container-image=nvcr.io/nvidia/pytorch:23.10-py3 nvidia-smi -L
#(如果要把目前目錄掛進容器,可加)
# --container-mounts="$PWD:/workspace"
echo "=== Job finished at $(date) ==="
```
- ### 每一行在做什麼?
* `#!/bin/bash`
指定用 bash 來執行這支批次腳本。
* `#SBATCH --job-name=hello`
設定作業名稱,便於在 `squeue`、輸出檔中辨識。
* `#SBATCH --partition=debug`
指定投遞到哪個 Partition(資源池)。沒有這個 partition 會 Pending;可用 `sinfo` 查看可用的 partition。
* `#SBATCH --ntasks=1`
要啟動的「任務數」。一般 CPU 單機工作一個就夠;MPI 或多任務才會大於 1。
* `#SBATCH --cpus-per-task=1`
每個任務綁定的 CPU 數。若你的程式會用到多執行緒,這裡要調大。
* `#SBATCH --output=slurm-%x-%j.out`
指定標準輸出與錯誤輸出要寫到哪個檔案(`%x`=job 名稱,`%j`=job ID)。
* `##SBATCH --time=00:05:00`(註解掉=暫不生效)
最長執行時間。建議給個合理值,能幫排程器安排資源並避免無限卡住。
* `##SBATCH --gres=gpu:1`(註解掉=暫不生效)
要求 GPU;需叢集已配置 GPU 與對應的 QOS/Partition 支援。
* `echo ...`
打出開始/結束時間與節點資訊,方便除錯與審計。
* `srun bash -lc 'echo "Hello from $(hostname)"; sleep 1'`
用 `srun` 在分配到的資源裡執行命令。即使在 `sbatch` 腳本內,建議用 `srun` 來啟動實際工作,能確保與資源配置的一致性(特別是多任務/多節點時)。
* (選用)`srun --container-image=... nvidia-smi -L`
在容器(Enroot `.sqsh`)中跑 `nvidia-smi -L` 列出 GPU。需要叢集有 **Pyxis/Enroot** 外掛;沒有就把這行保持註解狀態。
想把工作目錄掛進容器再加:`--container-mounts="$PWD:/workspace"`。
---
- ### 如何執行?
1. 存檔
把內容存成 `hello.sbatch`(檔案不必 `chmod +x`,`sbatch` 會直接讀取)。
2. 提交作業
```bash
sbatch hello.sbatch
```
會回覆類似:
```
Submitted batch job 123456
```
3. 查看排程狀態
```bash
squeue -j 123456 # 看這個 job 的狀態
squeue -u $USER # 看自己所有 job
sinfo -s # 看各 Partition 概況
```
4. 追蹤輸出檔(最即時)
```bash
tail -f slurm-hello-123456.out
```
(若你沒自訂 `--output`,預設檔名是 `slurm-123456.out`)
5. 查歷史紀錄與資源使用
```bash
sacct -j 123456 \
--format=JobID,JobName,Partition,State,Elapsed,AllocTRES,MaxRSS,NodeList
```
或看更詳細:
```bash
scontrol show jobid -dd 123456
```
6. 取消作業(若卡住或不需要)
```bash
scancel 123456
```
---
- ### 小提醒 / 常見坑
* **Partition 名稱**要真實存在;用 `sinfo` 確認。
* 若執行容器那行出現 `pyxis` 相關錯誤,代表叢集沒裝 **Pyxis/Enroot**,請改用非容器的 `srun` 指令或用叢集支援的容器方式。
* 要 GPU 請確保使用了對應的 **Partition/QOS** 且帳號有權限,否則會長時間 Pending(`Reason=QOSMaxGRES`、`(Priority)` 等)。
* `--time` 沒設定時會用 Partition 或叢集預設上限;建議給一個合理值,利於排程。
<br>
## 討論
### 如何查看 sbatch 的執行結果?
- ### Question
```
$ sbatch -A tj-account -p debug -N1 -n1 --wrap='hostname && whoami'
Submitted batch job 959
```
要如何查看執行結果?
- ### gpt-5 (2025/08/28)
最直接的方法有三種:看 **狀態**、找 **輸出檔**、查 **帳務紀錄**。
### 1) 先看工作狀態
```bash
# 只看這個工作
squeue -j 959
# 看詳細資訊(含原因、輸出路徑等)
scontrol show job 959 | egrep 'JobState|Reason|NodeList|StdOut|WorkDir'
```
- **執行範例**:
```
$ scontrol show job 959
JobId=959 JobName=wrap
UserId=tmp1(20011) GroupId=tj-account(1002) MCS_label=N/A
Priority=1 Nice=0 Account=tj-account QOS=normal
JobState=FAILED Reason=RaisedSignal:53(Real-time_signal_19) Dependency=(null)
Requeue=1 Restarts=0 BatchFlag=1 Reboot=0 ExitCode=0:53
RunTime=00:00:01 TimeLimit=UNLIMITED TimeMin=N/A
SubmitTime=2025-08-28T07:07:53 EligibleTime=2025-08-28T07:07:53
AccrueTime=2025-08-28T07:07:53
StartTime=2025-08-28T07:07:53 EndTime=2025-08-28T07:07:54 Deadline=N/A
SuspendTime=None SecsPreSuspend=0 LastSchedEval=2025-08-28T07:07:53 Scheduler=Main
Partition=tn AllocNode:Sid=slurm-login-6fb5d948d6-5mcvp:193068
ReqNodeList=(null) ExcNodeList=(null)
NodeList=esc8000b-0
BatchHost=esc8000b-0
StepMgrEnabled=Yes
NumNodes=1 NumCPUs=2 NumTasks=1 CPUs/Task=1 ReqB:S:C:T=0:0:*:*
ReqTRES=cpu=1,mem=240G,node=1,billing=1
AllocTRES=cpu=2,mem=240G,node=1,billing=2
Socks/Node=* NtasksPerN:B:S:C=0:0:*:* CoreSpec=*
MinCPUsNode=1 MinMemoryNode=0 MinTmpDiskNode=0
Features=(null) DelayBoot=00:00:00
OverSubscribe=OK Contiguous=0 Licenses=(null) LicensesAlloc=(null) Network=(null)
Command=(null)
WorkDir=/home/tmp1
StdErr=/home/tmp1/slurm-959.out
StdIn=/dev/null
StdOut=/home/tmp1/slurm-959.out
```
### 2) 讀取輸出檔(預設在提交指令的目錄)
`sbatch` 預設把 stdout/stderr 寫到 `slurm-<JobID>.out`。
你的例子應該是:
```bash
ls -l slurm-959.out
cat slurm-959.out
# 即時追蹤(工作還在跑也看得到)
tail -f slurm-959.out
```
> 不確定檔案在哪?用這招查路徑:
> `scontrol show job 959 | grep StdOut`(同時也可看 `WorkDir`)
### 3) 查完成後的帳務/結果(含退出碼)
```bash
# 工作結束後可用(需要 slurmdbd 正常)
sacct -j 959 \
--format=JobID,JobName%18,Partition,Account,User,State,ExitCode,Elapsed,NodeList
```
- **執行範例**:
```
$ sacct -j 959 \
--format=JobID,JobName%18,Partition,Account,User,State,ExitCode,Elapsed,NodeList
JobID JobName Partition Account User State ExitCode Elapsed NodeList
------------ ------------------ ---------- ---------- --------- ---------- -------- ---------- ---------------
959 wrap tn tj-account tmp1 FAILED 0:53 00:00:01 esc8000b-0
959.batch batch tj-account CANCELLED 0:53 00:00:01 esc8000b-0
959.extern extern tj-account COMPLETED 0:0 00:00:01 esc8000b-0
```
---
#### 小技巧
* 之後可以自訂輸出檔路徑與名稱,方便找:
```bash
sbatch -A tj-account -p tn \
-o /shared/slurm-logs/%x-%j.out -e /shared/slurm-logs/%x-%j.err \
--wrap='hostname && whoami'
```
其中 `%x`=JobName、`%j`=JobID。
* 若卡在 `PENDING`,用:
```bash
scontrol show job 959 | egrep 'JobState|Reason|Partition|QOS|Account|TRES'
```
看 `Reason`(例如資源不足、QOS/配額限制等)。
<br>
### 沒有看到 sbatch 的執行結果?
> :warning: **關鍵點**:
> - compute node 的 user id ,要跟 login node 的 user id 一致
> - gid 不一致好像不影響
- ### Question
```
$ sbatch -A tj-account -p debug -N1 -n1 --wrap='hostname && whoami'
Submitted batch job 961
```
執行了 sbatch, 但在 login 節點或是 compute 節點,都沒看到 961.out
```
tmp1@slurm-login-6fb5d948d6-5mcvp:~$ scontrol show job 961
JobId=961 JobName=wrap
UserId=tmp1(20011) GroupId=tj-account(1002) MCS_label=N/A
Priority=1 Nice=0 Account=tj-account QOS=normal
JobState=FAILED Reason=RaisedSignal:53(Real-time_signal_19) Dependency=(null)
Requeue=1 Restarts=0 BatchFlag=1 Reboot=0 ExitCode=0:53
RunTime=00:00:01 TimeLimit=UNLIMITED TimeMin=N/A
SubmitTime=2025-08-28T07:13:57 EligibleTime=2025-08-28T07:13:57
AccrueTime=2025-08-28T07:13:57
StartTime=2025-08-28T07:13:57 EndTime=2025-08-28T07:13:58 Deadline=N/A
SuspendTime=None SecsPreSuspend=0 LastSchedEval=2025-08-28T07:13:57 Scheduler=Main
Partition=debug AllocNode:Sid=slurm-login-6fb5d948d6-5mcvp:193068
ReqNodeList=(null) ExcNodeList=(null)
NodeList=e900-0
BatchHost=e900-0
StepMgrEnabled=Yes
NumNodes=1 NumCPUs=2 NumTasks=1 CPUs/Task=1 ReqB:S:C:T=0:0:*:*
ReqTRES=cpu=1,mem=240G,node=1,billing=1
AllocTRES=cpu=2,mem=240G,node=1,billing=2
Socks/Node=* NtasksPerN:B:S:C=0:0:*:* CoreSpec=*
MinCPUsNode=1 MinMemoryNode=0 MinTmpDiskNode=0
Features=(null) DelayBoot=00:00:00
OverSubscribe=OK Contiguous=0 Licenses=(null) LicensesAlloc=(null) Network=(null)
Command=(null)
WorkDir=/home/tmp1
StdErr=/home/tmp1/slurm-961.out
StdIn=/dev/null
StdOut=/home/tmp1/slurm-961.out
```
```
root@slurm-login-6fb5d948d6-5mcvp:/tmp# ll /home
total 36
drwxr-xr-x 1 root root 4096 Aug 28 06:42 ./
drwxr-xr-x 1 root root 4096 Aug 26 03:44 ../
drwxr-x--- 2 tmp1 tj-account 4096 Aug 28 07:09 tmp1/
root@slurm-login-6fb5d948d6-5mcvp:/tmp# ll /home/tmp1
total 24
drwxr-x--- 2 tmp1 tj-account 4096 Aug 28 07:09 ./
drwxr-xr-x 1 root root 4096 Aug 28 06:42 ../
-rw------- 1 tmp1 tj-account 260 Aug 28 07:09 .bash_history
-rw-r--r-- 1 tmp1 tj-account 220 Mar 31 2024 .bash_logout
-rw-r--r-- 1 tmp1 tj-account 3771 Mar 31 2024 .bashrc
-rw-r--r-- 1 tmp1 tj-account 807 Mar 31 2024 .profile
```
```
$ kubectl -n slurm exec -it pod/slurm-compute-e900-0 -- bash
root@e900-0:/tmp# ll /home/
total 8
drwxr-xr-x 1 root root 4096 Aug 26 09:26 ./
drwxr-xr-x 1 root root 4096 Aug 5 14:13 ../
```
- ### gpt-5 (2025/08/28)
你這個症狀其實已經「自白」了:
* `scontrol show job 961` 顯示
* `StdOut=/home/tmp1/slurm-961.out`
* `WorkDir=/home/tmp1`
* `BatchHost=e900-0`
* 但 **compute 節點 pod `slurm-compute-e900-0` 裡根本沒有 `/home/tmp1`(甚至 `/home/` 是空的)**
* 結果:Slurm 在 **e900-0** 要建立/寫入 `/home/tmp1/slurm-961.out` 時 I/O 失敗,工作被即時訊號殺掉 → `JobState=FAILED Reason=RaisedSignal:53(Real-time_signal_19)`;這類代碼常見成因就是 **stdout/stderr 路徑不存在或無法寫入**。([Stack Overflow][1], [bugs.schedmd.com][2], [Reddit][3])
換句話說:**login pod 有 `/home/tmp1`,compute pod 沒有**;/home 沒有做共享掛載。
---
### 立刻確認 / 取得輸出(快速救急)
1. 直接用 `srun` 偵錯(會把輸出寫回終端,不依賴檔案):
```bash
srun -A tj-account -p debug -N1 -n1 bash -lc 'hostname && whoami'
```
- 這能快速看出是否是路徑/權限問題(`sbatch` 常把這類錯誤吞掉)。([Stack Overflow][1])
- `bash -lc 'hostname && whoami'`
- `-l`:代表 **login shell**,會讓 `bash` 模擬使用者登入時的環境(讀取 `/etc/profile`、`~/.bash_profile` 等設定檔)。
- `-c`:代表 **command**,後面接要執行的字串命令。
- **指令意思**:
啟動一個登入型的 bash,再執行 `'hostname && whoami'` 這串命令。
- ### :warning: Error: couldn't chdir to `/home/tmp1': Permission denied: going to /tmp instead
```
$ srun -A tj-account -p debug -N1 -n1 bash -lc 'hostname && whoami'
[2025-08-28T07:30:02.233] error: couldn't chdir to `/home/tmp1': Permission denied: going to /tmp instead
[2025-08-28T07:30:02.235] error: couldn't chdir to `/home/tmp1': Permission denied: going to /tmp instead
/usr/bin/bash: /home/tmp1/.bash_profile: Permission denied
e900-0
tmp1
```
- **原因**:login pod 的 user id,跟 compute pod 的 user id 不一致
- **解法**:建立 user OS 帳號時,需指定相同 uid (`-u 20011`)
```
sudo useradd -m -u 20011 -s /bin/bash tmp1
```
2. 用存在的目錄當輸出與工作目錄(在 **compute** 一定存在的如 `/tmp`)再試一次:
```bash
sbatch -A tj-account -p debug \
--chdir=/tmp \
-o /tmp/slurm-%j.out -e /tmp/slurm-%j.err \
--wrap='hostname && whoami'
```
- 其中 `%j`=JobID 、 `%x`=JobName (`%x='wrap'`)。
跑完到 **compute 節點 pod** 取檔:
```bash
kubectl -n slurm exec -it pod/slurm-compute-e900-0 \
-- bash -lc 'ls -l /tmp/slurm-*.out; tail -n +1 /tmp/slurm-*.out'
```
3. 查帳務記錄(看退出碼/狀態):
```bash
sacct -j 961 --format=JobID,State,ExitCode,NodeList,StdOut,WorkDir
```
- **執行範例**:
```
$ sacct -j 961 --format=JobID,State,ExitCode,NodeList,StdOut,WorkDir
JobID State ExitCode NodeList StdOut WorkDir
------------ ---------- -------- --------------- -------------------- --------------------
961 FAILED 0:53 e900-0 /home/tmp1/slurm-%j+ /home/tmp1
961.batch CANCELLED 0:53 e900-0
961.extern COMPLETED 0:0 e900-0
```
> 遇到 Linux 訊號終止(含 real-time signals),Slurm 會把訊號號碼記錄在 ExitCode 後半部。([Slurm][4])
---
### 正解:把 **/home** 做成共享掛載(login 與 compute 都要同一份)
目前你在 login pod 看得到 `/home/tmp1`,但在 compute pod 看不到 → **/home 未共享**。要修好:
1. 準備一個可共享的儲存(NFS 或 PVC)並放使用者家目錄:
* 例:NFS 伺服器上 export `/nfs/home`,或 K8s 建 `home-pvc`(容量依需求)。
2. **同時**把這個卷掛到
* login pod:`mountPath: /home`
* 所有 slurmd 的 compute pods:`mountPath: /home`
3. 確保目錄與擁有者一致(UID/GID 在 login/compute 一致)。你的 `tmp1` 是 `uid=20011`、`gid=tj-account(1002)`,所以在共享卷上要:
```bash
# 在共享卷上(一次就好)
mkdir -p /home/tmp1
chown 20011:1002 /home/tmp1
chmod 750 /home/tmp1
```
> 具體怎麼在 Helm values 設置要看你用的 chart/版本,但原則就是:**同一個 PVC/NFS 同時掛到 login 與 slurmd pods 的 `/home`**。若暫時無法調整,至少在每個 compute pod 先建立目錄(僅測試用,pod 重建就沒了):
```bash
kubectl -n slurm exec -it pod/slurm-compute-e900-0 \
-- bash -lc 'mkdir -p /home/tmp1 && chown 20011:1002 /home/tmp1'
```
---
## 之後的最佳實務
* 明確指定輸出位置到**共享**路徑(好找又持久):
```bash
sbatch -A tj-account -p debug \
-o /home/tmp1/slurm/%x-%j.out \
-e /home/tmp1/slurm/%x-%j.err \
--wrap='hostname && whoami'
```
- 其中 `%j`=JobID 、 `%x`=JobName (`%x='wrap'`)。
(先 `mkdir -p /home/tmp1/slurm`)
* 若看到 `RaisedSignal:53` 或 `.out` 檔不見蹤影,**第一時間檢查輸出路徑是否存在/可寫、配額是否滿**。([bugs.schedmd.com][2], [HPC Community][5])
---
### 為什麼你剛剛兩邊都找不到 `slurm-961.out`?
因為 Slurm 試圖在 **e900-0** 的 `/home/tmp1/` 建檔,但 **那個路徑不存在**,工作一開始就因 I/O 問題被結束;自然就沒有任何 `.out` 可以在 login 或 compute 的 `/home` 找到。這正是社群常見案例:**stdout 目錄不存在 → 失敗且常看不到明確錯誤**。([Stack Overflow][1], [bugs.schedmd.com][2])
需要的話,我可以幫你把目前的 Helm values 片段貼過來,給你對應的 `extraVolumes/extraVolumeMounts`(或 chart 自帶的 home 共享設定)範例,直接套用到 login 與 slurmd。
[1]: https://stackoverflow.com/questions/77642713/all-slurm-jobs-fail-silently-with-exit-code-053?utm_source=chatgpt.com "All slurm jobs fail silently with exit code 0:53"
[2]: https://bugs.schedmd.com/show_bug.cgi?id=8895&utm_source=chatgpt.com "Slurm job output to non-existent directory result into silent ..."
[3]: https://www.reddit.com/r/HPC/comments/1fklwa6/need_help_slurm_error_code_053/?utm_source=chatgpt.com "Need Help SLURM Error Code 0:53 : r/HPC"
[4]: https://slurm.schedmd.com/job_exit_code.html?utm_source=chatgpt.com "Job Exit Codes - Slurm Workload Manager - SchedMD"
[5]: https://hpc-community.unige.ch/t/cpu-gpu-job-failing/3432?utm_source=chatgpt.com "CPU/GPU job failing"
<br>
{%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}