1. 這是什麼

一個放容器 image 的地方,而且是你自己的。

我 lab 用 Harbor(開源、可自架)。企業常見的還有 JFrog Artifactory、 Nexus、或雲端的 ECR/ACR/GAR。

Harbor projects

Java 類比:Nexus 對 jar 的角色。差別在 image 更大、更難掃, 而且它是執行環境不只是相依——jar 有問題你重打包, image 有問題可能是底層 OS 的 CVE。


2. 什麼時機需要它

第一個時機:你的叢集連不到外網。這時候不是「要不要」,是「沒有就裝不起來」。

第二個時機(更常見):你需要回答這三題其中一題——

  • 「線上跑的這顆 image 是誰建的、什麼時候?」
  • 「它掃過 CVE 了嗎?」
  • 「誰核准它進正式環境的?」

公開 registry 一題都答不了。


3. ⭐ 為什麼 image 跟模型不能放同一個地方

這是這篇的重點,而且它會決定你後面整個流程的形狀。

  容器 image 模型權重
放哪 registry(Harbor) S3(MinIO)
多久換一次 幾週~幾個月 可能一天三次
誰產的 CI/CD 訓練 pipeline
要不要簽章 看你
要不要掃 CVE 掃不了(那不是程式)
改一次的成本 重 build + 重掃 + 重簽 上傳一個檔案

綁在一起會怎樣:每次換模型,你要重跑一次完整的 image 建置、 CVE 掃描、簽章、以及所有相關的簽核。換一個權重檔要走一次發版流程。

分開之後,同一個 image 可以服務不同的模型, 靠環境變數(STORAGE_URI)指到不同的 S3 路徑。

這就是 KServe 的 storage-initializer 存在的理由—— 它讓 image 保持不可變,而模型可以換。


4. 怎麼用:兩個 project 撐起一個放行流程

我的 Harbor 有這幾個 project:

tools       ← 平台自己要用的 image(serving runtime、buildah…)
odh         ← 鏡進來的上游 image
demo        ← 已放行
demo-tmp    ← 待審

demo-tmpdemo 分開,是整個放行機制的關鍵。

流程是這樣:

build 完 → 推到 demo-tmp(待審)
           ↓  掃描、簽章、人工檢查
        管理員手動觸發 replication
           ↓
         demo(已放行)→ 正式環境只認這裡

正式環境的叢集只被允許從 demo 拉。 所以「放行」這個動作 在技術上就是「把 image 從 demo-tmp 複製到 demo」。

為什麼這比「加一個 tag」好

常見的做法是用 tag 表示狀態:myapp:stagingmyapp:prod

問題是 tag 可以被覆寫。 今天的 prod 和上週的 prod 可能是不同的東西, 而且沒有紀錄。

用兩個 project 的話:

  • 進到 demo 的東西,digest 不變——它就是被審過的那一顆
  • replication 的動作有紀錄,誰按的、什麼時候
  • 而且權限可以分開:開發者能推 demo-tmp,但不能推 demo

我實測過這件事:從 demo-tmp replicate 到 demo 之後, digest 完全相同,一顆 image 三個 tag。 那證明放行沒有改變成品本身——這正是稽核要的


5. 一律用 digest,不要用 tag

# ✗ tag 會漂移
image: myregistry/tools/llm-serve:cpu

# ✓ digest 是內容的雜湊,改了就是不同一顆
image: myregistry/tools/llm-serve@sha256:4ed07b551253fef4f01...

我 lab 那顆 serving image:

tools/llm-serve   digest=sha256:4ed07b551253fef4f01…   tags=['cpu']   314MB

cpu 這個 tag 明天可能指向別的東西,那個 digest 不會。

⚠️ 而且前面提過:IDMS(離線鏡像的來源改寫)是用 digest 比對的。 你的 pod spec 如果寫 tag,IDMS 不會生效——那是一個典型的「設了但沒作用」。


6. 關鍵指標

  「跑完了」 ⭐「做對了」
私有 registry image 推得上去、拉得下來 拿線上跑的 digest,反查得到它是誰建的、掃過沒、誰放行的

第二欄是唯一有意義的驗收:給我一個 digest,你能不能回答那三個問題。

如果答案是「要去問某某」,那你有的是一個檔案伺服器,不是 registry。


7. 什麼時候不需要它

  • 完全用公開 image、而且不需要回答上面那三題
  • 雲端環境、直接用託管的 registry(那也是私有 registry,只是別人管)

但如果你在企業內網、或有稽核要求,這一項沒有選擇。


你們的 image 放哪?有沒有「待審」和「已放行」分開的機制?

🧪 這篇的實驗環境與 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.2openshift-gitops-operator.v1.21.3

DataScienceCluster 開啟的元件

  • kserveaipipelinesdashboardworkbenchesmodelregistrykueue(Unmanaged)
  • 其餘(raytrustyaifeastaigateway…)為 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 檔案

⚠️ ODH ≠ RHOAI:元件同源,但 namespace 與部分名稱不同 (我這裡是 opendatahub,商用版是 redhat-ods-* 那一套)。 指令的邏輯可以照用,字串要自己對一次。