技術專欄

TECH

RUN:AI 企業級 AI 資源調度實戰例析

技術專欄 2026.09.11
RUN:AI 企業級 AI 資源調度實戰例析

GPU 伺服器是企業 AI 基礎設施中金額可觀的一筆投資,但在許多環境裡,這些算力有相當比例的時間處於閒置。問題通常不是「用不到」,而是「分不好」。有 Run:ai 時,GPU 成為可以動態調度的共用資源池。一整天下來,GPU 幾乎沒有空檔,同一筆硬體投資能產出的運算量大幅提升,設備的投資效益才真正發揮出來。

RUN:AI 企業級 AI 資源調度實戰例析

Run:ai 八項機制實測:每個機制的觸發條件與邊界

報告單位:豐康科技 軟體部門

測試期間:2026 年 9 月

報告性質:第一手實測紀錄。除前言示意圖外,所有畫面均為實機操作截圖,數據來自實際執行結果

前言:閒置的 GPU,是最昂貴的浪費

GPU 伺服器是企業 AI 基礎設施中金額可觀的一筆投資,但在許多環境裡,這些算力有相當比例的時間處於閒置。問題通常不是「用不到」,而是「分不好」。

以最常見的兩類工作負載,也就是模型訓練與推論服務為例:

動態調整工作負載:有無 Run:ai 的訓練與推論負載分布(示意圖)

沒有 Run:ai 時,常見的做法是把 GPU 固定切分給訓練與推論,彼此互不相通。問題是,這個比例怎麼切都不對:

  • 訓練切多了,白天上班時段推論資源不足,每一筆請求的回應時間都被拉長。員工等待 AI 回應的每一秒,對企業來說都是生產效率的損失。
  • 訓練切少了,訓練工作要跑更久;更嚴重的是,推論負載跟著上班時間走,分給推論的那一塊在下班後將近三分之二的時間都空著,切得越多,浪費越多。

以圖中各切一半的配置來算一筆帳。一台 B300 伺服器的導入成本直逼 2,000 萬元,再加上專責的 AI 平台管理團隊、廠商維護合約、電力與空調,每月營運費用抓 10 萬元並不誇張。以 5 年計算,總持有成本約 2,600 萬元。

分給推論的那一半,下班後將近三分之二的時間閒置,算下來 5 年約有 870 萬元的算力在空轉,相當於每個月白白燒掉 14 萬元以上,比整個月的營運費用還多。換句話說,整筆投資有三分之一買了卻沒在用。這還沒算進週末與國定假日;若把週末整天也算進去,推論那一半的閒置比例會超過七成。

有 Run:ai 時,GPU 成為可以動態調度的共用資源池。夜間推論流量低,訓練工作借用閒置算力,把整台機器跑滿;早上推論需求回升,排程器自動把借出的資源收回給推論服務,部分訓練工作讓出位置,待資源釋出後再從 checkpoint 接續;傍晚推論退潮,訓練再度擴張。一整天下來,GPU 幾乎沒有空檔,同一筆硬體投資能產出的運算量大幅提升,設備的投資效益才真正發揮出來。原本空轉的這三分之一,就能轉成實際的訓練產出。

延伸閱讀:K3 出現了,要自建嗎?我們先來算帳!


為什麼要實測

當然,示意圖描繪的是理想狀態。要讓它在真實環境中成立,企業最常遇到的不是效能問題,而是治理問題

  • 正式服務會不會被測試工作影響?
  • 各單位的資源怎麼分才公平,又不會有人閒置浪費?
  • 離峰時段能不能把閒置算力借給別人,白天再自動收回?
  • 高優先權的工作進來時,正在跑的訓練會不會前功盡棄?

這些問題在規劃階段都可以「按照文件說明」回答,但文件寫的是設計意圖,實際行為往往有前提條件。我們決定把整套規劃在實驗環境完整建置一次,逐項驗證。

本文的價值不在於「Run:ai 有這些功能」,而在於每個機制的觸發條件與邊界——這些是文件不會寫、但實際導入時一定會踩到的地方。


驗證環境與縮放方法

驗證環境刻意用了最小配置:

項目 規格
叢集管理 NVIDIA Base Command Manager 11.33.1
調度平台 Run:ai 2.25.28
Kubernetes v1.34.10
GPU 單張 NVIDIA L40S 48GB
推論框架 vLLM 0.27.1

