Pod 跑起來了,但狀態不是 Running?
上一篇我們跑起了第一個 Pod,YAML 對的、Image 對的,一切順利。但現實工作裡,有一半的時間你都在處理「狀態不是 Running」的 Pod。
kubectl get pods 一看,STATUS 那欄是 ImagePullBackOff 或 CrashLoopBackOff — K8s 不會像朋友一樣告訴你「你 Image 名字打錯了」,它只給你一個狀態碼,然後你自己查。
這篇教你兩件事:Pod 各種狀態代表什麼,以及碰到問題該怎麼一步一步定位。
Pod 真正的 phase 只有 4 種
K8s API 裡 Pod 的 phase 其實只有:
| Phase | 意思 |
|---|---|
Pending | 排隊中(Scheduler 還沒分配 Node) |
Running | 正常運行 |
Succeeded | 跑完正常結束(exit 0) |
Failed | 跑完非正常結束(exit 非 0) |
kubectl get pods 看到的 STATUS 欄常出現別的字,比如 ContainerCreating、CrashLoopBackOff、Terminating。這些不是 phase,是容器的 waiting reason 或 K8s 給人類看的友善顯示值。先區分這兩層,後面看狀態才不會混亂。
三個常踩的錯誤狀態
| 狀態 | 意思 | 常見原因 | 第一步排錯 |
|---|---|---|---|
ContainerCreating | 容器還在 Waiting | 拉 Image、掛 Volume、套 Secret | describe pod 看 Events |
ErrImagePull | 拉 Image 失敗 | 名字拼錯、tag 不存在 | describe pod |
ImagePullBackOff | 重複拉失敗,退避中 | 同上 | describe pod |
CrashLoopBackOff | 反覆 crash + 重啟 | 程式啟動就掛 | logs 看輸出 |
ErrImagePull → ImagePullBackOff
你寫了 image: ngin(少一個 x),K8s 拉不到 → 第一次失敗顯示 ErrImagePull → 重試還是失敗 → 進入退避,狀態變 ImagePullBackOff。
BackOff 是 K8s 很重要的概念:每次重試的間隔越來越長,避免無限快速失敗把 Node 搞爛。
CrashLoopBackOff(指數退避)
Image 拉到了、容器建好了,但程式一啟動就 crash。K8s 自動重啟,又 crash,又重啟…形成 loop。
退避策略:第一次等 10 秒重啟,第二次 20 秒,第三次 40 秒,第四次 80 秒…最長 5 分鐘封頂。
所以你如果觀察 RESTARTS 欄位,會發現數字一直長,但每次重啟之間越等越久 — 這不是 K8s 放棄了,是它在退避中。
常見原因:
- 程式碼有 bug,啟動就報錯退出
- 設定檔有問題(環境變數沒給、ConfigMap 沒掛)
- 依賴的服務連不上(DB 連線字串錯)
command寫錯,找不到執行檔
排錯三兄弟(記住這三招)
碰到任何 Pod 問題,固定流程:
# 1. 看狀態
kubectl get pods
# 2. 看 Events(90% 的問題在這)
kubectl describe pod <pod-name>
# 3. 看程式日誌
kubectl logs <pod-name>
三步的分工:
get pods告訴你「方向」(Image 問題?程式問題?資源問題?)describe看 Events 告訴你「為什麼啟動不了」(K8s 在做什麼、發生什麼錯誤)logs告訴你「為什麼啟動了又掛」(程式自己印的錯誤訊息)
實戰:故意把 Pod 搞壞
Case 1:Image 名字打錯
# pod-broken.yaml
apiVersion: v1
kind: Pod
metadata:
name: broken-pod
spec:
containers:
- name: broken
image: ngin # 故意少一個 x
ports:
- containerPort: 80
kubectl apply -f pod-broken.yaml
# pod/broken-pod created ← 注意!created ≠ running
kubectl get pods
# STATUS: ErrImagePull → ImagePullBackOff(來回切換)
kubectl describe pod broken-pod
# 拉到最下面 Events:
# Failed to pull image "ngin": manifest unknown
修復:
kubectl delete pod broken-pod
# 改 YAML 的 image: ngin → image: nginx:1.27
kubectl apply -f pod-broken.yaml
kubectl get pods # Running
⚠️ 永遠改 YAML 重新 apply,不要用 kubectl edit:edit 改的東西不會反映回 YAML 檔,下次再 apply 同樣的錯又出現。
Case 2:程式啟動就掛(CrashLoopBackOff)
# pod-crash.yaml
apiVersion: v1
kind: Pod
metadata:
name: crash-pod
spec:
containers:
- name: crash-test
image: busybox:1.36
command: ["/bin/sh", "-c", "echo hello && exit 1"]
kubectl apply -f pod-crash.yaml
kubectl get pods
# STATUS: CrashLoopBackOff, RESTARTS: 3, 4, 5...
kubectl describe pod crash-pod
# Events: Back-off restarting failed container
kubectl logs crash-pod
# hello ← 程式有印再退出
kubectl logs crash-pod --previous
# 看「上一個已死掉的容器」的 log(生產環境很常用)
💡/bin/sh -c為什麼必要:exit是 shell 內建指令、不是執行檔。直接寫command: ["exit", "1"]會找不到檔案。一律用/bin/sh -c "整串指令"包起來。
logs 是空的怎麼辦?
如果程式還沒印任何東西就 crash(比如 OOM 或 segfault),logs 會是空的。這時:
kubectl logs --previous 看上一輪kubectl get events --sort-by=.metadata.creationTimestamp 看叢集事件(Node 磁碟滿了之類更底層的問題)describe 看 Events 區塊裡的 OOMKilled / Error 訊息重點整理
- Pod phase 真正只有 4 種:Pending / Running / Succeeded / Failed
- ContainerCreating、CrashLoopBackOff 是 STATUS 顯示值,不是 phase
- BackOff = 退避,每次重試間隔越來越長
- 排錯三兄弟:
get pods→describe pod→logs - describe 解決「啟動不了」,logs 解決「啟動了又掛」
logs --previous看上一個已死容器的日誌
下一步
排錯會了,但有些容器故意要兩個一起跑 — 比如 nginx 寫日誌、旁邊放個小工具讀日誌轉發出去。下一篇講多容器 Pod 與 Sidecar 模式:什麼時候要把容器塞同一個 Pod、什麼時候該拆開。