iT邦幫忙

2026 iThome 鐵人賽

DAY 20
0
AI Engineering

OpenShift AI 簡易入門30天系列 第 20

Day 20:離線環境——image 怎麼進來、怎麼拉得到

  • 分享至 

  • xImage
  •  

這是什麼、解決什麼問題

你照著 Day 4 裝好了平台,然後在公司環境重做一次——卡在第一步。

ImagePullBackOff。因為 quay.io 連不到。

這不是例外,這是金融業的常態。 而且影響的不只是安裝:
每個 workbench image、每個 ServingRuntime、
pipeline 裡每個 packages_to_install——全部都要有離線的來源。


什麼時候你會用到

  • 叢集在內網、連不到外部 registry
  • 合規要求「所有 image 必須經過內部掃描」
  • 用內部 Harbor / Quay / JFrog

前置條件

  • 一個內部 registry
  • 一台同時連得到外網和內部 registry 的擺渡機
  • skopeo(或 oc mirror

步驟一:先搞清楚有三件事要做

這是最多人卡住的地方——他們做了第一件,以為就完成了。

做什麼 沒做會怎樣
A 搬 image 複製到內部 registry image 不存在
B 改路由 設 mirror 規則 image 在,但叢集還是去 quay.io 找
C 給權限 pull secret + TLS 信任 找得到,但拉不動

三件事是獨立的,而且錯誤訊息不一樣——步驟五那張表會用到。


步驟二:搬 image(A)

skopeo copy --all \
  docker://quay.io/opendatahub/odh-workbench-jupyter-datascience-cpu-py312-ubi9@sha256:<digest> \
  docker://registry.internal:8088/odh/odh-workbench-jupyter-datascience-cpu-py312-ubi9:3.5

⚠️ 來源用 digest,目的地用 tag。

因為 mirror 規則是按 digest 比對的(下一步會看到),
你搬的必須是規則會去找的那一顆,而 tag 隨時可能指向不同的 image

驗證這一步:

skopeo inspect docker://registry.internal:8088/odh/<name>:3.5 | jq -r '.Digest'

digest 要跟來源一模一樣。

我 lab 上搬一個 workbench image 花了 17 分 26 秒、1,698 MB。
完整的平台有幾十個——這是排期時要算進去的


步驟三:告訴叢集去哪找(B)

OpenShift 4.x 用 ImageDigestMirrorSet

apiVersion: config.openshift.io/v1
kind: ImageDigestMirrorSet
metadata:
  name: odh-workbench-mirror
spec:
  imageDigestMirrors:
  - source: quay.io/opendatahub/odh-workbench-jupyter-datascience-cpu-py312-ubi9
    mirrors:
    - registry.internal:8088/odh/odh-workbench-jupyter-datascience-cpu-py312-ubi9

⚠️ IDMS 只對 digest 形式的參照生效。
YAML 裡寫 image: quay.io/xxx:3.5(tag 形式)不會被改寫——
這是名字裡 Digest 的意思。(tag 需求要用 ImageTagMirrorSet。)

套用後節點會逐台重啟 CRI-O

oc get imagedigestmirrorset
oc get mcp        # 等 UPDATED=True

步驟四:憑證與權限(C)

TLS 信任

oc create cm registry-certs -n openshift-config \
  --from-file=registry.internal..8088=ca.crt

oc patch image.config.openshift.io/cluster --type=merge \
  -p '{"spec":{"additionalTrustedCA":{"name":"registry-certs"}}}'

⚠️ 檔名格式很特別:主機名裡的冒號寫成兩個點(registry.internal..8088)。

lab 可以走捷徑(正式環境不要,它關掉的是 TLS 驗證,
中間人可以換掉你的 image):

oc patch image.config.openshift.io/cluster --type=merge \
  -p '{"spec":{"registrySources":{"insecureRegistries":["registry.internal:8088"]}}}'

pull secret——三層,選對一層

影響範圍 什麼時候用
叢集全域(openshift-config/pull-secret 所有 namespace 平台自己要拉的
ServiceAccount 那個 SA 起的所有 pod 最常用
Pod(imagePullSecrets 單一 pod 特例

⚠️ 最常見的錯誤:建了 Secret,以為就生效了。
Secret 必須被 SA 連結、或被 pod 明確引用。

oc create secret docker-registry harbor-pull \
  --docker-server=registry.internal:8088 \
  --docker-username='robot$odh+puller' \
  --docker-password='<token>' -n <ns>

oc secrets link default harbor-pull --for=pull -n <ns>

⚠️ Harbor 的 robot 帳號含 $+,shell 裡一定要單引號,
不然會被展開成空的——而錯誤訊息只會說認證失敗

而且 OpenShift AI 的 pod 不都用 default SA:

oc get pods -n <ns> -o custom-columns='NAME:.metadata.name,SA:.spec.serviceAccountName'

每一個用到的 SA 都要連。


步驟五:⭐ 真的拉一次,然後看錯誤訊息分類

oc run pulltest --rm -it --restart=Never \
  --image=registry.internal:8088/odh/some-image:3.5 -- echo ok

失敗的話,訊息會告訴你是三件事裡的哪一件

訊息 是哪一件 回去看
manifest unknown A:image 不在那裡 步驟二
還是去 quay.io 拉 B:mirror 沒生效(多半是用了 tag) 步驟三
x509: certificate signed by unknown authority C:不信任 registry 步驟四 TLS
unauthorized: authentication required C:沒憑證或沒連上 SA 步驟四 pull secret
no route to host 網路/防火牆 不是這篇的問題

這張表能省下很多亂試的時間——五個症狀對應五個完全不同的原因。


步驟六:別忘了 image 以外的東西

這是離線環境最容易漏的一節。

東西 離線會怎樣 怎麼辦
pipeline 的 packages_to_install ❌ pip 連不到 PyPI 自己 build base image
workbench 裡 pip install ❌ 同上 內部 PyPI mirror
HuggingFace 模型下載 ❌ 連不到 先下載,放 S3
operator 更新 ❌ OperatorHub 連不到 離線 catalog

最後一列常被忽略,然後半年後要修 CVE 時才發現升不了級。
評估離線可行性時,把「怎麼更新」一起問。


怎麼確認做對了

檢查 怎麼看
1 image 在、digest 一致 skopeo inspect
2 IDMS 存在、節點更新完 oc get idmsoc get mcp
3 Secret 連上了 SA oc get sa <sa> -o jsonpath='{.imagePullSecrets}'
4 真的拉得下來 步驟五那個 pod
5 權限是最小的 用 pull 帳號試推,應該要 401
6 斷網再試一次 見下

第 6 項是唯一真的驗證:

把叢集對外的連線切掉,然後刪掉一個 pod 讓它重拉。

起得來,才代表你真的離線可用。
沒做這件事,你不知道有多少東西是靠著「其實還連得到」在跑。


常見問題

Q:oc mirrorskopeo 選哪個?
A:oc mirror 能整批處理 operator catalog,適合初次建置
skopeo 精準,適合補漏和日常維護。兩個都會用到。

Q:要 mirror 多少個 image?
A:沒有人能事先列全。 先在能連外網的環境裝一次,然後:

oc get pods -A -o json | jq -r '.items[].spec.containers[].image' | sort -u

這份清單就是你的搬運清單。

Q:pull 和 push 可以用同一個帳號嗎?
A:不要。給推送權限的憑證如果被用在每個拉取的 pod 上,
任何能進那個 pod 的人都能覆蓋你的 image。

Q:robot 帳號會過期嗎?
A:Harbor 的 robot 可以設有效期,預設有
症狀是「昨天還好好的,今天全部拉不到」——把到期日記進行事曆

Q:這些設定能寫進 GitOps 嗎?
A:IDMS 和 image config 是 cluster-scoped 的 CR,可以,而且建議一定要——
這是重建叢集時最容易漏的一塊。Day 25 會談。


  • 叢集:CRC 2.63.0 · OpenShift 4.22.7 · Kubernetes v1.35.6
    單節點 13 vCPU / 40 GiB / 120 GB,叢集內沒有 GPU
  • Operatoropendatahub-operator.v3.5.0(即 RHOAI 3.x 的上游開源版)、
    cert-manager-operator.v1.20.0(3.x 的必要相依,2.x 不需要)
  • 開啟的元件kserveaipipelinesdashboardworkbenchesmodelregistry
  • 叢集外:Harbor v2.15.2(私有 registry)、MinIO(S3),跑在宿主的 podman 上
  • 宿主:Framework Laptop 16(Ryzen AI 7 350 · 8C/16T · 64 GB · 1 TB NVMe)

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


補充資料

  • 🧪 這篇用到的 YAML/腳本:https://github.com/ryanGTR/openshift-ai-30days
  • 🤖 lab 服務的那個模型(從零手刻的小 GPT):https://github.com/ryanGTR/llm-from-scratch

你們的叢集連得到外網嗎?如果不能,image 現在怎麼進來的? 留言或到原文留言都可以,我會回。


上一篇
Day 19:下線與回滾——把東西拿掉這件事
下一篇
Day 21:權限——誰能做什麼
系列文
OpenShift AI 簡易入門30天23
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言