我為一個平台導入案寫了一份 42 條的驗收清單:每一條都有「指令/畫面」、 「預期輸出」、「不接受什麼」。全部照官方文件寫的。

然後我在自己的 lab 上把它跑了一遍。

三條有問題。而且如果我沒跑,它們會直接被帶進驗收現場。


Bug 1:一個會吐出 80 行假警報的指令

原本這樣寫:

oc get csv -A | grep -i opendatahub

在我的 lab 上,它吐 82 行

看起來像裝了 82 套 operator。第一反應是「環境壞了」。

實際上只裝了一套。

原因:operator 用 AllNamespaces 模式安裝時, 它的 CSV(ClusterServiceVersion)會被複製到每一個 namespace。 那 82 行是同一個 CSV 在 82 個 namespace 的副本。

修正:

oc get csv -A -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' \
  | sort -u | grep -i opendatahub
opendatahub-operator.v3.5.0        ← 就一套

這條 bug 的危害不是「數字不對」,是它會製造恐慌。 在驗收現場,你拿一個吐 82 行的指令去問廠商, 對方會花半小時解釋一件根本沒發生的事——而你們兩邊都會覺得對方有問題。

教訓:任何會回傳「一堆」的指令,先確認它回的是不同的東西, 還是同一個東西的多份副本


Bug 2:問錯了問題

原本 L5-2 寫的是:「比對鏡像的 digest 清單 vs 實際拉取的」。

看起來很嚴謹。但實際跑過才發現,那個問題問得太客氣了

真正該問的是:

「現在正在跑的 pod,有幾顆 image 根本不在你的私有 registry?」

oc get pods -A --field-selector=status.phase=Running \
  -o jsonpath='{range .items[*].status.containerStatuses[*]}{.imageID}{"\n"}{end}' \
  | sed 's|@.*||' | sort -u | grep -vE '^<你的私有 registry>'

離線環境的正確答案是:空的。

任何一行輸出,都代表那顆 image 是從外網拉的—— 也就是這個環境現在還連得到外網,或者它根本沒有真正離線過

差別在哪:原本的問法是「你的清單完整嗎」(比對兩份文件)。 新的問法是「這個環境現在有沒有依賴外網」(直接量事實)。

前者可以用一份漂亮的文件回答,後者不行。

我在 lab 上跑這條,結果是 operator 宣告 2 顆、實際 76 顆


Bug 3:把兩件無關的事寫成同一條

原本的清單把 insecureRegistries 和 IDMS 混在一條裡, 好像設了其中一個就代表「私有 registry 接好了」。

它們完全無關:

機制 作用 沒有它會怎樣
insecureRegistries/憑證信任 叢集敢不敢連你的 registry TLS 錯誤,連不上
IDMS(ImageDigestMirrorSet) quay.io/x@sha256:… 改導向私有 registry 照樣往外網拉 → 離線環境直接失敗

我的 lab 一開始只設了 insecureRegistries(讓 CRC 信任 HTTP 的 Harbor), 而 oc get imagedigestmirrorsetNo resources found

當時我以為「私有 registry 已經接好了」——因為推得上去、拉得下來。 那只證明「連得到」,不證明「叢集會去那裡拿」。

(現在補上了:odh-workbench-mirror。)

⚠️ 再補一個後來才知道的:IDMS 是用 digest 比對的。 如果你的 pod spec 寫的是 tag 不是 digest,IDMS 不會生效——要用 ITMS。 這又是一個「設了但沒作用」。


三條的共同點

bug 錯在哪 一句話
1 指令回的是副本不是實體 數量對不代表意義對
2 問了一個可以用文件回答的問題 要量事實,不要比對文件
3 把兩件事寫成一條 一條只驗一件事

而它們的共同來源只有一個:這份清單是照文件寫的,沒有跑過。


⭐ 所以:清單也是程式,也要測試

我後來把這件事訂成規矩:

任何驗收清單,在拿去用之前,要先在一個你控制得了的環境跑一遍。

理由跟寫程式一樣:

  • 你寫的指令可能有 bug(bug 1)
  • 你的預期輸出可能是錯的(bug 1 的「預期一行」其實是 82 行)
  • 你問的問題可能問錯方向(bug 2)
  • 你可能把兩個概念混在一起(bug 3)

而這些只有跑過才會發現

沒跑過的清單會怎樣

拿一份沒跑過的清單去驗收,最好的情況是浪費時間解釋假警報。

最壞的情況是:對方照著你的清單交付,而你的清單漏掉了真正的問題—— 然後雙方都有一份「全部通過」的紀錄,而系統是壞的。

那份紀錄會在出事時保護不了任何人。


附帶:跑清單的成本比想像低

我在 CRC 上跑完 42 條,花了大約兩天——而且順便發現了平台本身的四個坑

那兩天的產出不只是「清單修好了」,還有:

  • 每一條的實際輸出可以貼進清單當「預期」的範例
  • 對方看到範例輸出,就知道你真的跑過——談判位置完全不同
  • 你自己在現場看到異常輸出時,認得出來

最後那點最值錢。 驗收現場對方跑一個指令給你看, 你要能在三秒內判斷「這個輸出正不正常」—— 而那個能力只能靠自己先跑過一遍。


你們的驗收清單有沒有實際跑過一遍?還是寫完就直接拿去用?

🧪 這篇的實驗環境與 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-* 那一套)。 指令的邏輯可以照用,字串要自己對一次。