故障排除:Pod、Node、Control Plane 怎麼查
· tech
📑 目錄
CKA 佔比最高的一塊是故障排除(30%),但它其實不是新知識——它是把前面整個系列串起來的能力。排障最忌諱用猜的、亂試一通。真正的心法只有一句:沿著 Pod 的生命週期,一關一關問「它卡在哪」——因為 K8s 很貼心,它把「卡在哪一關」直接寫在狀態裡了。
第一步永遠是這三條指令:get → describe → logs
不管什麼症狀,起手式固定這個順序,由外而內剝洋蔥:
get(什麼狀態)→ describe(為什麼——Events 段是金礦)→ logs(程式說了什麼)→ exec/debug(進現場)。但動手前先分層:多數問題在 Pod 層,查不到再往 Node 與 Control Plane 上找最被低估的一條是 kubectl describe:它最下面的 Events 段,幾乎把 K8s 剛才「想做什麼、卡在哪」全寫出來了——排程失敗的原因、映像拉不動的錯誤、readiness probe 一直失敗、被 OOM 砍掉,全在這。很多人一出事就衝去看 logs,但答案往往在 Events 裡,而且更直接。 幾條常用的:
kubectl get pods -o wide # 狀態、重啟次數、落在哪台 node
kubectl describe pod <p> # 看最底下 Events 段(最重要)
kubectl logs <p> --previous # CrashLoop 必用:看「上一個掛掉的」實例日誌
kubectl get events --sort-by=.lastTimestamp # 全 namespace 依時間排的事件流
logs --previous 是排 CrashLoopBackOff 的關鍵:容器已經崩掉重啟了,當下的 logs 是新實例、常常空的,你要的是上一個崩掉那次留下的最後幾行。
讀懂 Pod 狀態:每個狀態都在指路
把第一張圖的四關攤成一張對照表,看到狀態就知道往哪查、根因大概是什麼:
| 狀態 | 卡在哪一關 | 最常見根因 | 先查 |
|---|---|---|---|
| Pending | ① 排不進 node | 資源不夠、[[k8s-scheduling-advanced | taint 沒 toleration]]、PVC 綁不到 PV |
| ContainerCreating 卡住 | ①→② 之間 | CNI 沒配好、Volume 掛不上、Secret/ConfigMap 不存在 | describe Events |
| ImagePullBackOff | ② 拉映像 | 映像名 / tag 打錯、私有 registry 少 imagePullSecret | describe Events(Failed to pull) |
| CrashLoopBackOff | ③ 起了就掛 | 程式啟動即崩、設定錯、[[k8s-config-secret | 少了環境變數]]、probe 設太嚴 |
| OOMKilled | ③ 記憶體爆 | 實際用量超過 memory limit 被砍 | describe(Last State: OOMKilled)、調 limit |
| Running 但 0/1 READY | ④ 沒通過 readiness | readiness probe 一直失敗 → 不進 [[k8s-service | Endpoints]]、收不到流量 |
| Running 但連不到 | ④ 網路層 | selector 打錯 Endpoints 是空的、[[k8s-ingress-dns | DNS]] 解不到、[[k8s-networkpolicy-cni |
這張表就是把整個系列反過來用:每一種故障,都是前面某一篇講過的機制在「沒運作」。 排障排的不是新東西,是你懂不懂那些機制。
換一層看:Node 與 Control Plane
如果一整台 node 上的 Pod 全出事,別再盯著 Pod——問題在 Node 層。kubectl get nodes 看到 NotReady,通常是那台的 kubelet 掛了、磁碟/記憶體壓力(DiskPressure / MemoryPressure)、或網路斷了。這時候得 SSH 上去用 journalctl -u kubelet 看 kubelet 的日誌、用 crictl 直接問容器執行期,而不是隔著 API 猜。
再往上,如果連 kubectl 本身都開始逾時、整個叢集像失聯——那是 Control Plane 層。api-server 或 etcd 出事,整個叢集的「下命令」能力就癱了。因為它們是 static pod,你得去 control plane 那台看 /etc/kubernetes/manifests、看那幾顆 pod 的容器狀態與日誌。一層一層往上,是因為越上層炸得越大:Pod 掛只影響一個服務,control plane 掛影響整座叢集。
kubectl debug:連 shell 都沒有時
有個實務常撞的牆:現在很多映像檔為了精簡與安全,是 distroless / 沒有 shell 的,你 kubectl exec -it -- sh 直接失敗,無從進去看。kubectl debug 就是解法——它用 ephemeral container 把一個帶工具的臨時容器,塞進同一個 Pod、共享它的網路與行程空間,讓你在旁邊 curl、看檔案、抓封包,而完全不動原本的容器:
kubectl debug -it <p> --image=busybox --target=<container> # 塞一個臨時容器進去查
kubectl debug node/<node> -it --image=busybox # 連 node 都能開一個特權容器來查
不用為了 debug 去改映像檔硬塞工具、也不用重啟 Pod 破壞現場——這是排查那些「精簡到沒東西可用」的 Production 容器時,最該記住的一招。
反思
排障的功力,不在背指令,在有沒有一張「生命週期地圖」
我看過太多人排障靠玄學:狀態沒細看,就開始重啟 Pod、砍了重建、改一堆設定碰運氣。真正有效的排障,是心裡有第一張圖那張生命週期地圖——看到 Pending 就知道是排程關、看到 ImagePullBackOff 就知道是映像關、看到 not Ready 就知道往 readiness 與 Endpoints 查。狀態不是報錯,是 K8s 在告訴你它走到哪一步走不下去了。 把這張地圖內化之後,排障就從「亂試到好」變成「看一眼就知道往哪挖」,這中間的效率差距是十倍起跳。這也呼應我在 SRE 排障那篇的核心主張:系統化的心智模型,永遠贏過臨場的靈光一閃。
Events 段是最被低估的金礦
如果只能留一條排障建議,我會說:先 describe,看 Events。 它是 K8s 主動幫你寫好的「剛才發生了什麼」——排程器為什麼放不進、映像為什麼拉不動、probe 為什麼失敗,全在那幾行。我早年的壞習慣是一出事就鑽進 logs,結果 logs 是應用層的輸出,常常跟「Pod 起不來」這種平台層問題根本無關。先問平台(Events)、再問應用(logs),這個順序讓我少走無數冤枉路。工具早就把答案攤在那了,差別只在你有沒有先看對地方。
這一篇讀完,整個系列才真的閉環
寫到這裡,我特別有感:故障排除之所以是 CKA 最大宗,正因為它不是獨立的一章,而是全部的驗收。你得懂 reconcile loop 才知道 Pod 為什麼會自己重啟、懂 排程才看得懂 Pending、懂 Service 與 DNS 才追得到「連不到」、懂 etcd 與 control plane才敢動最上層。排障能力是這些理解的總和,騙不了人。 所以我從不把「會不會查問題」當成一種獨立技能去練——它是你對這個系統理解夠不夠深的溫度計。整個系列走到這,從「宣告式」到「排障」,剛好繞回原點:你越懂它平常怎麼運作,出事時就越知道它是哪裡沒運作。