
第二週最後一篇。先聲明:這篇是概念篇,而且刻意寫得淺。 我自己沒有在 K8s 上跑過正式的 training pipeline,這裡寫的是從 inference 踩過的坑往回推,再加上讀文件整理出來的理解。有實戰經驗的讀者請多指正。
理由很直覺:兩邊都要 GPU,而 GPU 很貴。開一個 GPU 叢集讓兩邊一起用,不是很划算嗎?
我一開始也這樣想。直到套上 Day 06 的三種型態,才發現 training 和 inference 雖然都靠 GPU,資源性格卻幾乎完全相反。
以線上 inference 和批次 training 為例(批次/離線 inference 的性格比較接近 training,這裡先不展開):
1. 時間長度。 線上 inference 是毫秒到秒級的請求,得一直在線;training 是小時到天級的 job,跑完就結束。一個是 Deployment,一個是 Job,Day 05 講過,這兩個東西本質就不一樣。
2. 對中斷的容忍度。 線上 inference 斷一秒,使用者就看到錯誤;training 被中斷(有設 checkpoint 的話)頂多重跑最近一段。反過來說:training 可以放在便宜的 Spot / preemptible GPU 上,線上 inference 不行。 光這一點,就足以讓兩邊的 node pool 分開。
3. 尖峰的走向。 inference 的尖峰跟著流量走,白天高、半夜低,需要的是「快速 scale」;training 的尖峰是一開跑就吃滿、結束就歸零,需要的是「一次給足」。同一個 autoscaler 很難同時伺候這兩種曲線。
4. 記憶體的用法。 inference 載一次模型反覆用,VRAM 用量相對穩定;training 的 VRAM 跟著 batch size、gradient、optimizer state 變動,通常是「能塞多少就塞多少」。真的擠到一起時(同一張卡、或沒宣告 GPU request 的情況),training 很容易直接把 inference 擠死。
5. 失敗的代價。 inference 失敗影響使用者體驗;training 失敗是白燒幾個小時的 GPU 費用。兩邊的重試策略和告警邏輯完全不同。
行,但要分 node pool。這是我目前的理解:
nodeSelector/node affinity 綁到各自的 node pool,再加 taint/toleration 防別人跑進來。這樣共用的是管理層(一套 kubectl、一套監控、一套 RBAC),資源層則大致隔開(control plane、雲端配額還是共用)。我認為這是多數中小團隊最合理的形態。
真的需要「拆成兩個叢集」的理由,多半來自組織面(不同團隊、分開帳單、合規審查);技術上需要獨立 control plane、升級節奏或故障域時才會拆。
這個系列我一直提成本,因為以我現在的規模(一個人、幾個小專案),一個忘記關的 GPU job,就可能直接吃掉整個月的雲端額度。
把 training 和 inference 分開的另一個好處就在這裡:你可以只對 training 那個 node pool 設嚴格的上限,例如限制 GPU 節點數、給 job 設 activeDeadlineSeconds、用 ResourceQuota 限制 namespace 能要的 GPU 數量,幾乎不會動到 inference 那一側(GPU 配額仍是整個專案共用)。
混在一起的話,不是限制太緊波及 inference,就是放太鬆讓 training 有機會失控。
坦白說,我現在還沒有正式的 training pipeline,模型都是現成的(Whisper 和 embedding 模型都直接下載來用)。所以這篇對我來說是「先把架構想清楚」,不是「踩過坑之後的總結」。
不過光是 inference 這邊的經驗(Day 08),就足以讓我抓到一個原則:GPU 工作負載的部署決策,很大程度取決於「它能不能被中斷」。 想清楚這一題,training 和 inference 要不要分開,答案自然就出來了。
第二週到此結束。下週正式動手:在自己的機器上把 K8s 架起來,然後把第一隻 bot 搬進去。
training 和 inference 都用 GPU,但 training 可以被中斷、線上 inference 不行;光這個差異,就決定了它們該跑在不同的 node pool 上。