Tsung-Jung Tsai (TJ_Tsai)
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    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 %}

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully