chundev
日期:2026-05-27 環境:k3s cluster + NVIDIA GPU 節點(Ubuntu 24.04)+ nvidia-device-plugin
GPU worker pod 全部起不來、log 還是空的。根因不是程式,而是節點上 NVIDIA driver 被 apt 背景更新(unattended-upgrades),但節點一直沒重開機 — 正在跑的 kernel module 還是舊版,userspace 函式庫已是新版,兩者對不上。nvidia-smi 直接報 Driver/library version mismatch,device plugin 把 GPU 標成 unhealthy,容器在 OCI runtime 階段就被擋下。解法是重開節點,reload module 救不了。
GPU 運算節點上跑一個 Celery worker(吃 GPU 做推論)。某天發現「任務都不會被處理」。API、Redis 都正常,唯獨 worker pod 一直不 Ready。worker 已經穩定跑了一個多月,期間沒有任何 deploy,所以第一直覺「不是程式改壞的」是對的。
這類「跑好好的突然死掉、沒人動過 code」的狀況,幾乎都是節點環境變了,而 GPU 節點最常見的環境變化就是 driver 被自動更新。
兩顆 worker pod 呈現兩種看似不同、其實同源的錯誤。
第一顆 — StartError,而且 log 全空:
State: Terminated
Reason: StartError
Message: failed to create containerd task: failed to create shim task:
OCI runtime create failed: ... failed to inject CDI devices:
unresolvable CDI devices runtime.nvidia.com/gpu=GPU-xxxxxxxx-...
Exit Code: 128
Restart Count: 567
第二顆 — UnexpectedAdmissionError:
Reason: UnexpectedAdmissionError
Message: Pod was rejected: Allocate failed due to no healthy devices present;
cannot allocate unhealthy devices nvidia.com/gpu, which is unexpected
⚠️ 關鍵指紋:StartError + log 完全空白。 這代表容器在 containerd 的 OCI runtime 階段就被擋下來,你的程式一行都還沒執行。所以往 app log、程式碼裡找永遠找不到 — 要往「容器 runtime 怎麼把 GPU 塞進容器」這層看。
而 unresolvable CDI devices 跟 no healthy devices 不是兩個 bug,是同一個根因的兩種表現:device plugin 認為 GPU 壞了。
重點:每一步都是一條單純的指令,照順序貼就好,不需要任何複合 one-liner。
kubectl get pods -n myapp
確認 api / redis 正常、只有 worker 掛 → 縮小範圍到「跟 GPU 有關的東西」。
kubectl describe pod worker-xxxxx -n myapp
看 State / Reason / Message 那幾行,前面那兩段錯誤訊息就是從這裡來的。
kubectl logs worker-xxxxx -n myapp
空的。這就驗證了「程式還沒跑」的推論,問題在容器 runtime 那層。
unresolvable CDI 跟 unhealthy device 都把矛頭指向節點,所以 ssh 進去問 nvidia-smi:
ssh user@gpu-node
nvidia-smi
結果直接破案:
Failed to initialize NVML: Driver/library version mismatch
NVML library version: 580.159
比對「正在跑的 kernel module 版本」跟「已安裝的套件版本」:
cat /proc/driver/nvidia/version
NVRM version: NVIDIA UNIX x86_64 Kernel Module 580.126.09
dpkg -l | grep nvidia-driver
ii nvidia-driver-580 580.159.03-0ubuntu0.24.04.1 amd64
→ 正在跑的 kernel module 是 580.126.09,但套件已經被更新到 580.159.03。對不上。
uptime
up 61 days
節點 61 天沒重開機。再看 apt 更新紀錄:
grep -i nvidia /var/log/apt/history.log
Upgrade: nvidia-driver-580 (580.126.09..., 580.159.03...), ...
真相大白:unattended-upgrades 把 driver 更新成 580.159.03,但節點沒重開機,所以記憶體裡跑的還是一個多月前載入的舊 module。
uname -r
6.8.0-106-generic ← 正在跑的 kernel
dpkg -l 'linux-image-6.8.0-*generic'
ii linux-image-6.8.0-106-generic
ii linux-image-6.8.0-117-generic ← 新裝、還沒啟用
這一步很關鍵:那次更新同時換了 kernel(6.8.0-106 → 6.8.0-117),而對應正在跑的 6.8.0-106 的新版 nvidia module 已經被移除,只剩 build 給 6.8.0-117 的。也就是說 — 在不重開的情況下,根本沒有一個能對齊 userspace 的 module 可以載入。rmmod + modprobe 這條路走不通,只能重開機。
| 項目 | 狀態 |
|---|---|
| 正在跑的 kernel module | 580.126.09(舊) |
| userspace 函式庫 / 已裝套件 | 580.159.03(新,apt 自動更新的) |
| 正在跑的 kernel | 6.8.0-106(很久沒重開) |
| 已裝但未啟用的 kernel | 6.8.0-117(新 module 是 build 給它的) |
nvidia-smi 失敗 → device plugin 把 GPU 標 unhealthy + CDI spec 裡的 GPU UUID 失效 → 任何要 GPU 的容器在 runtime 階段就被擋。
匹配的一組(新 kernel 6.8.0-117 + 新 module 580.159.03)其實都裝好了,只是要重開機才會生效。
ssh user@gpu-node
sudo systemctl reboot
k8s 這邊不用手動做什麼:節點回來後 device plugin 會重新偵測到健康的 GPU、CDI spec 自動重生,worker 的 ReplicaSet 會自動拉新 pod。
⚠️ 如果這顆是 control-plane 節點、又跑著服務本體,要先讓相關人知道:重開期間控制平面 + 該節點上的服務會中斷數分鐘。正規做法是先 kubectl drain 再重開再 uncordon;小型 cluster 直接重開通常也可接受。
ssh user@gpu-node
uname -r # 應該變成新 kernel 6.8.0-117-generic
nvidia-smi -L # 應該列出 GPU,不再報 mismatch
kubectl get node gpu-node -o jsonpath='{.status.allocatable.nvidia\.com/gpu}'
# 回 1(或你的卡數)代表 GPU 重新可配置
kubectl get pods -n myapp -w
# 看 worker 從 ContainerCreating 變成 1/1 Running
最後別忘了把卡了一堆 restart 的舊 failed pod 清掉:
kubectl delete pod worker-old-1 worker-old-2 -n myapp
worker log 出現 Connected to redis://... 和 celery@... ready. 就代表真的能收任務了 — Running 不等於能工作,要看到 ready 才算數。
StartError + 空 log = 容器在 runtime 階段就被擋,程式沒執行。 別在 app log 裡鬼打牆,往容器 runtime / device injection 那層查。unresolvable CDI devices 和 no healthy devices 都是「device plugin 認為 GPU 壞了」的不同講法,別當成兩個問題分頭追。uptime + apt history 兩條指令就能驗證。uname -r 對 dpkg -l linux-image-* 判斷。sudo apt-mark hold nvidia-driver-*(凍結版本),或 sudo dpkg-reconfigure unattended-upgrades 關掉自動更新;要更新時手動做完馬上重開。