1. 這是什麼

Kueue 是 Kubernetes 的工作排隊器。

一般的 k8s 排程是「有資源就跑,沒資源就 Pending」。 Kueue 多了一層:先排隊,輪到你而且資源夠了才送進去跑

Java 類比ThreadPoolExecutor 的那個 queue。 沒有它,每個任務都直接搶執行緒;有了它,任務先排隊, 而你可以決定誰先誰後、每個團隊最多能佔多少。

在 AI 平台上這件事特別重要,因為訓練工作又大又長—— 一個沒排隊機制的叢集,先送出的大任務會把 GPU 全部佔住, 後面的人只能等,而且不知道要等多久。


2. 什麼時機需要它

當「誰能用 GPU」開始需要規則的時候。

具體的訊號:

  • 有人抱怨「我的訓練排不進去」,而你查不出是誰佔住的
  • 兩個團隊共用一批 GPU,開始為了先後順序吵架
  • 有人一次送十個任務把卡佔滿,其他人整天做不了事

如果你只有一個團隊、GPU 也只有一兩張——ResourceQuota 就夠了, 不需要 Kueue。 排隊機制的價值在「多方競用」,不在「資源不夠」。


3. ⭐ 怎麼用:以及它為什麼一開始就擋你

照直覺,開一個元件就是把它設成 Managed

oc patch dsc default-dsc --type=merge \
  -p '{"spec":{"components":{"kueue":{"managementState":"Managed"}}}}'
Error from server (Forbidden): admission webhook
"datasciencecluster-v2-validator.opendatahub.io" denied the request:
Managed is no longer supported as a managementState

「不再支援」——但它沒告訴你該用什麼。

而更混亂的是,去看 CRD 的 schema:

oc get crd datascienceclusters.datasciencecluster.opendatahub.io -o json | \
  jq -r '.spec.versions[] | select(.name=="v2") |
         .schema.openAPIV3Schema.properties.spec.properties.components.properties
         | to_entries[] | "\(.key)\t\(.value.properties.managementState.enum)"'
kueue          ["Managed","Unmanaged","Removed"]     ← Managed 還在 enum 裡
kserve         ["Managed","Removed"]
aipipelines    ["Managed","Removed"]
...

CRD 的 enum 裡明明列著 Managed

所以你會遇到這個狀況:照 schema 寫是合法的,但 webhook 會擋。

正解是 Unmanaged

oc patch dsc default-dsc --type=merge \
  -p '{"spec":{"components":{"kueue":{"managementState":"Unmanaged"}}}}'
# patched

Unmanaged 的意思是:「我要用 Kueue,但 operator 不負責裝它,我自己裝。」

在 RHOAI 上,你要另外裝 RHBOK(Red Hat Build of Kueue)這個獨立 operator。

設完之後:

KueueReady   False   PreConditionFailed: pre-conditions not met

這是正確的狀態——它在說「你選了 Unmanaged,但我還沒看到你裝的那個 Kueue」。


4. ⭐ 這件事真正的教訓:兩層驗證會不一致

Kubernetes 的資源驗證有兩層:

誰做 依據
① Schema 驗證 API server CRD 的 OpenAPI schema(enumrequired、型別)
② Admission webhook operator 自己寫的程式 任意邏輯

第二層可以拒絕第一層允許的東西,而且兩層可能不同步—— CRD schema 是宣告在 YAML 裡的,webhook 是程式碼, 改了程式碼忘了更新 schema,就會出現這種情況。

對你的實際影響

你不能只靠 oc explain 或 CRD schema 來決定怎麼寫 YAML。

oc explain dsc.spec.components.kueue.managementState
# 會告訴你可以填 Managed —— 而那是錯的

唯一可靠的驗證是 --dry-run=server

oc apply -f my-dsc.yaml --dry-run=server

--dry-run=server真的送到 API server 跑完整個 admission 流程 (包含所有 webhook),只是不寫進 etcd。

⚠️ 注意不要用 --dry-run=client——那個只做本機的 schema 檢查, 完全不會碰到 webhook,也就抓不到這個問題。

這一條可以直接寫進你的變更流程: 所有要進正式環境的 YAML,先在測試叢集 --dry-run=server 過一次。 那比人工 review 有效得多,因為它跑的是真正會擋你的那段程式。


5. 關鍵指標

  「跑完了」 ⭐「做對了」
Kueue KueueReady=True 送一個超過配額的任務,它真的排隊而不是直接跑

第二欄跟這個系列其他篇是同一個模式:排隊機制的價值在它會擋東西。 沒擋過任何東西的排隊器,跟沒有排隊器一樣。


6. 什麼時候不需要它

  • 單一團隊、GPU 不需要搶
  • ResourceQuota 就能表達你的規則(例如「這個 namespace 最多兩張卡」)

Kueue 解決的是「順序」與「公平」,不是「上限」。 如果你要的只是上限,ResourceQuota 更簡單而且不用多裝一個 operator。


7. 我沒驗過的部分

沒有實際裝 RHBOK 也沒有跑過排隊——我的叢集只有一個節點、 沒有 GPU、CPU 還已經用到 99%,跑不了有意義的排隊實驗。

所以這篇能給你的是:怎麼開、為什麼被擋、正解是什麼, 以及那個兩層驗證的教訓。排隊行為本身我沒有證據。


你們有在用 Kueue 或其他排隊機制嗎?還是靠 namespace quota 硬擋?

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