K8s · 工作負載 · 第 09 課 · · 11min read

Pod 生命週期與排錯:CrashLoopBackOff、ImagePullBackOff 怎麼解?

Pod 不是 Running 就是好的,還有 Pending、CrashLoopBackOff、ImagePullBackOff、Error 一堆狀態。這篇用「故意搞壞」的方式逐個觸發,讓你看到時不會慌。

#Kubernetes #Pod #排錯 #CrashLoopBackOff #ImagePullBackOff
章節目錄 · 8

Pod 跑起來了,但狀態不是 Running?

上一篇我們跑起了第一個 Pod,YAML 對的、Image 對的,一切順利。但現實工作裡,有一半的時間你都在處理「狀態不是 Running」的 Pod

kubectl get pods 一看,STATUS 那欄是 ImagePullBackOffCrashLoopBackOff — 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 欄常出現別的字,比如 ContainerCreatingCrashLoopBackOffTerminating這些不是 phase,是容器的 waiting reason 或 K8s 給人類看的友善顯示值。先區分這兩層,後面看狀態才不會混亂。

三個常踩的錯誤狀態

狀態意思常見原因第一步排錯
ContainerCreating容器還在 Waiting拉 Image、掛 Volume、套 Secretdescribe 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 podsdescribe podlogs
    • describe 解決「啟動不了」,logs 解決「啟動了又掛」
    • logs --previous 看上一個已死容器的日誌

    下一步

    排錯會了,但有些容器故意要兩個一起跑 — 比如 nginx 寫日誌、旁邊放個小工具讀日誌轉發出去。下一篇講多容器 Pod 與 Sidecar 模式:什麼時候要把容器塞同一個 Pod、什麼時候該拆開。