從 gate 放行到真的上線,中間那一步是空的
前面我寫了四棒 pipeline、 寫了 gate 擋不擋得住、 寫了 InferenceService 怎麼上線。
但有一段我一直沒寫,因為我以為它是顯而易見的:
模型訓練完、gate 放行之後,它怎麼變成線上服務?
今天我去查了。答案是:不會。中間那一步是空的。
先看程式碼說什麼
gate 那一棒通過之後,最後做的事是:
print(" ✓ 放行")
reg.append(entry)
put(REG_LOCAL, MODELS, REG_KEY) # 寫台帳
put(ART / "ckpt.pt", MODELS, "llm-candidate/ckpt.pt")
put(ART / "tokenizer.json", MODELS, "llm-candidate/tokenizer.json")
print(" 已註冊進台帳並送進候選區 s3://models/llm-candidate/")
放行 = 把模型複製到 llm-candidate/。
而 InferenceService 讀的是哪裡?
oc get isvc llm-scratch -o jsonpath='{.spec.predictor.containers[0].env[?(@.name=="STORAGE_URI")].value}'
# s3://models/llm/
llm/,不是 llm-candidate/。
⭐ 時間戳說得更清楚
mc ls --recursive local/models
[2026-08-28 08:13] 5.5 MiB llm-candidate/ckpt.pt ← gate 放行的那顆
[2026-08-28 08:13] 36 KiB llm-candidate/tokenizer.json
[2026-08-24 02:41] 32 MiB llm/ckpt.pt ← 線上服務實際讀的
[2026-08-24 03:35] 99 MiB llm/clean_corpus.txt
[2026-08-24 02:41] 97 KiB llm/tokenizer.json
候選區那顆是 8/28 的,5.5 MB。線上那顆是 8/24 的,32 MB。
不只時間差四天——大小差六倍,那根本是兩顆完全不同的模型。
gate 放行的東西,從來沒有上線過。
更麻煩的:服務講的是另一份台帳
去問服務它自己是誰:
curl -s localhost:18000/model | jq
{
"serving_digest": "sha256:4d694be9342d…",
"in_registry": true,
"status": "production",
"metrics": {"test_loss": 3.462, "test_bpc": 4.9946},
"data_quality_gate": true
}
status: production。看起來一切正常。
但那份台帳是用 ConfigMap 掛進容器的本機檔,裡面兩筆, 建立時間都是 2026-06-21:
00b47fc84755 archived 2026-06-21
4d694be9342d production 2026-06-21 ← 線上這顆
而 gate 寫的台帳應該在 S3 上。我去找:
mc ls --recursive local/models | grep registry.json
# (沒有)
兩份台帳,互不相干。
- 服務讀的那份:ConfigMap,兩筆,六月的
- gate 寫的那份:S3,而我在 bucket 裡找不到它
所以 /model 回的 production 是真的——它只是在講六月那顆模型,
跟這條 pipeline 跑出來的東西完全沒有關係。
整條鏈畫出來長這樣
pipeline train → s3://models/runs/<run_id>/ckpt.pt
↓
gate 放行 → s3://models/llm-candidate/ckpt.pt 8/28, 5.5 MB
↓
┌───────────────┐
│ ??? │ ← 這裡什麼都沒有
└───────────────┘
↓
ISvc 讀取 → s3://models/llm/ckpt.pt 8/24, 32 MB
中間那個框,就是「上線」這件事。而它沒有被實作。
為什麼會這樣——而且為什麼很常見
我不是忘了寫。我是做到「候選區」就停下來了,因為那一步之後的東西不是技術問題。
從候選到上線,需要決定的是:
| 問題 | 這是誰的決定 |
|---|---|
| 誰有權按下「上線」 | 不是工程問題 |
| 上線要不要人工確認 | 流程問題 |
| 換上去之後怎麼回滾 | 要先有回滾機制 |
| 舊的那顆留多久 | 保存政策 |
| 出事了誰負責 | 組織問題 |
技術上,「上線」可以是一行 mc cp llm-candidate/ llm/。
而正因為它技術上這麼簡單,它幾乎總是最後才被做, 或者被做成「某個人手動跑一下」——然後那個人變成單點。
⭐ 這就是「治理只做到一半」的具體長相: 前面每一棒都自動化、有紀錄、有 gate, 而最後那個真正改變線上狀態的動作,是人工的、沒有紀錄的。
兩種補法
A. 自動 promote(適合換版頻繁)
gate 通過就直接複製到 llm/,然後重啟 ISvc。
mc cp local/models/llm-candidate/ckpt.pt local/models/llm/ckpt.pt
oc rollout restart deploy/llm-scratch-predictor
⚠️ 這樣做的前提是你的 gate 真的擋得住東西。 如果 gate 的門檻是 run 參數(誰都能填), 自動 promote 等於把「上線」的權限交給任何能建 run 的人。
B. 人工放行,但留下紀錄(適合金融業)
候選區保持不動,上線是一個明確的、有簽核的動作——
而且那個動作應該是 git commit,不是 mc cp。
把 ISvc 的 STORAGE_URI 或模型 digest 寫進 GitOps repo,
上線 = 改那個檔案 + PR + merge。
這樣「誰放行的」這個問題,答案就在 git log 裡。
這跟容器 image 的
demo-tmp → demo放行機制是同一個形狀: 待審區和放行區分開,而搬動的那個動作要留痕。
最誠實的一段
我兩種都還沒做。
我的 lab 現在的狀態是:pipeline 會跑、gate 會擋、台帳會寫、 候選區會更新——而線上那顆是六月手動放上去的,四天前的候選從來沒上去過。
而我一直沒發現,因為:
oc get isvc→READY=True✅- 推論打得通 ✅
/model回production✅- pipeline 的 run 是綠的 ✅
每一個檢查都通過,而這條鏈中間是斷的。
要發現它,唯一的辦法是去比對兩個路徑的時間戳—— 而那不在任何一份健康檢查清單上。
給驗收的一句
「請告訴我,線上這顆模型是哪一次 run 產出的。」
如果對方要去翻聊天記錄、或者說「應該是上次那個」, 那條鏈就是斷的——不管前面的 pipeline 做得多漂亮。
而這一題比「有沒有 gate」有用得多,因為它問的是結果不是流程。
你們的模型從「訓練完」到「線上服務」,中間那一步是誰做的?自動的還是人工的?
🧪 這篇的實驗環境與 lab 檔案(最後更新 2026-08-30)
叢集
- CRC 2.63.0 · OpenShift 4.22.7 · Kubernetes v1.35.6
- 單節點:13 vCPU / 40 GiB RAM / 120 GB disk
- 宿主:Framework Laptop 16(Ryzen AI 7 350 · 8C/16T · 64 GB RAM · 1 TB NVMe · RTX 5070 顯卡模組)
- ⚠️ 叢集內看不到 GPU(CRC 是 VM,RTX 5070 未 passthrough)
Operator
opendatahub-operator.v3.5.0← 即 RHOAI 3.x 的上游開源版cert-manager-operator.v1.20.0(3.x 的必要相依;2.x 不需要)openshift-pipelines-operator-rh.v1.23.2、openshift-gitops-operator.v1.21.3
DataScienceCluster 開啟的元件
kserve、aipipelines、dashboard、workbenches、modelregistry、kueue(Unmanaged)- 其餘(
ray/trustyai/feast/aigateway…)為Removed
叢集外的依賴(跑在宿主的 podman 上,crc start 不會帶起來)
- Harbor v2.15.2(私有 registry)· MinIO(S3)
模型端
- Python 3.12.13 · PyTorch 2.11.0+cu128 · FastAPI + uvicorn · prometheus-client
lab 檔案
- YAML/Containerfile/腳本:github.com/ryanGTR/openshift-ai-30days
(含 Day 對照表;主機名是佔位符,跑
set-lab-host.sh換成你自己的) - 服務的那個模型:llm-from-scratch(從零手刻的小 GPT)
⚠️ ODH ≠ RHOAI:元件同源,但 namespace 與部分名稱不同
(我這裡是 opendatahub,商用版是 redhat-ods-* 那一套)。
指令的邏輯可以照用,字串要自己對一次。
留言與指正
我特別想知道:你們的模型從「訓練完」到「線上服務」,中間那一步是誰做的?自動的還是人工的?