目標架構是 8 張 GPU 的雙分區規劃(正式區 2 張、開發測試區 6 張)。我們採用等比例縮放

1 張 L40S ≙ 8 張目標 GPU

正式區分到 fraction 0.25(等於 2 張卡),開發測試區 0.75(等於 6 張卡)。

這樣做的前提是:調度機制與卡的絕對數量無關。配額怎麼分、誰能搶誰、什麼時候回收,這些邏輯在一張卡和八張卡上完全一樣。至於容量能塞多少參數的模型,那是另一個維度的問題,不在本次驗證範圍。

因為切片變小,實測用的是 0.5B 到 2B 等級的模型,不影響任何機制的驗證。


架構設計:用配額階層取代物理分區

Run:ai 的資源治理有兩層:Department(部門)定義分區總量,Project(專案)定義用途額度。

層級 名稱 Quota Over-Quota 對應目標
Department prod 0.25 2 張卡
└ Project prod-inference 0.25 關閉 整卡配置、24/7 常駐
└ Project prod-training 0 開啟 僅借用閒置、自動歸還
Department dev-test 0.75 6 張卡
└ Project dev-shared 0.37 關閉 開發公用區(3 卡)
└ Project 四個研發單位 各 0.09 開啟 各單位保障額度

 

Department 層級:兩個新建部門與各自的配額

Project 層級:七個專案,可見各自的 GPU quota 與已配置量

這個設計的關鍵在於 prod-training 的配置。它的保證配額是 0,但開啟 over-quota——意思是它只吃 prod-inference 沒用滿的部分。當推論服務擴張需要資源時,訓練會被自動回收。這正好對應「推論最優先,訓練少但可能會有」的需求,而且不需要犧牲推論任何保證配額。


觀念釐清:Run:ai 有兩種「優先權」

這是導入時最容易設錯的地方,值得先講清楚。

Workload Priority:掛在工作負載上

建立工作負載時可設定的優先權,共七個等級

系統預設值:

工作類型 優先權 可否被中斷
Inference(推論) very-high (150) Non-preemptible
Workspace(工作區) high (125) Non-preemptible
Training(訓練) low (40) Preemptible

這個決定誰擠掉誰,是真正的搶佔依據。

Over-Quota Priority:掛在專案/部門上

Project 層級的 GPU over quota 設定:開關、Max GPU allocation、Rank 與 Weight

同一 Rank 內的多個專案依 Weight 比例分配閒置資源

這個設定由兩部分組成:Rank(High / Medium High / Medium / Medium Low / Low)決定排序,Weight(數值)決定同一 Rank 內的分配比例。介面上的長條圖會即時顯示「這個專案的權重佔多少比例」。

關鍵區別:Over-Quota Priority 只在借用閒置資源時生效,它是「分配權重」而非「位階」。設定得再高,也不會讓一個專案變得不可被回收;設定得低,也不影響它拿到自己的保證配額。

一個容易踩的坑

有人會想「我這個訓練很重要,把 priority 調成 high」。結果反而更糟——一旦調高,該工作負載就失去使用 over-quota 的資格。如果它的保證配額是 0,就會永遠停在 Pending。


驗證一:權限邊界

我們建立了一個研究員帳號,角色為 L1 Researcher,Scope 僅限一個專案。

研究員視角:標題旁的 View only 標記,清單中只有自己被授權的專案

實測差異:

面向 管理者 L1 Researcher
側邊選單 Analytics / Resources / Organization / Access / Policies / Admin 僅 Workload manager
Projects 頁面 可編輯配額 View only
可見專案 全部 7 個 僅 1 個
建立 Workspace / Training
建立 Inference 權限不足

最後一項最有價值。許多企業的需求是「研究員可以自由做實驗,但把模型變成對外服務必須循程序申請」——Run:ai 的內建角色就達成了這件事,不需要額外的流程管控或客製開發。

另外值得注意:其他專案對這個帳號來說是完全不可見的,不是「看得到但點不進去」。這降低了誤操作的可能性,也減少研究員需要理解的複雜度。


驗證二:多模型併行與統一入口

三個推論服務同時運行,各佔不同 fraction:

三個 vLLM 服務分屬不同專案,GPU compute request 分別為 0.2 / 0.12 / 0.1

