昨天我的結論是:Kubernetes 不會讓 GPU 變多,它真正解決的是多節點、多服務與多種資源之間的治理。
但要驗證這件事,不能一開始就拿一大包 Helm chart 往叢集裡裝。當 Pod 卡在 Pending,或者 container 裡的 nvidia-smi 失敗時,如果我分不出是 Driver、Container Toolkit、Device Plugin 還是 Scheduler 的問題,Kubernetes 只會讓故障多一層包裝。
所以今天我先做兩件事:畫出後續 28 天會共用的參考架構,然後在手邊這台機器上跑一次 preflight,看看我現在真的能驗證到哪一層。
一開始畫圖時,我最容易把 Device Plugin 和 Container Toolkit 混在一起。實際把路徑拆開後,它們處理的是不同問題:
Kubernetes Control Plane
API Server ─ Scheduler ─ Controller
│ │
│ 根據 Node 可用資源做 placement
▼
┌────────────────────── GPU Node ───────────────────────┐
│ Kubelet │
│ ├── NVIDIA Device Plugin ──► 回報 nvidia.com/gpu │
│ └── Container Runtime ────► NVIDIA Container Toolkit │
│ │ │
│ ▼ │
│ Driver ─ GPU / VRAM │
│ │
│ Model Serving Pod ──► Model Cache / Persistent Storage │
│ │ │
│ ├────────────► Service / Ingress ──► Client │
│ └────────────► Metrics / Logs / Traces │
└──────────────────────────────────────────────────────────┘
外部依賴:Container Registry、Model Registry / Object Storage
我把每一層都配上一個可觀察的現象:
| 層次 | 我想確認的問題 | 異常時實際會看到什麼 |
|---|---|---|
| GPU + Driver | Host 有沒有辨識到裝置 | Node 上的 nvidia-smi 就失敗 |
| Container Toolkit / Runtime | Container 能不能取得 GPU | Host 看得到,container 看不到 |
| Device Plugin | Kubelet 有沒有回報裝置 | Node 沒有 nvidia.com/gpu capacity |
| Scheduler | Pod 能不能放到合適節點 | Pod Pending,event 顯示資源不足 |
| Storage / Registry | Image 與模型權重能不能取得 | ImagePullBackOff 或模型載入失敗 |
| Serving / Service | 模型是不是真的準備好接流量 | Pod Running,API 卻還不能用 |
這張表改變了我後續的檢查順序。如果 Host 上的 nvidia-smi 都失敗,就還沒有必要追 Scheduler;如果 Node 已經公告 nvidia.com/gpu,Pod 卻還在 Pending,才往 resource request、label、taint 與 affinity 的方向看。
我對這套測試環境的最小規劃,是一個 control-plane node 與一個 GPU worker。Control Plane 負責維持叢集 desired state;GPU worker 才放 inference、embedding、ASR 或 batch job。如果之後加入 CPU worker,ingress、controller 和一般 API 也會盡量移出 GPU node。
我不是因為 control plane 「絕對不能」跑 workload 才這麼拆,而是希望後面做資源競爭與故障注入時,不要連用來觀察和恢復叢集的控制面也一起被拖下水。
GPU worker 之後會加上型號、VRAM 級距等 label,也會用 taint 擋住一般 Pod。這些設定會在 Day 06 到 Day 09 再實際驗證;今天我只先把角色邊界留下來。
畫到 storage 這一段時,我原本也想用一個 PVC 簡化掉。但把復原方式列出來後,就發現三種資料根本不應該用同一種策略:
| 資料 | 我關心的特性 | 目前的處理方式 |
|---|---|---|
| Container image | 有版本、可以重新拉取 | OCI Registry,部署時固定 digest |
| Model weights | 很大、讀取密集、通常可重建 | Model/Object Registry + Node cache |
| 應用與使用者資料 | 不能任意遺失 | Stateful storage + backup/restore |
模型 cache 不見的代價是下次啟動變慢;資料庫不見卻可能無法復原。這個差異也會影響 readiness:container process 啟動不代表模型已下載、權重已載入、GPU 已分配且 warm-up 已完成。Running 和「可以接流量」不能畫上等號。
第一個 Pod 不會先跑模型,而是只做 capability check。這樣失敗時變因比較少:
apiVersion: v1
kind: Pod
metadata:
name: gpu-capability-check
spec:
restartPolicy: Never
containers:
- name: cuda-check
image: nvidia/cuda:12.4.1-base-ubuntu22.04
command: ["bash", "-lc"]
args: ["nvidia-smi"]
resources:
limits:
nvidia.com/gpu: 1
這裡只寫 GPU limits 不是漏寫。Kubernetes 對 GPU extended resource 允許只寫 limits,request 會採相同值;如果 request 和 limit 都寫,兩者必須相等。
到真正的 GPU 叢集後,我會照這個順序查:
# 1. Node 是否對 Kubernetes 公告 GPU
kubectl get nodes \
-o custom-columns='NAME:.metadata.name,GPU:.status.allocatable.nvidia\.com/gpu'
# 2. Device Plugin 是否正常
kubectl -n kube-system get pods -l name=nvidia-device-plugin-ds
# 3. 套用 capability Pod 並查看 placement
kubectl apply -f k8s/gpu-capability-check.yaml
kubectl get pod gpu-capability-check -o wide
# 4. 從 container 裡看取得的 GPU
kubectl logs gpu-capability-check
# 5. Pending 時直接看 Scheduler 理由
kubectl describe pod gpu-capability-check
我目前操作的是 arm64 macOS,不是 NVIDIA GPU Node,也沒有連入可用的 GPU Kubernetes 叢集。我還是先跑了 preflight,但把實驗結果記成 skipped:
PYTHONPATH=src python3 scripts/record_preflight.py \
--track kubernetes \
--kind kubernetes-gpu-preflight \
--experiment-id kubernetes-day02-preflight-20260916-001 \
--require-executable kubectl \
--require-executable nvidia-smi \
--reason "No GPU Kubernetes context was accessed; no workload was executed."
我在終端看到的結果是:
required executables: 2
available executables: 1
kubectl: available
nvidia-smi: unsupported
status: skipped (no cluster workload was executed)
我又回到 shell 直接確認:
$ command -v kubectl
/usr/local/bin/kubectl
$ command -v nvidia-smi || echo 'nvidia-smi: not found'
nvidia-smi: not found
這個畫面其實正好驗證了前面畫的邊界:有 kubectl 只代表這台機器有 client,與 Node 有沒有 GPU、Device Plugin 有沒有回報資源、Pod 能不能被排程,都還是不同的事。
如果我只為了讓文章看起來完整,貼一段預期的 kubectl logs 就宣告成功,那後面的排程、隔離與恢復實驗也沒有可信度。
Day 03 我需要回到一台可存取的 NVIDIA Linux GPU node,並且要依序看到:
nvidia-smi 可以列出實際裝置。nvidia.com/gpu。這一段確實需要 GPU 雲端資源或可存取的實體節點。我會等真正跑過 kubectl get、describe、logs 和 GPU telemetry 後,再放成功畫面。
我今天沒有得到一個成功的 GPU Pod,但我已經知道下一次失敗時要停在哪一層找原因。
Driver 讓 Host 看見 GPU,Toolkit 讓 container 看見 GPU,Device Plugin 讓 Kubernetes 看見 GPU,Scheduler 才能決定 Pod 要去哪裡。
這就是這張參考架構對我的用途:不是證明系統很完整,而是把每一層應該出現的證據先排好。
下一篇:Day 03|把 GPU 變成 Kubernetes Resource:Driver、Toolkit 與 Device Plugin
本系列以自建、可公開的測試環境驗證設計,不公開任何內部網路、帳號或未公開的實際部署資訊。