模型上線之後,第一件事是打它一發看看。這兩個坑我各花了十分鐘。


一、oc exec ... curl 會告訴你 curl 不存在

oc exec -n llm-serve-demo deploy/llm-scratch-predictor -c kserve-container -- \
  curl -s localhost:8080/model
executable file `curl` not found in $PATH: No such file or directory
command terminated with exit code 1

不是路徑問題,是容器裡真的沒有 curl

現代的 serving image 大多是 distroless 或 minimal base, 連 curlwgetpsnetstat 都沒有。這是刻意的—— 少一個二進位就少一個 CVE,也少一個攻擊者能用的工具。

正確做法是從外面打,不要進去打:

oc port-forward -n llm-serve-demo deploy/llm-scratch-predictor 18000:8000 &
curl -s localhost:18000/model

(如果你真的需要在 pod 內排錯,OpenShift 有 oc debug 可以起一個帶工具的 副本,但那會是另一個容器,網路命名空間不同,要注意你在測的到底是誰。)


二、port 是 8000,不是 8080

我照慣例打 8080,port-forward 說連上了:

Forwarding from 127.0.0.1:18080 -> 8080

然後每一個請求都失敗:

error: an error occurred forwarding 18080 -> 8080:
  failed to connect to localhost:8080 inside namespace ...:
  dial tcp [::1]:8080: connect: connection refused

這個錯誤訊息會誤導你,因為 Forwarding from ... 那行看起來像成功了。 port-forward 只是建立了轉發通道,它不驗證對面有沒有人在聽。

去看容器實際開的 port:

oc get deploy llm-scratch-predictor -o jsonpath='{.spec.template.spec.containers[0].ports}'
# [{"containerPort":8000,"protocol":"TCP"}]

8000。 8080 是 KServe 生態的慣例值,但那是慣例不是規定—— 自訂容器要開哪個 port 是你自己在 YAML 裡寫的。

改成 8000 就通了:

oc port-forward -n llm-serve-demo deploy/llm-scratch-predictor 18000:8000 &
curl -s localhost:18000/model | jq
{"serving_digest":"sha256:4d694be9342d…","in_registry":true,"status":"production",
 "metrics":{"test_loss":3.462,"perplexity":31.88,"test_bpc":4.9946}}

一分鐘的檢查清單

打不到自己的模型服務時,照順序:

# 1. pod 活著嗎(不是 Running 就先看 Init 或 CrashLoop)
oc get pods -l serving.kserve.io/inferenceservice=<name>

# 2. 容器開哪個 port(不要假設 8080)
oc get deploy <name>-predictor -o jsonpath='{.spec.template.spec.containers[0].ports}'

# 3. 應用自己說它 listen 在哪
oc logs deploy/<name>-predictor -c kserve-container --tail=5
# INFO: Uvicorn running on http://0.0.0.0:8000

# 4. 從外面打
oc port-forward deploy/<name>-predictor 18000:<真正的port> &
curl -s localhost:18000/health

第 3 步最可靠——應用啟動時通常會自己印出來它在聽哪個 port, 那比任何設定檔都準。


你們排錯時是用 oc exec 還是 port-forward?有沒有在容器裡裝除錯工具的政策?

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