昨天我在本地做 preflight 時,結果停在 skipped。我手邊有 kubectl client,但沒有連入可驗證的 NVIDIA GPU Kubernetes 叢集。這個結果沒有證明 GPU 可用,卻剛好提醒我:
有
kubectl、Node 有 GPU、Container 看得到 GPU,以及 Scheduler 能分配 GPU,是四件不同的事。
所以 Day 03 不會一開始就部署模型服務。我先把 Host 到 Pod 之間的鏈路逐層驗證,直到 Kubernetes API 裡真的出現 nvidia.com/gpu,再用最小 Pod 取得一張 GPU。
從硬體到 Pod,我把責任拆成四層:
NVIDIA GPU
│
▼
Linux Driver
│ Host 上的 nvidia-smi 可辨識裝置
▼
NVIDIA Container Toolkit + Container Runtime
│ Container 可取得 GPU device 與 driver libraries
▼
NVIDIA Device Plugin
│ 向 kubelet 公告 nvidia.com/gpu
▼
Kubernetes API / Scheduler
│ capacity / allocatable / Pod limits
▼
GPU Pod
每一層都有自己的證據。如果 Host 上的 nvidia-smi 就失敗,問題還沒有到 Kubernetes;Host 正常,Container 卻看不到 GPU,應該先查 Runtime 與 Toolkit;Device Plugin 沒有正常回報,Scheduler 就根本不知道這個節點有 GPU。
這也是為什麼我不想只放一張 nvidia-smi 成功畫面。只證明最底層的 Host 能辨識裝置,不代表 Pod 已經能使用。
Driver 是最底層的前提。這一層失敗時,我會在 Host 上就看到 nvidia-smi 失敗、kernel module 沒有載入,或 Driver 與目前 kernel 不相容。
如果使用雲端服務商提供的 GPU node image,Driver 可能已經預裝。這時不應該未經確認又讓 GPU Operator 管理第二套 Driver;安裝前先弄清楚責任屬於 node image、叢集管理者還是 Operator。
Driver 正常後,Container Runtime 還要知道怎麼把 GPU device 和必要的 driver libraries 交給 container。這是 NVIDIA Container Toolkit 處理的邊界。
我會把這層的驗證與 Kubernetes 分開。如果連最小 GPU container 都無法在節點上取得裝置,先查 Runtime 設定,不要直接將原因歸給 Scheduler。
Kubernetes 透過 Device Plugin framework 處理 GPU 這類特殊硬體。NVIDIA Device Plugin 正常運作後,kubelet 才會把 GPU 數量回報成 extended resource:
nvidia.com/gpu
我會直接查 Node 的 capacity 與 allocatable,不用「Device Plugin Pod 看起來在 Running」當結論:
kubectl get nodes \
-o custom-columns='NAME:.metadata.name,CAPACITY:.status.capacity.nvidia\.com/gpu,ALLOCATABLE:.status.allocatable.nvidia\.com/gpu'
Device Plugin Pod 正常、Node 也出現 nvidia.com/gpu,才代表資源已進入 Kubernetes 的排程模型。
這裡有兩條路。
第一條是分別管理 Driver、Container Toolkit 和 Device Plugin。好處是每一層都看得清楚,適合學習邊界與排錯;代價是版本相容、升級與節點生命週期都要自己處理。
第二條是使用 NVIDIA GPU Operator,可以管理 Driver、Container Toolkit、Device Plugin、DCGM Exporter 等元件,但仍然必須先確認雲端 node image 已經幫忙處理了哪些層次。Operator 不是「有 GPU 就盲目安裝」的按鈕。
實機驗證文件同時保留這兩種情況:若環境已經有 vendor-managed GPU stack,只做驗證;若是可拋棄、且明確授權的測試叢集,才依官方文件安裝 GPU Operator。
只要今天的問題是「Kubernetes 能不能把 GPU 交給 Pod」,就不應該加入模型下載、Storage、Serving framework 和 API 等其他變因。
我準備的 manifest 在 k8s/day03-gpu-smoke-test.yaml:
apiVersion: v1
kind: Pod
metadata:
name: day03-gpu-smoke-test
spec:
restartPolicy: Never
automountServiceAccountToken: false
tolerations:
- key: sku
operator: Equal
value: gpu
effect: NoSchedule
containers:
- name: cuda-check
image: nvidia/cuda:12.4.1-base-ubuntu22.04
command: ["bash", "-lc"]
args:
- >-
nvidia-smi --query-gpu=index
--format=csv,noheader,nounits
resources:
limits:
nvidia.com/gpu: 1
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
GPU 放在 limits 不是漏寫 request。Kubernetes 對 GPU extended resource 允許只寫 limit,此時 request 會使用相同數值;如果兩者都寫,必須相等。
| 檢查點 | 通過時要看到什麼 | 失敗時先回哪一層 |
|---|---|---|
| Host | nvidia-smi 可辨識 GPU 與 Driver |
Driver / kernel / node image |
| GPU stack Pods | Operator 或 Device Plugin 元件穩定 Running | Operator logs / DaemonSet / node compatibility |
| Node resource | capacity 與 allocatable 出現 nvidia.com/gpu |
Device Plugin / kubelet |
| Pod placement | Smoke Pod 被排到 GPU node | Scheduler events / resource request / taint |
| Container | Pod log 至少列出一個可見 GPU index | Toolkit / runtime / device injection |
| Cleanup | Pod 刪除後可再次申請同一資源 | Kubelet / stale workload / runtime |
這六個檢查點只要有一個缺少,我就不會把結果寫成「GPU Kubernetes 已可用」。特別是 Pod Running 不代表 command 成功;必須同時看 exit code 與 log。
「把 GPU 加進 Kubernetes」不是單一安裝動作,而是一條從 Host 到 Pod 的責任鏈。
Driver 讓 Host 看見 GPU;Container Toolkit 讓 Runtime 把 GPU 交給 Container;Device Plugin 把 GPU 公告成
nvidia.com/gpu;到這裡,Scheduler 才第一次看得到這個資源。
今天先把這條鏈的實作方式、通過條件與失敗邊界定義完整。因為目前還沒有可授權操作的 GPU 叢集,我不把未執行的 manifest 寫成已驗證結果;待交接清單中六個檢查點都通過,才能補上實測結論。
明天會繼續往上看:Scheduler 看到的 capacity、allocatable 與已申請數量,到底是什麼意思?
下一篇:Day 04|Kubernetes 看得到 GPU 之後,Scheduler 到底看到了什麼
這篇需要可拋棄的 NVIDIA Linux GPU 節點與 Kubernetes 管理權限。完整操作步驟、停止條件、去識別要求與回傳清單在 docs/day03-operations/02-kubernetes-gpu-resource.md。不得對 production 叢集或未明確授權的節點操作。