從實體卡的角度看:

單張 L40S 上同時有三個 VLLM::EngineCore 程序,記憶體用量分別為 5028 / 9060 / 4606 MiB

模型權重透過 Data Source 資產掛載——資產定義一次,所有專案都能重複使用,不需要每個單位各自設定路徑。

對使用者而言,不需要記住三個不同位址。我們在前面架設了 LiteLLM 作為統一閘道:

統一入口列出所有可用模型,並可做金鑰、成本與團隊管控


驗證三:配額上限是結構性保證

在一個保證配額只有 0.09 的專案裡,一口氣提交 7 個訓練任務(每個 0.09)。

七個 Training 工作負載,優先權皆為 low (40)、Preemptible

結果:5 個 Running,2 個 Pending

各 namespace 的 fraction 加總:0.45 + 0.30 + 0.12 = 0.87

排程器對 Pending 的工作給出了明確原因:

排程器事件:dev-test 部門配額已達上限

OverLimit: dev-test quota has reached the allowable limit of GPUs.
Limit is 0.76 GPUs, currently 0.75 GPUs allocated and workload requested 0.09 GPUs.

這裡最值得注意的數字是 0.87——整台機器還有 0.13 是空的。機器有空卡,但制度不讓開發測試區拿,因為那部分落在正式區的保留範圍內。

這就是結構性保證:不依賴任何人的自律,是排程器根本不給。

實測補充:over-quota 上限怎麼算

實測確認 over-quota 的可用空間是「部門 limit 減去部門已用量」,而不是「其他專案未使用的 quota 加總」。兩種算法會得出不同的結果(5 個 vs 4 個),這個差異在容量規劃時會有影響。


驗證四:跨部門的資源隔離

延續上一個情境——開發測試區已經觸頂,此時在正式區提交一個推論服務。

結果:立即 Running。 因為兩個部門的配額完全獨立,開發區滿不滿與正式區無關。

正式區增加負載後:0.45 + 0.30 + 0.24 = 0.99,機器接近滿載

同時,先前的訓練任務完全不受影響——epoch 持續遞增、loss 持續下降、累計訓練時間連續,沒有中斷或重啟的跡象。

記憶體隔離的實證

訓練容器啟動時會印出它看到的顯存總量:

視角 顯存
容器內 visible_mem_total 4146 MiB
實體卡 memory.total 46068 MiB

容器只看得到自己被分配的那一份,看不到卡上其他工作負載的存在。這是驅動層的硬隔離,不是靠應用層自律。

一個常見的疑問

nvidia-smi 顯示的實際用量(18713 MiB)低於 fraction 加總(0.99 ≈ 45GB),這正常嗎?

正常。fraction 是排程層的預留額度,而非實際寫入量nvidia-smi 顯示的是當下實際寫入的顯存。當時的 5 個訓練任務雖然把 GPU 使用率跑滿,但每個只佔約 300 MB 的 VRAM,卡上實際寫入的記憶體幾乎全來自三個 vLLM 服務,而推論的 KV cache 也未滿載,所以實際佔用低於配額。

換個說法:額度是承諾,不是浪費。而 Run:ai 的 Workloads 清單有一個 Idle GPU devices 欄位,正是用來抓「拿了配額卻沒在用」的情況。


驗證五:跨單位借調與回收(Reclaim)

這一段驗證「離峰借用、原擁有者需要時自動歸還」的實際行為。

前置:A 單位(保證配額 0.09)借用了大量他人閒置額度,跑著 4 個訓練任務。

第一個實驗:B 單位提交 3 個工作

結果:只有 1 個 Running,另外 2 個 Pending,而且 A 單位完全沒有被動到

第二個實驗:C 單位(完全未使用資源)提交 1 個工作

結果:立即觸發回收

A 單位有一個工作轉為 Pending,C 單位的工作同時 Running

A 單位某個 pod 年齡歸零(2m1s)並轉 Pending,C 單位的工作同時起來(2m5s)

關鍵發現:回收的觸發條件

兩個實驗的差異解釋了整個機制:

情境 是否觸發回收
B 單位已拿到保證配額,再要更多 不觸發,Pending 等待
C 單位一分未拿,提出保證配額需求 立即觸發

回收只在某個單位拿不到它的「保證配額」時才發生,不是「有人提出需求」就發生。

