Model Registry:登記成功,但每一個值都是 0
1. 這是什麼
一份紀錄:線上這顆模型是什麼、哪來的、誰核准的、現在什麼狀態。
OpenShift AI 有一個內建元件叫 Model Registry,它是這件事的工具化版本。
Java 類比:像 Nexus/Artifactory 對 jar 的角色。 你的 jar 不會只躺在某台機器的資料夾裡,它有版本、有座標、有誰上傳的。 模型也該有。
2. 什麼時機需要它
不是「因為它是最佳實踐」。是因為你答不出下面某一個問題:
| 你被問到 | 沒有台帳的話 |
|---|---|
| 線上這顆模型是哪一次訓練出來的? | 翻聊天記錄 |
| 誰核准它上線的? | 「應該是我吧」 |
| 它上線前的評估數字是多少? | 要重跑一次才知道 |
| 要換回上一版,上一版是哪一顆? | 找不到 |
如果這四題你都答得出來,你可能還不需要這個元件—— 一份大家都在維護的表格也能回答。
需要工具的時機是:回答這些問題的成本開始高於維護工具的成本。 通常發生在模型超過個位數、或者換版頻率超過每月一次的時候。
3. 怎麼用
四步。以下每一段我都跑過。
3-1 在 DSC 開元件
oc patch dsc default-dsc --type=merge \
-p '{"spec":{"components":{"modelregistry":{"managementState":"Managed"}}}}'
約一分鐘後:
oc get dsc default-dsc -o jsonpath='{range .status.conditions[?(@.type=="ModelRegistryReady")]}{.status}{"\n"}{end}'
# True
⚠️ 但 ModelRegistryReady=True 不代表你可以登記模型了。
這一步只是把 operator 和 UI 裝起來。真正的 registry 還沒建。
3-2 準備一個資料庫(要你自己給)
Registry 的中繼資料要存在關聯式資料庫。CR 的 spec 要求
mysql(必填 database / host / username)或 postgres(必填 database)。
這是最容易卡住的一步:官方文件把它當前置條件,但你要在 「元件已經 Ready」之後才會發現這件事。
lab 版(正式環境請用真的 DB,不要用 emptyDir):
apiVersion: v1
kind: Secret
metadata: { name: mr-db, namespace: odh-model-registries }
stringData:
MYSQL_ROOT_PASSWORD: <pw>
MYSQL_DATABASE: modelregistry
MYSQL_USER: mruser
MYSQL_PASSWORD: <pw>
(外加一個 mysql:8.0 的 Deployment 和 Service,就一般寫法。)
3-3 建 registry 實例 —— ⚠️ namespace 不能亂放
apiVersion: modelregistry.opendatahub.io/v1beta1
kind: ModelRegistry
metadata:
name: demo-registry
namespace: odh-model-registries # ← 這個不能改
spec:
rest: {}
mysql:
host: mr-mysql.odh-model-registries.svc.cluster.local
port: 3306
database: modelregistry
username: mruser
passwordSecret: { name: mr-db, key: MYSQL_PASSWORD }
我一開始放在自己的 project 裡,被擋下來:
admission webhook "vmodelregistry.opendatahub.io" denied the request:
namespace must be odh-model-registries
那個 namespace 是 DSC 上設的,可以查:
oc get dsc default-dsc -o jsonpath='{.spec.components.modelregistry.registriesNamespace}'
# odh-model-registries
另外注意 API 版本:v1alpha1 送出去會回
Warning: Version v1alpha1 of the ModelRegistry API is deprecated ... Please use v1beta1 instead.
用 v1beta1,而且它的必填欄位少一個(v1alpha1 要 grpc+rest,v1beta1 只要 rest)。
建好之後:
oc get pods -n odh-model-registries
# demo-registry-7d657fc9d9-w82t8 2/2 Running ← registry + kube-rbac-proxy
# mr-mysql-5fcc5bb656-zdnc5 1/1 Running
⚠️ 順帶一提:那個
ModelRegistryCR 的.status.conditions在我的環境 一直是空的——pod 跑起來了,但 CR 沒有回報任何狀態。 所以不要用 CR 的 status 判斷它好了沒,要去看 pod 和打 API。
3-4 登記一顆模型
oc port-forward -n odh-model-registries svc/demo-registry 18443:8443 &
TOK=$(oc whoami -t)
B="https://localhost:18443/api/model_registry/v1alpha3"
# 建立一個「模型」
curl -sk -X POST "$B/registered_models" -H "Authorization: Bearer $TOK" \
-H 'Content-Type: application/json' \
-d '{"name":"llm-scratch","description":"從零訓練的小 GPT","owner":"ryan"}'
# 建立一個「版本」,掛上指標
curl -sk -X POST "$B/model_versions" ...
# 掛上 artifact(模型檔在哪)
curl -sk -X POST "$B/model_versions/<id>/artifacts" -H ... \
-d '{"artifactType":"model-artifact","name":"ckpt",
"uri":"s3://models/llm/ckpt.pt","modelFormatName":"pytorch"}'
⚠️ 注意 API 路徑是 v1alpha3,不是 CR 的 v1beta1。
這兩個版本號沒有關係。我試 /api/model_registry/v1beta1/... 得到 404 page not found。
4. ⭐ 它會怎麼咬你:登記成功,但值全是 0
這是這篇最重要的一段。
我登記模型版本時,把評估指標一起掛上去:
"customProperties": {
"test_bpc": {"metadataType":"MetadataDoubleValue","doubleValue": 4.9946},
"model_digest": {"metadataType":"MetadataStringValue","stringValue":"sha256:4d694be…"},
"data_quality_gate": {"metadataType":"MetadataBoolValue", "boolValue": true}
}
HTTP 200。條目建立成功。讀得回來。
然後我把它讀出來看:
"test_bpc": {"metadataType":"MetadataDoubleValue","double_value": 0}
"model_digest": {"metadataType":"MetadataStringValue","string_value": ""}
"data_quality_gate": {"metadataType":"MetadataBoolValue", "bool_value": false}
每一個值都是零值。
原因:API 吃的是 snake_case(double_value),我送的是 camelCase(doubleValue)。
它不認識那個欄位,就用零值填上——而且不報錯。
同一個請求,只把欄位名改成 snake_case:
"test_bpc": {"double_value": 4.9946}
"model_digest": {"string_value": "sha256:4d694be9342d"}
"data_quality_gate": {"bool_value": true}
全部正確存進去。
為什麼這個特別糟
想一下這個台帳條目長什麼樣:
- 模型名字:對的
- 版本名字:對的
- 建立時間、作者:對的
- artifact 的 S3 路徑:對的
- 而所有的治理數據——指標、digest、品質閘門——全是零值
它看起來完全正常。 你在 UI 上會看到一筆整齊的紀錄。 只有當你去比對「這個 digest 跟線上那顆一樣嗎」的時候, 才會發現你在比對一個空字串。
而 data_quality_gate 從 true 變成 false 更危險:
如果有任何流程讀這個欄位做決定,它會用一個假的 false 去做判斷。
這是「假綠」的另一種形態,而且是最陰的一種: 寫入成功、讀得回來、格式正確、內容是空的。
5. 關鍵指標
| 「跑完了」 | ⭐「做對了」 | |
|---|---|---|
| Model Registry | ModelRegistryReady=True、pod 2/2 |
登記一筆之後讀回來比對:digest 不是空字串、指標不是 0 |
驗收這個元件只有一個方法:登記一筆,讀回來,逐欄比對。 不要看 UI 上有沒有東西——有東西不代表那東西是對的。
6. 什麼時候不需要它
模型數量少、換版不頻繁的時候。 一份維護中的表格能回答那四個問題就夠了。
而且注意這個元件的真實成本不是那兩個 pod,是:
- 你要多維護一個資料庫(正式環境要備份、要 HA、要納管)
- 要走 API 才能登記——代表你的 pipeline 要多一段程式碼
- 而如果沒有人把「登記」接進放行流程,它只是一個沒人看的資料庫
我自己的 lab 在這之前用的是最土的做法:一個 JSON 檔掛成 ConfigMap。 能回答問題的爛台帳,勝過沒有台帳。
7. 順帶:這個元件跟「治理」還差一段
裝了 Model Registry 不等於你的模型受治理。
它是一個紀錄的地方,不是一個擋事的地方。
沒有任何東西阻止你把一顆沒登記的模型部署上線——
我實測過,服務會誠實地回 "status": "UNREGISTERED",然後繼續服務。
(那件事我另外寫了一篇: 三顆一樣的模型,一顆上線兩顆被擋)
你們的模型台帳在哪?Excel、Confluence、還是有工具? 出事時查得到「線上這顆是誰核准的」嗎?
🧪 這篇的實驗環境與 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-* 那一套)。
指令的邏輯可以照用,字串要自己對一次。
留言與指正
我特別想知道:你們的模型台帳在哪?Excel、Confluence、還是有工具?出事時查得到「線上這顆是誰核准的」嗎?