---
# System prepended metadata

title: 'Slurm / 指令 / srun, sbatch, squeue, sacct'
tags: [Slurm, squeue, Linux, Slurm /, sacct, sbatch, enroot, Slinky, SlinkyProject, HPC]

---

Slurm / 指令 / srun, sbatch, squeue, sacct
===
###### tags: `Slurm /`
###### tags: `Slurm`, `HPC`, `Linux`, `Slinky`, `SlinkyProject`, `sbatch`, `squeue`, `sacct`, `enroot`

<br>

**目錄**
- [Slurm / 指令 / srun, sbatch, squeue, sacct](#Slurm--指令--srun-sbatch-squeue-sacct)
    - [檢查當前可用資源](#檢查當前可用資源)
    - [srun 範例 – 基本操作](#srun-範例-–-基本操作)
        - [查詢主機名稱](#查詢主機名稱)
        - [進入節點互動](#進入節點互動)
        - [攜帶環境變數，傳到 srun 任務](#攜帶環境變數，傳到-srun-任務)
        - [指定特定的 node](#指定特定的-node)
        - [指定特定的 partition](#指定特定的-partition)
        - [指定特定的 feature](#指定特定的-feature)
        - [指定 CPU 需求](#指定-CPU-需求)
        - [指定記憶體需求](#指定記憶體需求)
        - [查詢 GPU 狀態](#查詢-GPU-狀態)
        - [根據 GPU 需求配置節點](#根據-GPU-需求配置節點)
        - [指定 partition (分區的任意 node) + GPU 需求](#指定-partition-分區的任意-node--GPU-需求)
        - [指定 node + GPU 需求](#指定-node--GPU-需求)
        - [指定 partition + 限定 GPU 類型 + GPU 需求](#指定-partition--限定-GPU-類型--GPU-需求)
    - [srun 範例 – 以容器方式執行](#srun-範例-–-以容器方式執行)
        - [預備知識](#預備知識)
        - [簡易範例](#簡易範例)
        - [環境變數傳遞流程 - 預備知識](#環境變數傳遞流程---預備知識)
        - [環境變數傳遞](#環境變數傳遞)
        - [完整範例](#完整範例)
        - [透過 `enroot` 指令](#透過-enroot-指令)
        - [情境1：用 `srun` 跑一個 Ubuntu 的映像檔，掛載主機的 `/work`，使用 root 身份執行](#情境1：用-srun-跑一個-Ubuntu-的映像檔，掛載主機的-work，使用-root-身份執行)
        - [srun: error: Unable to allocate resources: Invalid account or account/partition combination specified](#srun-error-Unable-to-allocate-resources-Invalid-account-or-accountpartition-combination-specified)
    - [sbatch 範例](#sbatch-範例)
        - [`hello.sbatch`](#hellosbatch)
        - [參數：`--exclusive`](#參數：--exclusive)
    - [Squeue Pending 常見的原因](#Squeue-Pending-常見的原因)
        - [PD 常見 REASON 對照表（含排查建議）](#PD-常見-REASON-對照表（含排查建議）)
        - [快速排查流程（實務版）](#快速排查流程（實務版）)
        - [提交時預防小技巧](#提交時預防小技巧)
    - [Slurm srun 如何把環境變數帶到 worker 的 task process](#Slurm-srun-如何把環境變數帶到-worker-的-task-process)
        - [Slurm `srun` 如何把環境變數帶到 worker 的 task process](#Slurm-srun-如何把環境變數帶到-worker-的-task-process84)
        - [`--export` 的本質：決定「打包進 step 的 env 清單」](#--export-的本質：決定「打包進-step-的-env-清單」)
        - [`slurmstepd` 與 task process 的環境：目標是 task，不是 stepd](#slurmstepd-與-task-process-的環境：目標是-task，不是-stepd)
        - [在 worker 上驗證「task process 實際收到哪些 env」](#在-worker-上驗證「task-process-實際收到哪些-env」)
        - [實務上還可能混進 env 的來源（容易讓人以為是「直接繼承」）](#實務上還可能混進-env-的來源（容易讓人以為是「直接繼承」）)
    - [Slurm srun 的 `--container-image=...` 參數所指的路徑，是指 login node 所在？還是 worker node 所在？](#Slurm-srun-的---container-image-參數所指的路徑，是指-login-node-所在？還是-worker-node-所在？)
        - [Q: `srun --container-image=/mnt/data/ubuntu+24.04.sqsh` 這個路徑是在 login node 還是 worker node 上解析的？](#Q-srun---container-imagemntdataubuntu2404sqsh-這個路徑是在-login-node-還是-worker-node-上解析的？)
        - [Q: 為什麼是 worker node？](#Q-為什麼是-worker-node？)
        - [Q: 我的指令失敗了，如何排查？](#Q-我的指令失敗了，如何排查？)
        - [Q: 如何解決檔案不在 worker node 上的問題？](#Q-如何解決檔案不在-worker-node-上的問題？)
        - [Q: 如果我的 `/mnt/data` 是各節點共享的（NFS mount），還需要擔心嗎？](#Q-如果我的-mntdata-是各節點共享的（NFS-mount），還需要擔心嗎？)
        - [Q: 完整的測試指令範例？](#Q-完整的測試指令範例？)
    - [sacct](#sacct)
    - [討論](#討論)
        - [如何查看 sbatch 的執行結果？](#如何查看-sbatch-的執行結果？)
        - [沒有看到 sbatch 的執行結果？](#沒有看到-sbatch-的執行結果？)

> Date: 2026-04-27 18:27:25 GMT+8

<br>

## 檢查當前可用資源
```
# 檢查當前可用資源
$ sinfo -N -l
Mon Dec 15 07:22:57 2025
NODELIST   NODES PARTITION       STATE CPUS    S:C:T MEMORY TMP_DISK WEIGHT AVAIL_FE REASON              
c2m4-0         1  cpu-set*        idle 88     2:22:2 515856        0      1 c2m4,c2m none                
c4m16-0        1  cpu-set*        idle 88     2:22:2 515856        0      1 c4m16,c4 none
```

<br>

---

<br>

## srun 範例 -- 基本操作
> 最基本指令操作

### 查詢主機名稱
```
$ srun hostname
esc8000a-1
```

<br>

### 進入節點互動
```bash
$ srun --pty bash
$ srun --pty bash -i   # -i: interactive shell, 強制成互動 (預設可忽略 -i )
```
- `--pty`: pseudo-terminal（偽終端機）
    - 與遠端執行的 shell 進行互動，輸入命令並立即看到輸出
        > 當您在 Slurm 環境中使用 `srun` 運行一個作業時，通常情況下，您的作業會作為一個批次程序在後台或遠端節點上執行，其標準輸入/輸出 (stdin/stdout) 會被重新導向到日誌檔案。
        > 
        > 添加 `--pty` 選項會強制 `srun` 為您啟動的任務（在這個例子中是 `bash -i`，一個互動式 shell）分配一個**互動式的偽終端介面**。
        > 
        > 簡而言之，--pty 將您的批次作業變成了一個可以即時對話的互動式會話。
- `-i`: interactive shell
    - 強制成互動 (預設可忽略 -i )
    - 差異：
        - `bash`：看到自己連在 pseudo-tty 上 → 自動判斷為互動
        - `bash -i`：再明講一次「我要互動」→ 結果一樣是互動

<br>

### 攜帶環境變數，傳到 srun 任務
- ### 在指令前面加 `VAR=...`
    ```bash
    XDG_CACHE_HOME=/tmp/xdg-cache srun ... your_cmd
    # 或
    env XDG_CACHE_HOME=/tmp/xdg-cache srun ... your_cmd
    ```
- ### 用 Slurm 官方的 `--export`（最推薦、也最清楚）
    > [slurm.schedmd.com](https://slurm.schedmd.com/srun.html)
    > 
    - `--export` 可以指定要傳哪些變數，或直接帶值。
    - **未指定時預設是 `--export=ALL`**
    - **範例1**：
        ```bash
        srun --export=ALL,FOO=bar,BAZ=123 ... your_cmd
        ```
        - 新增 `FOO` 和 `BAZ` 環境變數 (或蓋掉既有的值)
    - **範例2**：也可以只傳部分（會自動帶 `SLURM_*` 再加你指定的）：
        ```bash
        srun --export=EDITOR,ARG1=test ... your_cmd
        ```
        - 傳遞過去的環境變數只會包含：使用者環境中的 `EDITOR` 變數、所有 `SLURM_*` 環境變數，以及 `ARG1=test`。
    - **範例3**：不會傳遞任何使用者環境變數。
        ```bash
        srun --export=NONE ... your_cmd
        ```

<br>

### 指定特定的 node
> - **使用參數**：
>   ```
>   -w, --nodelist=hosts...     request a specific list of hosts
>   ```

```bash
$ srun -w c2m4-0 hostname
$ srun --nodelist c2m4-0 hostname
$ srun --nodelist=c2m4-0 hostname
```
- **如果節點不存在，則會出現底下錯誤**：
    ```
    $ srun -w c2m4-0 hostname
    srun: error: Unable to allocate resources: Invalid node name specified
    ```
    - **要改用請求 partition，等待資源建立**：
        ```
        $ srun -p cpu-set hostname
        srun: Requested partition configuration not available now
        srun: job 164 queued and waiting for resources
        srun: job 164 has been allocated resources
        c2m4-0
        ```
- **如果某個 partition 有多種 flavor 的 nodeset 可選擇**：
    - **無法透過 `-p` (`--partition`) + `-w` (`--nodelist`)**
        ```
        $ srun -p cpu-set -w c2m4-0 hostname
        srun: error: Unable to allocate resources: Invalid node name specified
        ```
    - **要改用：`-p` (`--partition`) + `-C` (`--constraint`)**
        > - **使用參數**：
        >   ```
        >   -C, --constraint=list       specify a list of constraints
        >   ```
        ```
        $ srun -p cpu-set --constraint c2m4
        srun: Requested partition configuration not available now
        srun: job 164 queued and waiting for resources
        srun: job 164 has been allocated resources
        c2m4-0
        ```
        - **當前行為**：
            - partition 下的多個 nodeset ，都會被喚醒 (scale: 0 -> 1)
            - c2m4-0 節點會被喚醒
            - c4m16-0 節點會被喚醒


<br>

### 指定特定的 partition
> - **使用參數**
>   ```
>   -p, --partition=partition   partition requested
>   ```
```bash
$ srun -p cpu-set hostname
```

<br>

### 指定特定的 feature
> - **使用參數**
>   ```
>   -C, --constraint=list       specify a list of constraints
>   ```

```bash
$ srun -C c2m4 hostname
$ srun -C a30 hostname
```
- ### 注意事項
    - **Partition 優先於 Feature**：
        - Slurm 先根據 partition 確定候選節點池，再用 feature 過濾
    - **預設 Partition 陷阱**：
        - `-C` 約束不會跨 partition 搜尋節點
        - 如果沒有指定 `-p`，會自動挑選 default partition
            ```
            $ scontrol show partitions 
            PartitionName=a30-set
               AllocNodes=ALL Default=NO QoS=N/A

            PartitionName=cpu-set
               AllocNodes=ALL Default=YES QoS=N/A   <-- default partition
            ```
        - 如果某個資源不在 default partition，需要明確指定 `-p <partition-name>` 才能使用
        - 沒有配對到相對應的資源，會出現底下的錯誤：
            ```
            srun: error: Unable to allocate resources: Requested node configuration is not available
            ```

<br>

### 指定 CPU 需求
> - **使用參數**
>   ```
>   -c, --cpus-per-task=ncpus   number of cpus required per task
>   ```

```bash
# 明確指定使用 1 個 core 的 CPU
$ srun -c 1 hostname
c2m4-0
c2m4-0
```
- ### 注意事項
    - **在指定 `-c 1`，會預帶參數 `-n 2`，因此會印出兩筆**
        ```
        -n, --ntasks=ntasks         number of tasks to run
        ```
        - 當你明確指定 `--cpus-per-task` 時，Slurm 會**重新評估** `--ntasks` 的預設值
        - 在某些 Slurm 配置下，明確指定 `-c` 會觸發**預設 ntasks=2** 的行為
            - 可能根據節點配置（Sockets=2）重新計算預設的 `--ntasks` 值
        - 結果：執行 **2 次** `hostname`（因為 ntasks=2）
    - **明確設定預設 ntasks 行為：**
        1. 編輯 `slurm.conf`，明確設定預設 ntasks 行為：
           ```conf
           # 在 slurm.conf 中加入或修改
           DefCpuPerTask=1
           ```
        2. 重新載入 Slurm 配置：
           ```shell
           scontrol reconfigure
           ```
        3. 驗證修正結果：
           ```shell
           srun -c 1 hostname  # 應該只輸出一次
           ```

<br>

### 指定記憶體需求
> - **使用參數**
>   ```
>   --mem=MB                minimum amount of real memory
>   ```

```bash
# 明確指定使用 1G 的記憶體
$ srun --mem=1G hostname
$ srun --mem-per-cpu=1G hostname
```

<br>

### 查詢 GPU 狀態
```
$ srun nvidia-smi
```

<br>

### 根據 GPU 需求配置節點
```
$ srun --gres='gpu:8' nvidia-smi -L    
```

<br>

### 指定 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>

---

<br>

## srun 範例 -- 以容器方式執行
> 跑 [pyxis](https://github.com/NVIDIA/pyxis) 容器

### 預備知識
- `--container-*` 參數，由 pyxis plugin 提供，例如：
    - `--container-image=docker://ubuntu:24.04`
    - `--container-env=NAME[,NAME...]`

<br>

### 簡易範例
> https://github.com/SlinkyProject/slurm-operator/issues/41

- ### 不需要 GPU 情況
    ```bash
    # 不需要 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)
    
    $ srun -p cpu-set -C c4m16 --container-image ubuntu:24.04 hostname
    pyxis: importing docker image: ubuntu:24.04
    pyxis: imported docker image: ubuntu:24.04
    c4m16-0   # 要等 7 分鐘？
    ```
- ### 需要 GPU 情況
    ```bash
    # 需要 GPU 情況：
    $ srun --container-image nvcr.io/nvidia/pytorch:23.10-py3 --pty bash
    ```

<br>

### 環境變數傳遞流程 - 預備知識


<br>

### 環境變數傳遞
- ### 環境變數傳遞過程
    ```
    login 節點
    -> (透過 `--export`)
      -> Slurm 計算節點
        -> (透過 `--container-env`)
          -> 容器內部
    ```
- ### 階段一：login 節點 -> 計算節點/(Slurm 世界：slurm node)/(Slinky 世界: worker pod)
    > Slurm 會決定要把你的任務丟到哪幾台計算節點。此時，`--export` 參數決定了哪些變數要從你現在的螢幕前傳到那幾台計算節點上。
    - 由 `--export=...` 控制
        - 預設值：`--export=ALL`
    - **優先權**：login 節點 > `--export=...`
        ```
        export XYZ=123
        srun --export=XYZ=789 ... -> 最終結果：XYZ=123
        ```
        - 有定義過，就不會再重新賦值
        - If "ALL" is specified, then all user environment variables will be loaded and will take precedence over any explicitly given environment variables. ([slurm.schedmd.com](https://slurm.schedmd.com/sbatch.html))
- ### 階段二：計算節點/(Slurm 世界：slurm node)/(Slinky 世界: worker pod) -> 容器
    - 由 `--container-env=NAME[,NAME...]` 控制
        - 預設值：`--container-env=all`
        - pyxis 的定義：「**只接受變數名稱**」，不是用來做 `NAME=value` 指派的 ([GitHub](https://github.com/NVIDIA/pyxis?tab=readme-ov-file#usage))
    - **優先權**：
        - **預設**：容器環境變數 > 計算節點環境變數
            (避免容器內的環境變數，被外面的世界影響)
        - **使用 `--container-env=NMAE1[,NAME2...]` 參數**：計算節點環境變數 `NAME` > 容器環境變數
            (明確定義：由外面世界的環境變數，蓋掉容器內的環境變數)
    
- ### 範例：在階段一(login 節點 -> 計算節點)：控制**傳入**/**不傳入**
    - ### `--export` 不使用 `ALL` 和 `NONE`
        ```
        $ export XYZ=xyz
        $ srun --export=XYZ=abc \
            /usr/bin/bash -c 'env | egrep "XYZ|PATH" || true'
        XYZ=abc
        ```
        ```
        $ export XYZ=xyz
        $ srun --export=XYZ=abc,ALL \
            /usr/bin/bash -c 'env | egrep "XYZ|PATH" || true'
        PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
        XYZ=xyz
        ```
    - ### `--export=NONE` (不傳入任何環境變數)
        ```
        $ srun --export=NONE,XYZ=789 \
            /usr/bin/bash -c 'env | egrep "XYZ|PATH" || true'
        (沒有任何結果)
        $ srun --export=XYZ=789,NONE \
            /usr/bin/bash -c 'env | egrep "XYZ|PATH" || true'
        XYZ=789
        ```
        ```
        # 階段一：--export=NONE (不傳入任何環境變數)
        $ export XYZ=xyz
        $ srun \
            --export=NONE \
            --container-image=docker://ubuntu:24.04 \
            --container-env=XYZ \
            --pty bash -c 'env | grep XYZ'
        _SLURM_SPANK_OPTION_pyxis_container_env=XYZ
        ```
    - ### `--export=ALL` (傳入所有環境變數)
        ```
        # 階段一：--export=ALL
        $ export XYZ=xyz
        $ srun \
            --export=ALL \
            --container-image=docker://ubuntu:24.04 \
            --container-env=XYZ \
            --pty bash -c 'env | grep XYZ'
        _SLURM_SPANK_OPTION_pyxis_container_env=XYZ
        XYZ=xyz
        ```
        ```
        # 階段一：不存在 XYZ 環境變數
        $ unset XYZ
        $ srun \
            --export=ALL \
            --container-image=docker://ubuntu:24.04 \
            --container-env=XYZ \
            --pty bash -c 'env | grep XYZ'
        _SLURM_SPANK_OPTION_pyxis_container_env=XYZ
        ```
        ```
        # 階段一：不使用 export 關鍵字
        $ XYZ=xyz
        $ XDG_CACHE_HOME=/tmp srun \
            --export=ALL \
            --container-image=docker://ubuntu:24.04 \
            --container-env=XYZ \
            --pty bash -c 'env | grep XYZ'
        _SLURM_SPANK_OPTION_pyxis_container_env=XYZ
        ```
- ### 範例：在階段二(計算節點 -> 容器)：控制**傳入**/**不傳入**
    - ### 觀察 ABC 和 XYZ
        ```
        # 階段一：--container-env=ABC  (只列 ABC，不列 XYZ)
        $ export ABC=abc
        $ export XYZ=xyz
        $ srun \
            --export=ALL,PATH=/this/is/login/path,FOO=bar \
            --container-image=docker://ubuntu:24.04 \
            --container-env=ABC \
            --pty bash -c 'env | egrep "ABC|XYZ|PATH|FOO"'

        _SLURM_SPANK_OPTION_pyxis_container_env=ABC
        FOO=bar
        PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin (容器內的值)
        XYZ=xyz  (依舊有輸出 XYZ)
        ABC=abc
        ```
    - ### 觀察 PATH，並比較優先權：login 節點 vs `--export=PATH`
        ```
        # [case 1A] 階段一：--export=ALL,PATH + --container-env=ABC,PATH
        $ export ABC=abc
        $ export XYZ=xyz
        $ srun \
            --export=ALL,PATH=/this/is/login/path,FOO=bar \
            --container-image=docker://ubuntu:24.04 \
            --container-env=ABC,PATH \
            --pty bash -c 'env | egrep "ABC|XYZ|PATH|FOO"'

        _SLURM_SPANK_OPTION_pyxis_container_env=ABC,PATH
        FOO=bar
        PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin (為 login 節點內的值)
        XYZ=xyz
        ABC=abc
        ```
        ```
        # [case 1B] 階段一：--export=PATH,ALL + --container-env=ABC,PATH
        $ export ABC=abc
        $ export XYZ=xyz
        $ srun \
            --export=PATH=/this/is/login/path,ALL,FOO=bar \
            --container-image=docker://ubuntu:24.04 \
            --container-env=ABC,PATH \
            --pty bash -c 'env | egrep "ABC|XYZ|PATH|FOO"'

        _SLURM_SPANK_OPTION_pyxis_container_env=ABC,PATH
        FOO=bar
        PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin (依舊是 login 節點內的值)
        XYZ=xyz
        ABC=abc
        ```
        ```
        # [case 2] 階段一：--export=PATH,ALL + --container-env=ABC,PATH
        $ export ABC=abc
        $ export XYZ=xyz
        $ PATH=/this/is/login/path1 \
            /usr/bin/srun \
            --export=PATH=/this/is/login/path2,ALL,FOO=bar \
            --container-image=docker://ubuntu:24.04 \
            --container-env=ABC,PATH \
            --pty bash -c 'env | egrep "ABC|XYZ|PATH|FOO"'

        [2025-12-26T08:12:10] error: execve(): bash: No such file or directory
        srun: error: c2m4-0: task 0: Exited with exit code 2
        ```


<br>

---

### 完整範例
```
srun -p cpu-set -C c4m16 \
    --container-image ubuntu:24.04 \
    --container-mounts=/work:/mnt/work \
    --container-remap-root \
    --pty bash
```
- `/work`: 當前 user 在 login pod 中的路徑，不是 user 所在的 host path
    ```
    
    ```

<br>

---

### 透過 `enroot` 指令
```
/home/tj_tsai$ export XDG_CACHE_HOME=/home/tj_tsai

tj_tsai@slurm-login-slinky-597f646d-m4szz:/home/tj_tsai$ enroot import -o ubuntu-24.04.sqsh 'docker://ubuntu:24.04'
[INFO] Querying registry for permission grant
[INFO] Authenticating with user: <anonymous>
[INFO] Authentication succeeded
[INFO] Fetching image manifest list
[INFO] Fetching image manifest
[INFO] Found all layers in cache
[INFO] Extracting image layers...

100% 1:0=0s 20043066d3d5c78b45520c5707319835ac7d1f3d7f0dded0138ea0897d6a3188

[INFO] Converting whiteouts...

100% 1:0=0s 20043066d3d5c78b45520c5707319835ac7d1f3d7f0dded0138ea0897d6a3188

[INFO] Creating squashfs filesystem...

Could not create destination file: Permission denied
```


<br>

---

### 情境1：用 `srun` 跑一個 Ubuntu 的映像檔，掛載主機的 `/work`，使用 root 身份執行
- ### gpt-5.2 (2025/12/15)
    情境大概像這樣：
    - 用 `srun` 跑一個 Ubuntu 的映像檔（例如 enroot / OCI image）
    - 把主機的 `/work` 掛進容器裡（通常也是掛到 `/work`）
    - 用 `--container-remap-root` 讓容器內變成 root 身份

    典型指令長這樣（依你實際的 image 名稱調整）：

    ```bash
    srun \
      --container-image=docker.io/library/ubuntu:22.04 \
      --container-mounts=/work:/work \
      --container-remap-root \
      --pty /bin/bash
    ```

    ### 說明：
    - ### `--container-image=...`
         指定要用的容器映像檔（例如 Ubuntu）。
    - ### `--container-mounts=/work:/work`
        格式是：`<host_path>:<container_path>`
        這裡是把「主機的 `/work`」掛到「容器裡的 `/work`」。
        - 如果你想掛到別的路徑，例如 `/mnt/work`，就改成：
            ```bash
            --container-mounts=/work:/mnt/work
            ```
        - 如果你有多個目錄要掛載，可以用逗號分隔：
            ```bash
            --container-mounts=/work:/work,/home:/home
            ```
    - ### `--container-remap-root`
        啟用 root remap，讓你在容器內看到的 UID/GID 是 root（0），但外面仍然用實際的 user 身份去存取檔案（配合 enroot 的 user-mapped root 機制）。

    ### 執行範例
    ```bash
    $ srun -p cpu-set -C c4m16 \
        --container-image ubuntu:24.04 \
        --container-mounts=/work:/mnt/work \
        --container-remap-root \
        --pty bash
    srun: Required node not available (down, drained or reserved)
    srun: job 357 queued and waiting for resources
    srun: job 357 has been allocated resources
    pyxis: importing docker image: ubuntu:24.04
    pyxis: imported docker image: ubuntu:24.04
    # 等待 7-8 分鐘

    root@c4m16-0:/# 
    root@c4m16-0:/# cd /mnt/work/

    root@c4m16-0:/mnt/work# id
    uid=0(root) gid=0(root) groups=0(root)

    root@c4m16-0:/mnt/work# cd root/
    bash: cd: root/: Permission denied

    ```

<br>

### srun: error: Unable to allocate resources: Invalid account or account/partition combination specified
- ### 設定1
    ```conf
    AccountingStorageEnforce: associations,limits,safe
    EnforcePartLimits: ALL
    ```
    ```bash
    $ srun -p c8m64gt1 --pty bash
    srun: error: Unable to allocate resources: Invalid account or account/partition combination specified
    ```
- ### `us.anthropic.claude-sonnet-4-5-20250929-v1:0` (2026/01/19)
    根據錯誤訊息 `Invalid account or account/partition combination specified` 和您提供的設定，問題的根本原因是：

    ### 主要原因參數

    **`AccountingStorageEnforce: associations,limits,safe`**

    這個參數啟用了帳戶關聯（associations）強制驗證，要求：
    1. 使用者必須屬於某個有效的 **Account**
    2. 該 Account 必須與指定的 **Partition** 有關聯
    3. 必須在 Slurm 資料庫中預先建立這些關聯

    ### 為什麼會失敗

    當您執行 `srun -p c8m64gt1 --pty bash` 時：
    - 您沒有使用 `-A` 或 `--account` 參數指定帳戶
    - Slurm 嘗試使用您的預設帳戶（或無帳戶）
    - 由於啟用了 `associations` 強制驗證，Slurm 檢查發現：
      - 您的使用者沒有與 `c8m64gt1` partition 關聯的有效帳戶
      - 或者根本沒有設定帳戶關聯

    ### 解決方案

    #### 方案 1：建立帳戶關聯（建議）

    在 controller 上執行：

    ```bash
    # 建立帳戶
    sacctmgr add account <account_name>

    # 將使用者加入帳戶並關聯到 partition
    sacctmgr add user <username> account=<account_name> partition=c8m64gt1

    # 檢查關聯
    sacctmgr show assoc where user=<username>
    ```

    然後使用帳戶提交任務：
    ```bash
    srun -A <account_name> -p c8m64gt1 --pty bash
    ```

    #### 方案 2：移除強制驗證（不建議）

    如果不需要帳戶管理功能，可以調整設定：

    ```yaml
    # 移除 associations 驗證
    AccountingStorageEnforce: limits,safe
    # 或完全移除此行
    EnforcePartLimits: ALL
    ```

    ### 輔助參數說明

    `EnforcePartLimits: ALL` 本身不是直接原因，但它會：
    - 強制執行 partition 層級的資源限制
    - 配合 `AccountingStorageEnforce` 一起作用

    **結論**：核心問題參數是 `AccountingStorageEnforce: associations`，需要建立使用者與 partition 的帳戶關聯才能使用。
    
- ### 設定2：`AllowAccounts=ALL`
    ```
    $ scontrol show partition
    PartitionName=c8m64gt1
       AllowGroups=ALL AllowAccounts=ALL AllowQos=ALL
       AllocNodes=ALL Default=NO QoS=N/A
       DefaultTime=NONE DisableRootJobs=NO ExclusiveUser=NO ExclusiveTopo=NO GraceTime=0 Hidden=NO
       MaxNodes=UNLIMITED MaxTime=UNLIMITED MinNodes=0 LLN=NO MaxCPUsPerNode=UNLIMITED MaxCPUsPerSocket=UNLIMITED
       NodeSets=c8m64gt1
       Nodes=c8m64gt1-0
       PriorityJobFactor=1 PriorityTier=1 RootOnly=NO ReqResv=NO OverSubscribe=NO
       OverTimeLimit=NONE PreemptMode=OFF
       State=UP TotalCPUs=88 TotalNodes=1 SelectTypeParameters=NONE
       JobDefaults=(null)
       DefMemPerNode=UNLIMITED MaxMemPerNode=UNLIMITED
       TRES=cpu=8,mem=515856M,node=1,billing=8,gres/gpu=1
    ```
    有設定 `AllowAccounts=ALL`，這樣還是會限制有關聯？

- ### `us.anthropic.claude-sonnet-4-5-20250929-v1:0` (2026/01/19)

    是的，即使設定了 `AllowAccounts=ALL`，當您啟用 `AccountingStorageEnforce: associations` 時，**仍然會要求使用者至少要加入到某個帳戶（account）**。

    ### 原因解析

    - ### `AllowAccounts=ALL` 的作用
        這個參數的意思是：
        - 允許**所有已建立的帳戶**使用這個 partition
        - 但**前提是使用者必須屬於某個帳戶**
        - 它不會自動繞過 `AccountingStorageEnforce: associations` 的檢查

    - ### `AccountingStorageEnforce: associations` 的作用
        這個參數強制要求：
        1. ✅ 使用者必須在 Slurm 資料庫中有帳戶關聯記錄
        2. ✅ 使用者必須明確屬於某個 Account
        3. ✅ 該 Account 必須有權限使用目標 partition

    ### 驗證方式

    檢查目前的使用者關聯狀態：

    ```bash
    # 查看使用者是否有帳戶關聯
    sacctmgr show assoc where user=$USER format=user,account,partition

    # 查看所有帳戶
    sacctmgr show account
    ```

    如果輸出為空或沒有相關記錄，就會出現您遇到的錯誤。

    ### 解決方案比較

    - ### 選項 1：建立最小帳戶關聯（推薦）

        保持安全控制，建立基本的帳戶結構：

        ```bash
        # 建立預設帳戶
        sacctmgr -i add account default_account Description="Default Account"

        # 將使用者加入預設帳戶（不限制 partition）
        sacctmgr -i add user $USER account=default_account

        # 這樣就可以使用了
        srun -p c8m64gt1 --pty bash
        ```

    - ### 選項 2：關閉關聯強制驗證

        如果確定不需要帳戶管理功能：

        ```yaml
        # 移除 associations，只保留資源限制驗證
        AccountingStorageEnforce: limits,safe
        EnforcePartLimits: ALL
        ```

    - ### 選項 3：完全關閉強制驗證

        最寬鬆的設定：

        ```yaml
        # 完全移除帳戶驗證（不推薦生產環境）
        # AccountingStorageEnforce: ""
        EnforcePartLimits: ALL
        ```

    ### 參數相互關係

    ```
    AllowAccounts=ALL (partition 層級)
        ↓ 允許所有已存在的帳戶
        ↓
    AccountingStorageEnforce: associations (全域層級)
        ↓ 強制使用者必須有帳戶關聯
        ↓
    結果：使用者必須屬於「某個」帳戶，才能使用任何 partition
    ```

    **結論**：`AllowAccounts=ALL` 不會繞過帳戶關聯要求，您需要：
    1. 移除 `associations` 驗證（簡單但不安全）
    2. 或建立基本的使用者-帳戶關聯（推薦）

<br>

---

<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>

---

### 參數：`--exclusive`
- ### gpt-5.2 (2025/12/17)
    `--exclusive` 的意思是：**這個 job 拿到的每一個 Slurm node，都要「整台/整個 node 專屬給這個 job 用」**，不允許其他 job 在同一個 node 上再分到任何資源（CPU/記憶體/GPU 等）。

    你可以把它想成：**把 node 鎖起來，只給我這個作業用**。

    ### 對 user 來說有什麼用？

    * **避免跟別人的 job 共用同一台 node**（減少互相干擾、效能更穩定）
    * **需要完整獨占硬體時**（例如怕別人也用同一張 GPU、或你要做效能測試/benchmark）
    * **確保你的工作不會被「塞在同一台 node 的剩餘空間」**（有些系統允許多個 job 共享同一個 node，只要資源還夠）

    ### 代價 / 副作用

    * **更難排到**：因為你要求「整台」而不是「一部分」，排隊時間可能變長
    * **可能浪費資源**：你只用到 node 的一小部分，也會把整台卡住，別人不能用

    ### 什麼時候可能「看起來沒差」？

    * 如果你們的 Slurm node 本來就設定成不共享（例如 partition / node 預設就是 exclusive），那你加不加 `--exclusive` 可能結果一樣。
    * 在某些 Slurm-on-K8s 設計裡，一個 Slurm compute “node” 本質上就是一個 worker pod（資源切得很死），共享本來就不常見；但如果一個 Slurm node/pod 仍可能承載多個 job step（或資源有剩可分配），`--exclusive` 仍然有意義。

    ### 小例子

    * 沒有 `--exclusive`：你拿到 3 個 nodes，但每個 node 可能還會再跑別人的 job（只要資源夠）。
    * 有 `--exclusive`：你拿到的 3 個 nodes 會被標記成「被你獨占」，別人不能再分到上面。

    如果你願意貼一下 `scontrol show node <其中一個node>` 的輸出（看 `State`、`AllocTRES`、`CfgTRES`、`Partitions`），我也可以告訴你在你們這套環境下 `--exclusive` 是否真的會改變行為、還是其實本來就獨占了。


<br>

---
---

<br>

## Squeue Pending 常見的原因
> by gpt-5 (2025/09/15)

下面是把 **`squeue` 顯示為 `PENDING (PD)`** 時最常見的 `REASON` 做成速查表（含意思、常見成因、快速檢查、解法）。
建議常用顯示格式：

```bash
# 一眼看到原因、QoS、需求與開始時間
squeue --states=PD -o "%.18i %.9P %.10T %10Q %20R %12S %8M %4C %8m %b %E"
# 或看單一工作完整細節
scontrol show job <JOBID> -o
```

---

### PD 常見 REASON 對照表（含排查建議）

| REASON | 代表意思（為什麼在等） | 常見成因 | 快速檢查 | 可能解法 |
| ------ | ------------------ | -------- | ------ | -------- |
| **Resources**| 等資源釋放（CPU/記憶體/GPU/節點不足） | 叢集被其他工作占滿、你要的 TRES 太大| `sinfo -S+%e`、`sinfo -Nel` 看可用節點/TRES| 降低資源需求（`--cpus-per-task/--mem/--gpus`/`--nodes`）、改較空的 partition、放寬 `--constraint`、分割成多個小 job |
| **Priority** | 有更高優先序的工作在前面| Fairshare/age/QoS 造成優先度較低| `sprio -j <JOBID>`、`scontrol show job` 看 `Priority`| 等排程、改用較高 **QoS**（`-q`），或請管理員調整優先序/權重 |
| **Dependency** | 設了相依（`--dependency`）尚未滿足| 等前置 job 結果或狀態| `scontrol show job` 看 `Dependency=`| 等完成／移除依賴：`scontrol update JobId=<id> Dependency=`|
| **PartitionConfig**| 要求不符合 partition 設定| 要 GPU 但該 partition 無 GPU、超過 partition 限制 | `scontrol show partition <P>` 看 `Allow*`/`DefaultTime/MaxTime/State`/Features| 改到合適 partition（`-p`）、調整需求、移除不相容 `--constraint/--gres`|
| **BeginTime**| 設了延後啟動（`--begin=...`） | 手動排程到未來、或系統規則造成| `squeue -j <id> -o "%i %S %R"` | 立刻開始：`scontrol update JobId=<id> BeginTime=now`|
| **JobHeldUser**| 你把 job hold 住 | 用了 `--hold` 或 `scontrol hold`| `scontrol show job` 看 `Hold` | 解除：`scontrol release <id>` |
| **JobHeldAdmin** | 管理員把 job hold | 管理維護、配額控管| 同上 | 找管理員解除或等待|
| **Licenses** | 需要軟體授權數量不足| 併發超過 license 數 | `scontrol show licenses`（若有啟用） | 等待、降低併發、改時段、增購/調整 license|
| **ReqNodeNotAvail**| 指定的節點目前不可用| 指名 `--nodelist`/`--constraint` 的節點 **down/drain/reserved** | `sinfo -R`、`sinfo -Nel` 看節點狀態| 移除指定、放寬條件、等節點恢復|
| **QOSMaxCpuPerUser** | 你的 **QoS** 對「每使用者 CPU 上限」已滿 | 同一 QoS 下開太多 CPU| `sacctmgr show qos <qos> format=Name,MaxTRESPU,MaxCPUsPU,GrpCPUs`| 降需求、改 QoS（`-q`）、請調高 QoS 限制 |
| **QOSMaxJobsPerUser**| QoS 對「每使用者同時工作數」達上限 | 同一 QoS 下開太多 job| `sacctmgr show qos <qos> format=Name,MaxJobsPU`| 減少併發、改 QoS、讓既有 job 跑完|
| **QOSGrpCpuLimit** | **QoS 群組級 CPU 上限**已滿| 同組別的人把 CPU 用滿| `sacctmgr show qos <qos> format=Name,GrpCPUs`| 等待、改 QoS、請調高群組上限 |
| **QOSGrpNodeLimit**| **QoS 群組級節點數上限**已滿| 同組別併發太多節點| `sacctmgr show qos <qos> format=Name,GrpNodes` | 同上 |
| **AssociationJobLimit**| 你的帳號關聯（User/Account/Partition）**job 數上限**到頂 | assoc 上設了 MaxJobs/MaxSubmit| `sacctmgr list assoc where user=<u> format=User,Account,Partition,MaxJobs,MaxSubmit` | 降併發、改帳號/partition、請調整 assoc 限制 |
| **AssociationCpuLimit**| 關聯的 **CPU 上限**已滿| 同上 | `sacctmgr list assoc ... format=...,MaxCPUs,MaxTRESPerUser,MaxTRESPerJob`| 同上或降需求 |
| **AssociationNodeLimit** | 關聯的 **節點數上限**已滿 | 同上 | 同上 | 同上 |
| **AssocGrpJobsLimit**| 關聯群組層級 **job 數上限**已滿| team/project 併發太多| `sacctmgr list assoc ... format=...,GrpJobs` | 等待、請調高、分批提交|

> 小提醒：`sacctmgr show`/`list assoc` 通常需要管理權限；若無權限，可改跟管理員確認你所在的 **Account / QoS / Partition** 配額。

---

### 快速排查流程（實務版）

1. **先看原因**

   ```bash
   squeue -j <JOBID> -o "%i %T %Q %R %S %b %E"
   scontrol show job <JOBID> -o
   ```
2. **看 partition 與節點狀態**（是否 down/drain/資源足夠）

   ```bash
   sinfo -s
   sinfo -Nel | grep -E "down|drain|alloc|mix|idle"
   sinfo -p <PART> -o "%P %a %l %c %m %G %f"
   ```
3. **檢查你要的資源是否太大/條件過嚴**

   * 有沒有過度限定 `--constraint/--nodelist/--gres`？
   * CPU/MEM/GPU 是否能降低或改多 job 拆分？
4. **若顯示 Priority**：

   ```bash
   sprio -j <JOBID>
   ```

   等公平排程或改 QoS / 請調權重。
5. **若顯示 QoS / Association 限制**：
   檢查 QoS 與 assoc 的 Max/Grp 限制（見上表），調整 QoS、降需求、或請管理員調整。
6. **若是 Dependency / BeginTime / Hold**：

   * `Dependency`：確認前置 job 狀態，或移除相依。
   * `BeginTime`：`scontrol update JobId=<id> BeginTime=now`。
   * `Hold`：`scontrol release <id>`（Admin hold 需管理員）。

---

### 提交時預防小技巧

* 提前用 `sinfo` 選對 partition、確認有支援的 GPU/Features。
* 避免過度狹隘的 `--constraint/--nodelist`。
* 合理切分工作：大量 CPU/GPU 需求可拆成多個較小 job。
* 對 license 服務軟體（如商業求解器）避開尖峰時段或排程到夜間（`--begin=22:00`）。
* 用合適的 **QoS**（`-q`）對應你的使用情境與配額。

<br>

---
---

<br>

## Slurm srun 如何把環境變數帶到 worker 的 task process
> by gpt-5.2 (2025/12/30)

### Slurm `srun` 如何把環境變數帶到 worker 的 task process

在 Slurm 的執行模型裡，「環境變數的傳遞」不是 worker 端去讀 login pod 的環境，而是 **login pod 上的 `srun` 先把這次要傳的環境變數清單整理好**，再把它作為 **step 啟動參數的一部分**，透過 Slurm 的 RPC 送到執行端，最後由 `slurmstepd` 在 `execve()` 啟動 task 時把這份 env 注入到 process。

#### 一個更精準的端到端流程（login pod 執行 `srun`）

```text
(login pod)
  你的 shell 環境
      │
      ▼
  srun 依 --export 規則，組出「這次 step 要用的環境變數清單」
      │   （打包：包含你指定要傳的 key/value）
      ▼
(slurmctld / controller)
  分配節點 + 協調建立 step（RPC 協調與狀態管理）
      │
      ▼
(worker pod)
  slurmd 收到 launch step/task 的請求
      │
      ▼
  slurmstepd (root) 建立 step、設定 cgroup/limits、準備執行環境
      │
      ▼
  execve() 啟動你的 task process（用你的 UID），並把那份 env 清單帶進去
```

這裡的**關鍵角色**就是：**`srun`（client）負責決定「要傳哪些環境變數」**；worker 端只是接收並在啟動 task 時使用。

---

### `--export` 的本質：決定「打包進 step 的 env 清單」

`--export` 影響的是 `srun`（或 `sbatch`/`salloc`）在提交 step/job 時，**要不要把目前 shell 的哪些環境變數，打包成一份 env list**。

常見直覺落差在於：

* 你在 login pod 的 shell 裡 `export FOO=bar`
  **不等於** worker 端會「自己去讀」到這個變數
* 真正會被帶過去的是：`srun` **當下**依 `--export` 規則整理出的那一份清單

所以環境變數的傳遞是「一次性快照」：

* `srun` 打包當下的 env 清單 → 送出 → worker 啟動 task
* 之後 login 那邊再改 env，不會回頭影響已啟動的 step/task

---

### `slurmstepd` 與 task process 的環境：目標是 task，不是 stepd

在 worker pod 端，`slurmstepd`（通常是 root）負責把 step 的執行環境準備好，最後 `execve()` 啟動你的 task process（用你的 UID）。要注意兩個常被誤會的點：

1. **`--export` 的目標是「你的 task process」**
   `slurmstepd` 自己（那個 root 的 daemon）不需要、也不一定會擁有你那份 `--export` 的 env。

2. **worker 端不會回去讀 login pod 的 env**
   傳遞完全依賴 `srun` 當下打包的清單；不是動態同步。

---

### 在 worker 上驗證「task process 實際收到哪些 env」

最直接、最不會被 shell/啟動腳本誤導的方法，是看該 task process 的 `/proc/<pid>/environ`。

常用做法（示意）：

* 先在 `srun` 內執行一個會停住的程式（例如 `sleep`），拿到 PID
* 在 worker 上讀：

  * `/proc/<pid>/environ`（NUL 分隔）
  * 或在 task 裡直接 `env | sort` 印出來

這能確認「step 啟動時被注入的 env」到底有哪些，特別適合你在追的那種 **XDG/ENROOT 之類變數干擾**問題。

---

### 實務上還可能混進 env 的來源（容易讓人以為是「直接繼承」）

即使你把 `--export` 控得很嚴，task 的最終環境仍可能包含其他來源的影響，例如：

* Slurm 自動加的 `SLURM_*` 相關變數
* 系統/容器映像內的預設環境（例如 `/etc/profile`, `bash -lc` 行為）
* Prolog/Epilog、SPANK plugins（若有啟用）
* 站台設定或 wrapper（有時候是你們自訂的 entrypoint/啟動腳本）

所以排查時，建議以「task process 的 `/proc/<pid>/environ`」作為準繩，再回推到底是哪一段把變數放進去：是 `srun --export` 打包的、還是 worker 端啟動鏈（shell/profile/scripts/plugins）額外加的。

<br>

---
---

<br>

## [srun] `--container-image` 路徑解析

> `global.anthropic.claude-opus-4-6-v1` (2026/04/27)

### Q: `srun --container-image=/mnt/data/ubuntu+24.04.sqsh` 這個路徑是在 login node 還是 worker node 上解析的？

**A: Worker node。** 檔案必須存在於執行任務的 compute node 上，不是你下 `srun` 指令的 login node。

---

### Q: 為什麼是 worker node？

**A:** NVIDIA Pyxis（提供 `--container-image` 的 Slurm SPANK plugin）的架構設計：

1. **Login node（`S_CTX_LOCAL`）**：只負責解析和傳遞參數，不做任何檔案 I/O
2. **Worker node（`S_CTX_REMOTE`）**：`slurmstepd` 進程實際讀取 `.sqsh` 檔案並呼叫 `enroot create`

原始碼中路徑解析（`pyxis_slurmstepd.c`）：
```c
if (strspn(context.args->image, "./") > 0) {
    context.container.squashfs_path = strdup(context.args->image);
}
```
此段程式碼只在 worker node 上執行。Pyxis **不會**自動將檔案從 login node 傳輸到 worker node。

---

### Q: 我的指令失敗了，如何排查？

**A:** 確認檔案是否存在於目標 worker node：

```bash
# 檢查 worker node 上是否有該檔案
srun -p a30-set -w c8m64gt1-0 ls -l /mnt/data/ubuntu+24.04.sqsh
```

如果回傳 `No such file or directory`，代表 `.sqsh` 檔只在 login node 上，尚未存在於 worker node。

---

### Q: 如何解決檔案不在 worker node 上的問題？

| 方法 | 指令範例 | 適用情境 |
|------|----------|----------|
| 放到共享儲存 | `cp /mnt/data/ubuntu+24.04.sqsh /shared/containers/` | 叢集有 NFS/Lustre 共享目錄 |
| 手動複製到 worker | `scp /mnt/data/ubuntu+24.04.sqsh c8m64gt1-0:/mnt/data/` | 單次測試、快速驗證 |
| 改用 Docker URI | `--container-image=docker://ubuntu:24.04` | 不想管路徑，讓 Pyxis 自動 pull 並快取 |

---

### Q: 如果我的 `/mnt/data` 是各節點共享的（NFS mount），還需要擔心嗎？

**A: 不需要。** 如果 `/mnt/data` 是共享檔案系統，login node 和 worker node 看到的是同一份檔案，指令可以直接運作。可用以下方式確認：

```bash
# 確認 /mnt/data 是否為共享掛載
srun -w c8m64gt1-0 df /mnt/data
```

若顯示的裝置與 login node 上 `df /mnt/data` 相同，表示是共享的。

---

### Q: 完整的測試指令範例？

```bash
# 在 worker node 上用 ubuntu+24.04.sqsh 容器執行 ls /
time srun -p a30-set -w c8m64gt1-0 --container-image=/mnt/data/ubuntu+24.04.sqsh ls /
```


<br>

---
---

<br>

## sacct
> - 用來顯示 job 的執行狀況
> - 只能看到自己帳號內的 job

```
$ sacct
JobID           JobName  Partition    Account  AllocCPUS      State ExitCode 
------------ ---------- ---------- ---------- ---------- ---------- -------- 
12           nvidia-smi         tn                     1    RUNNING      0:0 
12.extern        extern                                1    RUNNING      0:0 
12.0         nvidia-smi                                1    RUNNING      0:0 
13           nvidia-smi         tn                     0 CANCELLED+      0:0 
14           nvidia-smi         tn                     1  COMPLETED      0:0 
14.extern        extern                                1  COMPLETED      0:0 
14.0         nvidia-smi                                1  COMPLETED      0:0
```

**進一步查詢 job 12**
```
$ scontrol show job 12
JobId=12 JobName=nvidia-smi
   UserId=tj_tsai(10001) GroupId=gid10000(10000) MCS_label=N/A
   Priority=1 Nice=0 Account=(null) QOS=normal
   JobState=RUNNING Reason=None Dependency=(null)
   Requeue=1 Restarts=0 BatchFlag=0 Reboot=0 ExitCode=0:0
   RunTime=01:03:07 TimeLimit=UNLIMITED TimeMin=N/A
   SubmitTime=2025-10-16T08:41:08 EligibleTime=2025-10-16T08:41:08
   AccrueTime=Unknown
   StartTime=2025-10-16T08:41:08 EndTime=Unknown Deadline=N/A
   SuspendTime=None SecsPreSuspend=0 LastSchedEval=2025-10-16T08:41:08 Scheduler=Main
   Partition=tn AllocNode:Sid=slurm-login-slinky-765f696cc5-5vkfs:1362
   ReqNodeList=esc8000a-0 ExcNodeList=(null)
   NodeList=esc8000a-0
   BatchHost=esc8000a-0
   StepMgrEnabled=Yes
   NumNodes=1 NumCPUs=1 NumTasks=1 CPUs/Task=1 ReqB:S:C:T=0:0:*:*
   ReqTRES=cpu=1,mem=4G,node=1,billing=1,gres/gpu=1
   AllocTRES=cpu=1,mem=4G,node=1,billing=1,gres/gpu=1
   Socks/Node=* NtasksPerN:B:S:C=0:0:*:* CoreSpec=*
   MinCPUsNode=1 MinMemoryCPU=4G MinTmpDiskNode=0
   Features=(null) DelayBoot=00:00:00
   OverSubscribe=OK Contiguous=0 Licenses=(null) LicensesAlloc=(null) Network=(null)
   Command=nvidia-smi
   WorkDir=/mnt/data/sqsh
   TresPerNode=gres/gpu:1
```
- 可以查其他不屬於自己的 job


<br>

---
---

<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 %}