已經拿到保證額度的單位,再多要的部分屬於借用,排程器不會為此中斷別人的工作。

這對導入規劃很重要。如果客戶期待的是「一提需求就能立刻拿回全部額度」,實際行為會有落差。但反過來說,這也意味著回收是最後手段,不會沒事就打斷正在跑的任務——對訓練工作的穩定性是好事。

另外值得注意,被回收的是 A 單位借來的部分;B 單位那個正在使用自己保證配額的工作完全未受影響。回收非常精準。


驗證六:同單位內的搶佔與訓練續跑(Preemption)

前一段是單位之間的借調。這一段是單位內部的搶佔,機制完全不同。

前置:某專案配額用滿,內含一個推論服務(150)與一個訓練任務(40)。此時提交第二個推論服務。

新推論服務 Initializing,原推論服務不受影響,訓練任務轉為 Pending

訓練被中止時的行為

訓練跑到 epoch 21,收到 SIGTERM 並確認 checkpoint 已落盤

[03:22:19] epoch  21/3000 loss=0.6989 test_acc=0.8223 best=0.8223 cum_train_time=63.0s
[03:22:22] !!! received SIGTERM (preempted) - latest checkpoint is already on disk

資源釋放後自動續跑

刪除新的推論服務後,訓練自動重新排程:

RESUMED 後三個數值與中斷前完全一致,epoch 從 22 繼續

[03:23:05] >>> RESUMED from checkpoint: epoch=21 elapsed=63.0s best_acc=0.8223
[03:23:09] epoch  22/3000 loss=0.7013 test_acc=0.8225 best=0.8225 cum_train_time=66.4s

epochelapsedbest_acc 三個數值與中斷前最後一行完全一致。訓練成果沒有任何損失。

Reclaim ≠ Preemption

這兩個機制經常被混為一談,但觸發條件和行為都不同:

  Reclaim Preemption
範圍 跨佇列(單位之間) 同佇列(單位內部)
驅動因素 保證配額不足 優先權高低
對象 over-quota 借用的部分 同佇列中優先權較低者

搞混這兩者會導致測試往錯誤方向進行——例如在開著 over-quota 的專案裡試圖驗證搶佔,排程器會直接走借用路徑,永遠不會觸發搶佔。

實作重點:搶佔的必要條件

搶佔要成立,佇列必須沒有 over-quota 的出路

實測確認:Project 層的 limit == quota 就足夠,不需要對 Department 層做額外設定。原因是工作負載在 Project 層就被擋住,根本走不到 Department 那一層。

更方便的是,在介面上關閉 Project 的「Allow workloads to exceed the project quota」時,系統會自動把 limit 設為等於 quota——不需要任何額外的手動操作。


驗證七:擴縮與閒置治理

Autoscaling

設定 min/max replica 與觸發條件(Concurrency > 2)

壓測期間擴張到 3 個 pod

Desired 與 Actual 曲線,1 → 3 的擴張過程

擴縮條件除了併發數(Concurrency),也支援 Throughput、Latency 與自訂指標。

Scale-to-Zero

Minimum 設為 0

Running / requested pods 顯示 0/0-3

冷啟動實測:

第一次請求(冷啟動)56.6 秒,第二次(已有 replica)4.6 秒

情境 時間
冷啟動(scale from zero) 56.6 秒
熱請求 4.6 秒

56 秒的延遲來自 vLLM 重新載入模型與啟動推論引擎。

這個數字直接決定了適用場景:對 24/7 低延遲的正式服務,Scale-to-Zero 不可接受,應維持 min replica ≥ 1;對開發測試區的實驗性服務,這是很好的資源回收手段。

Idle GPU Timeout

Workspace 在閒置逾時後自動轉為 Stopped

設定閒置門檻後,佔用 GPU 但沒有實際運算的工作負載會被自動停止。

適用範圍是這個功能的關鍵限制:Workload type 只有三個選項——Training、Preemptible workspaces、Non-preemptible workspaces,不包含 Inference

原因是推論服務的 replica 數由 Knative 的自動擴縮機制掌控,Run:ai 的閒置回收不會去干預。所以推論服務的閒置治理,只能靠前面的 Scale-to-Zero。


驗證八:用量可視化與報表

叢集總覽:GPU 配置量、閒置量、各專案的平均配置與使用率

