啟動時指令碼
onstart 是一段 bash 指令碼,在執行個體首次啟動時執行一次。你可以在這裡安裝套件、拉取權重、複製儲存庫或預熱快取 —— 也就是你原本每次部署後都得手動重做一遍的設定工作。
把它貼進範本表單的 啟動時指令碼,或透過 API 設定 onstart。
它在哪裡執行,在哪裡不執行
| 啟動模式 | 行為 |
|---|---|
ssh | 在 Superheat 基礎映像檔上,於 sshd 與 Jupyter 啟動後執行一次。在第三方映像檔上,只會被匯出 |
jupyter | 與 ssh 相同 |
vm | 由 cloud-init 寫進客體系統,並在第一次開機時執行一次 |
args | 由我們匯出,但不執行 —— 以 SUPERHEAT_ONSTART 送達,只有在映像檔會讀它時才會被執行 |
在 args 模式下,程序由映像檔自己的進入點掌控,而這是刻意的:在那裡換上 Superheat 的進入點會把你的命令丟掉,而那正是一個 args 範本的全部內容。所以那段指令碼是以 SUPERHEAT_ONSTART 交給容器,之後我們不再對它做任何事。
決定它會不會執行的是映像檔,不是模式。Superheat 基礎映像檔會從自己的進入點讀 SUPERHEAT_ONSTART、執行那段指令碼一次,然後 exec 你的命令——所以一個跑在我們映像檔上的 args 範本確實會執行它。第三方映像檔從來沒聽過這個變數,也就會忽略它,在 ssh 模式下跟在 args 模式下一樣。如果你的指令碼沒有執行,就把範本改到 Superheat 基礎映像檔上、把設定工作內建進你自己的映像檔,或讓它成為你的命令做的第一件事。
它是以環境變數送達的
指令碼內容是以 SUPERHEAT_ONSTART 環境變數交給容器的。它 絕不會 從主機掛載成檔案。基礎映像檔的進入點會讀取這個變數,把內容以 0700 模式寫入 /var/lib/superheat/onstart.sh,然後執行它。為了相容已經在讀取 ONSTART 的映像檔,這個簡短名稱也被接受為別名。
由此衍生出四件事,而這四件事都會咬到那些以為它是檔案掛載的人。
內容是原封不動傳遞的,所以你不必為了 Superheat 做任何跳脫處理。 引號、反斜線、換行、heredoc 與 $ 都會照你打的樣子原樣送到容器。請像在編輯器裡寫指令碼那樣寫,而不是像在 bash -c "…" 引數裡那樣寫。變數展開發生在容器內,在 bash 執行該檔案時,依據容器的環境進行 —— 不會提前發生。
指令碼是用 bash 執行的,所以 shebang 只是裝飾。 在開頭寫 #!/usr/bin/env python3 不會讓它變成 Python 指令碼;它只是變成一段第一行是註解的 bash 指令碼,而下一行就是語法錯誤。要執行 bash 以外的東西,請讓指令碼把內容寫出成檔案,再呼叫正確的直譯器。
長度是有上限的。 範本欄位把 onstart 限制在 65,536 個字元,這裡不適合放大量內容。如果你的設定工作超過一兩個畫面,請把它放進儲存庫,讓啟動時指令碼去抓下來執行 —— 這也讓變動最頻繁的那部分納入版本控制。
你無法從 shell 讀到它。 進入點刻意把 SUPERHEAT_ONSTART 連同 Jupyter 權杖與 SSH 金鑰相關變數,一起排除在匯出給登入 shell 的環境之外。透過 SSH echo $SUPERHEAT_ONSTART 什麼也拿不到。如果你需要看實際執行了什麼,請改讀 /var/lib/superheat/onstart.sh。
執行一次就是只有一次
不論成功或失敗,進入點都會在 /var/lib/superheat/.onstart-done 寫下一個哨兵檔,之後絕不會在該容器上再次執行這段指令碼。停止再啟動執行個體會重跑進入點,但哨兵檔會讓它跳過啟動時指令碼的內容,所以只完成一半的設定不會被無聲地疊著重試一次。
如果你的指令碼失敗,容器仍會保持運作。失敗只會被記錄下來,不會致命,正是為了讓你能 SSH 進去手動修正。有兩個日誌位置很重要:
cat /var/log/superheat/onstart.log # your script's own output
進入點的摘要行,包括結束代碼,也會出現在主控台的執行個體日誌中。請參閱日誌。
因為 sshd 是在啟動時指令碼 之前 就啟動的,你可以在它還在跑的時候連進去觀察:
tail -f /var/log/superheat/onstart.log
修好之後若要重跑,請刪除哨兵檔並自行執行指令碼:
rm -f /var/lib/superheat/.onstart-done
bash /var/lib/superheat/onstart.sh
完整範例
這個例子會安裝相依套件、用以機密環境變數提供的權杖抓取模型,並留下一個標記檔,讓你一眼就能看出它有沒有跑完。
set -euo pipefail
echo "onstart: $(date -u +%FT%TZ) on ${GPU_COUNT} GPU(s)"
pip install --no-cache-dir -r /workspace/requirements.txt
# HF_TOKEN comes from a template env entry marked secret, or from
# env_overrides at deploy time. Never hard-code it here.
if [ -n "${HF_TOKEN:-}" ]; then
huggingface-cli download meta-llama/Llama-3-8B \
--local-dir /workspace/models/llama-3-8b
else
echo "HF_TOKEN unset — skipping model download" >&2
fi
date -u +%FT%TZ > /workspace/.setup-complete
echo "onstart: done"
值得照抄的重點:用 set -euo pipefail 讓失敗的步驟直接中止指令碼,而不是留下一個蓋到一半的環境;用 ${HF_TOKEN:-} 讓未設定的變數不會在 set -u 下中斷執行;以及把所有東西都寫進 /workspace,這樣停止再啟動後仍會保留。
公開範本的啟動時指令碼每一位 Superheat 使用者都讀得到,分享出去的配方也會原封不動帶著它。請把權杖放進標示為 機密 的環境變數項目,並在指令碼中讀取它們,如上例所示。請參閱環境變數。
之後編輯它
onstart 是啟動配方的一部分,所以更動它會重新產生範本的 hash_id。已經在執行的執行個體會保有啟動當下所用的指令碼 —— 編輯只會影響之後才部署的執行個體。