基礎映像檔
ssh 與 jupyter 啟動模式需要一個內含 SSH 伺服器、JupyterLab 與啟動時契約的映像檔。像 pytorch/pytorch 這類單純的上游映像檔完全沒有這些,這就是為什麼這兩種模式僅限使用衍生自 Superheat 基礎映像檔的映像檔。
superheat/base
這是規範名稱,託管於 Docker Hub。該映像檔同時鏡像至 ghcr.io/superheat-software/base,標籤集完全相同,由同一次建置推送 —— 因此兩種參照皆可使用,且當 Docker Hub 無法連線時主機會回退至鏡像來源。建議使用短名稱。
兩者皆為公開,所以主機不需要憑證就能拉取,使用基礎映像檔的範本也不需要填寫登錄庫憑證。
已發布的標籤
| 標籤 | 內容 |
|---|---|
cuda-12.1 | CUDA 12.1 |
cuda-12.4 | CUDA 12.4 |
cuda-12.6 | CUDA 12.6 |
cuda-12.8 | CUDA 12.8 |
latest | 最新的 CUDA 次版本 |
sha-<12 chars>-<minor> | 每次建置搭配每個 CUDA 次版本各一個不可變標籤 |
每個 cuda-<minor> 標籤都是以 Ubuntu 22.04 上對應的 NVIDIA CUDA cuDNN runtime 映像檔建置的 —— cuda-12.4 來自 nvidia/cuda:12.4.1-cudnn-runtime-ubuntu22.04。映像檔只有 amd64,這與機群相符:Superheat 主機都是 x86_64。
cuda-<minor> 這套命名也是 [Automatic] 範本標籤解析時的依據。把範本的標籤留在 [Automatic],會挑出所選機器的 CUDA 能執行的最新標籤,因此同一個範本能在驅動程式版本混雜的機群上通用。當你需要的是可重現而非最新的建置時,請釘選特定標籤。
它額外加了什麼
在 CUDA runtime 之上:openssh-server、附帶 pip 與 jupyterlab 的 python3、tini、gosu、openssl、ca-certificates、curl、git 與 rsync。工作目錄是 /workspace,進入點是 tini -- /opt/superheat/entrypoint.sh。
這個映像檔帶有 com.superheat.base="true" 標籤。Superheat 就是靠這個標籤辨識一個映像檔可用於 ssh 與 jupyter 模式,而且任何以 FROM 基礎映像檔建置出來的東西都會繼承它。
進入點做了哪些事
每個步驟都是冪等的,所以停止再啟動執行個體時,可以安全地在同一個容器上重跑一次。
| 步驟 | 細節 |
|---|---|
| 1. 匯出環境 | 容器環境會寫入 /etc/environment 與 /etc/profile.d/superheat-env.sh,因為 sshd 與 Jupyter 產生的是不會繼承環境的登入 shell。佈建用的機密值會被排除在外。 |
| 2. 安裝金鑰 | PUBLIC_KEY 與 SUPERHEAT_SSH_PUBLIC_KEYS 會寫入 /root/.ssh/authorized_keys。 |
| 3. 啟動 sshd | 主機金鑰若不存在就會產生,接著 sshd 會在容器連接埠 22 上接聽。只接受金鑰;不接受密碼。 |
| 4. 啟動 Jupyter | 只有在設定了 JUPYTER_TOKEN 時才執行。系統會產生一次自我簽署憑證,並讓 JupyterLab 透過 HTTPS 繫結到 0.0.0.0:${JUPYTER_PORT}。 |
| 5. 執行 onstart | SUPERHEAT_ONSTART 只執行一次,由一個哨兵檔把關。失敗會被記錄下來,容器仍會保持運作。 |
| 6. 交棒 | 若映像檔有 CMD 就會執行它;否則容器會閒置,以維持執行狀態讓連接埠對應保持有效。 |
環境契約
這些變數名稱是 Superheat 與映像檔之間的介面。以基礎映像檔為底建置,進入點會替你處理它們;自己寫進入點,這些就是你必須實作的東西。
| 變數 | 用途 |
|---|---|
PUBLIC_KEY | 單一 OpenSSH 公開金鑰,附加到 authorized_keys |
SUPERHEAT_SSH_PUBLIC_KEYS | 以換行分隔的 OpenSSH 公開金鑰,附加到 authorized_keys |
SUPERHEAT_ONSTART | 在首次啟動時執行一次的 bash。屬敏感值;不會保存到環境檔案中。也接受 ONSTART 這個簡短別名 |
JUPYTER_TOKEN | 設定後,JupyterLab 會以它作為存取權杖啟動 |
JUPYTER_PORT | Jupyter 繫結的連接埠,預設為 8080 |
JUPYTER_DIR | Jupyter 的根目錄,預設為 /workspace |
JUPYTER_LAB | true 使用 JupyterLab(預設),false 使用傳統 notebook |
OPEN_BUTTON_PORT | 僅供參考 —— 主控台 開啟 按鈕的目標連接埠 |
CONTAINER_ID | 僅供參考 —— 工作負載識別碼 |
GPU_COUNT | 僅供參考 —— 指派了多少個 GPU |
SUPERHEAT_TCP_PORT_<n> | 僅供參考 —— 容器連接埠 <n> 對外發布的主機連接埠 |
Superheat 會最後才設定這些值,排在你的範本環境變數與任何租用者覆寫之後,所以它們都不會被蓋掉。請參閱環境變數。
建置你自己的映像檔
從 CUDA 版本符合你程式碼需求的標籤繼承,安裝你的相依套件,並且不要動進入點:
FROM superheat/base:cuda-12.4
RUN pip install --no-cache-dir torch torchvision transformers
COPY train.py /workspace/train.py
WORKDIR /workspace
三條規則能讓成果在 ssh 與 jupyter 模式下仍可啟動:
- 要繼承,不要重建。 從
nvidia/cuda開始建置並自行複製一份 sshd 進去,會失去com.superheat.base標籤,連帶失去進入點契約。 - 不要覆寫
ENTRYPOINT。 基礎進入點負責啟動 sshd、Jupyter 與你的啟動時指令碼。覆寫它會讓ssh範本變成一個沒有任何方式能連進去的容器。 - 把你的程序放進
CMD。 進入點會在佈建完成後執行CMD,因此用這種方式啟動的長時間執行伺服器,旁邊還會有 sshd 與 Jupyter 一起跑。
如果你的映像檔沒辦法遵守這些規則 —— 例如不受你控制的上游映像檔,或本身就有進入點的映像檔 —— 請改用 args 啟動模式。它會執行任何映像檔原生的進入點,完全不需要基礎映像檔提供什麼。請參閱啟動模式。
該從哪個標籤繼承
| 情況 | 標籤 |
|---|---|
| 你的框架釘選了某個 CUDA 次版本 | 對應的 cuda-<minor> |
| 你想要最新可用的 CUDA,而且可以重新建置 | latest |
| 你需要幾個月後仍解析成完全相同的建置 | sha-… 標籤 |
釘選 cuda-<minor> 標籤是常見的做法。每當新的 CUDA 次版本發布時,latest 就會跟著移動;這對開發用映像檔沒問題,但拿來當可重現訓練任務的基礎就很糟。
檔案系統配置
| 路徑 | 內容 |
|---|---|
/workspace | 工作目錄,預設也是 Jupyter 的根目錄 |
/opt/superheat/entrypoint.sh | 佈建進入點 |
/var/lib/superheat/onstart.sh | 實際執行的啟動時指令碼內容 |
/var/lib/superheat/.onstart-done | 阻止啟動時指令碼重複執行的哨兵檔 |
/var/log/superheat/onstart.log | 啟動時指令碼的輸出 |
/var/log/superheat/jupyter.log | Jupyter 的輸出 |
/etc/profile.d/superheat-env.sh | 登入 shell 所看到的環境 |
sshd 與 Jupyter 也會把輸出串流到容器日誌,也就是主控台顯示的內容。請參閱日誌。