Overview 頁面同時顯示 Allocated GPU devices 0.51Idle allocated GPU devices 0.42——後者是「已配置但沒在運算」的量。這個對比很有說明力:配置率高不等於使用率高。

有意思的是,長期閒置的往往是常駐推論服務(沒有流量時整份配額都空著),而訓練任務通常滿載。這正好解釋了為什麼推論需要 Scale-to-Zero 這類機制,也說明正式區的 GPU 使用率偏低是設計上的取捨(保證低延遲),而非浪費。

Consumption Report

可依 Project / Department / Cluster 分段,選擇時間區間

輸出欄位包含 GPU allocation / deserved quota / over quota / idle hours 與 CPU、記憶體用量

報表輸出的欄位設計值得一提——它同時給出 GPU deserved quota hours(應得配額)、GPU over quota hours(超額借用)與 GPU idle hours(閒置時數)。這三個數字放在一起,就能回答「這個單位是不是常常在借別人的資源」以及「借了之後有沒有真的在用」。


實作要點整理

以下是驗證過程中確認的行為,這些在文件上不一定寫得清楚:

排程行為

項目 實測結果
Reclaim 觸發條件 某佇列拿不到保證配額,而非「有人提出需求」
Preemption 觸發條件 同佇列內優先權高低,且無 over-quota 出路
Preemption 前提 Project 層 limit == quota 即足夠,不需設定 Department 層
over-quota 可用上限 部門 limit − 部門已用量
調高 workload priority 的副作用 失去 over-quota 資格,quota 為 0 時將永久 Pending

介面限制

項目 實測結果
Quota 輸入精度 僅接受兩位小數
Department 的 Max GPU allocation 介面強制大於 quota,無法設為相等
關閉 Project over-quota 系統自動將 limit 設為等於 quota

關於 Department 的 Max GPU allocation 無法等於 quota:實測中部門配額 0.75、上限被迫設為 0.76。這 0.01 的差距在實務上無害——0.01 fraction 約等於 480MB,小於任何實際工作負載的需求,逃生口存在但寬度不足以通過。

功能適用範圍

功能 適用 不適用
Idle GPU Timeout Training、Workspace Inference
Scale-to-Zero Inference Training、Workspace

兩者剛好互補,涵蓋了所有工作負載類型的閒置治理。


結論

八項機制全部驗證通過,其中三項的實際行為與初始預期有差異,值得在規劃階段就納入考量:

一、回收是需求驅動,而非時鐘驅動。 平台沒有「依時段自動調整配額」的原生功能。實際運作是:夜間訓練以 over-quota 借用閒置資源,白天推論負載提出需求時排程器自動回收。若一定要時間驅動,需要外部排程呼叫 API 改寫配額,屬額外開發工項。

二、回收的門檻比想像中高。 只有在某個單位拿不到保證配額時才會發生。這對訓練工作的穩定性是好事,但與「一提需求就立刻拿回」的直覺不同。

三、閒置治理需要兩套機制。 Idle GPU Timeout 管不到推論服務,而推論服務恰恰是最容易長期閒置的類型。Scale-to-Zero 是唯一的手段,但要接受冷啟動延遲。

至於最核心的問題——正式服務會不會被測試工作影響——答案是三道獨立防線:

  1. 開發測試區的 Max GPU allocation:即使正式區完全閒置,開發區也拿不到那部分資源
  2. 正式區推論服務的保證配額:部門內部也保證拿得到
  3. 推論類別的預設值:Non-preemptible + 最高優先權,一旦排程即不被中斷

第一道是結構性的,不依賴任何人的自律。這也是整套設計中最值得強調的一點:好的資源治理不是靠規範,是靠排程器根本不給


附錄:驗證用的模型與參數

模型 fraction --gpu-memory-utilization --max-model-len
Qwen2.5-1.5B-Instruct 0.12 0.75 4096
gemma-2-2b-it 0.20 0.85 2048
Qwen2.5-0.5B-Instruct 0.10 0.80 4096

所有 vLLM 工作負載均加上 --enforce-eager

在 GPU fraction 環境下,--gpu-memory-utilization 是相對於切分後的可見顯存,不是整張卡。此外,CUDA context 等固定開銷不隨 fraction 縮小,在小切片上的佔比會顯著放大——這是規劃小 fraction 時容易低估的部分。