Pod 排錯

本章介紹 Pod 運行異常的排錯方法。

一般來說,無論 Pod 處於什麼異常狀態,都可以執行以下命令來查看 Pod 的狀態

  • kubectl get pod <pod-name> -o yaml 查看 Pod 的配置是否正確

  • kubectl describe pod <pod-name> 查看 Pod 的事件

  • kubectl logs <pod-name> [-c <container-name>] 查看容器日誌

這些事件和日誌通常都會有助於排查 Pod 發生的問題。

Pod 一直處於 Pending 狀態

Pending 說明 Pod 還沒有調度到某個 Node 上面。可以通過 kubectl describe pod <pod-name> 命令查看到當前 Pod 的事件,進而判斷爲什麼沒有調度。如

$ kubectl describe pod mypod
...
Events:
  Type     Reason            Age                From               Message
  ----     ------            ----               ----               -------
  Warning  FailedScheduling  12s (x6 over 27s)  default-scheduler  0/4 nodes are available: 2 Insufficient cpu.

可能的原因包括

  • 資源不足,集群內所有的 Node 都不滿足該 Pod 請求的 CPU、內存、GPU 或者臨時存儲空間等資源。解決方法是刪除集群內不用的 Pod 或者增加新的 Node。

  • HostPort 端口已被佔用,通常推薦使用 Service 對外開放服務端口

Pod 一直處於 Waiting 或 ContainerCreating 狀態

首先還是通過 kubectl describe pod <pod-name> 命令查看到當前 Pod 的事件

可以發現,該 Pod 的 Sandbox 容器無法正常啓動,具體原因需要查看 Kubelet 日誌:

發現是 cni0 網橋配置了一個不同網段的 IP 地址導致,刪除該網橋(網絡插件會自動重新創建)即可修復

除了以上錯誤,其他可能的原因還有

  • 鏡像拉取失敗,比如

    • 配置了錯誤的鏡像

    • Kubelet 無法訪問鏡像(國內環境訪問 gcr.io 需要特殊處理)

    • 私有鏡像的密鑰配置錯誤

    • 鏡像太大,拉取超時(可以適當調整 kubelet 的 --image-pull-progress-deadline--runtime-request-timeout 選項)

  • CNI 網絡錯誤,一般需要檢查 CNI 網絡插件的配置,比如

    • 無法配置 Pod 網絡

    • 無法分配 IP 地址

  • 容器無法啓動,需要檢查是否打包了正確的鏡像或者是否配置了正確的容器參數

Pod 處於 ImagePullBackOff 狀態

這通常是鏡像名稱配置錯誤或者私有鏡像的密鑰配置錯誤導致。這種情況可以使用 docker pull <image> 來驗證鏡像是否可以正常拉取。

如果是私有鏡像,需要首先創建一個 docker-registry 類型的 Secret

然後在容器中引用這個 Secret

Pod 一直處於 CrashLoopBackOff 狀態

CrashLoopBackOff 狀態說明容器曾經啓動了,但又異常退出了。此時 Pod 的 RestartCounts 通常是大於 0 的,可以先查看一下容器的日誌

這裏可以發現一些容器退出的原因,比如

  • 容器進程退出

  • 健康檢查失敗退出

  • OOMKilled

如果此時如果還未發現線索,還可以到容器內執行命令來進一步查看退出原因

如果還是沒有線索,那就需要 SSH 登錄該 Pod 所在的 Node 上,查看 Kubelet 或者 Docker 的日誌進一步排查了

Pod 處於 Error 狀態

通常處於 Error 狀態說明 Pod 啓動過程中發生了錯誤。常見的原因包括

  • 依賴的 ConfigMap、Secret 或者 PV 等不存在

  • 請求的資源超過了管理員設置的限制,比如超過了 LimitRange 等

  • 違反集群的安全策略,比如違反了 PodSecurityPolicy 等

  • 容器無權操作集群內的資源,比如開啓 RBAC 後,需要爲 ServiceAccount 配置角色綁定

Pod 處於 Terminating 或 Unknown 狀態

從 v1.5 開始,Kubernetes 不會因爲 Node 失聯而刪除其上正在運行的 Pod,而是將其標記爲 Terminating 或 Unknown 狀態。想要刪除這些狀態的 Pod 有三種方法:

  • 從集群中刪除該 Node。使用公有云時,kube-controller-manager 會在 VM 刪除後自動刪除對應的 Node。而在物理機部署的集群中,需要管理員手動刪除 Node(如 kubectl delete node <node-name>

  • Node 恢復正常。Kubelet 會重新跟 kube-apiserver 通信確認這些 Pod 的期待狀態,進而再決定刪除或者繼續運行這些 Pod。

  • 用戶強制刪除。用戶可以執行 kubectl delete pods <pod> --grace-period=0 --force 強制刪除 Pod。除非明確知道 Pod 的確處於停止狀態(比如 Node 所在 VM 或物理機已經關機),否則不建議使用該方法。特別是 StatefulSet 管理的 Pod,強制刪除容易導致腦裂或者數據丟失等問題。

如果 Kubelet 是以 Docker 容器的形式運行的,此時 kubelet 日誌中可能會發現如下的錯誤

如果是這種情況,則需要給 kubelet 容器設置 --containerized 參數並傳入以下的存儲卷

處於 Terminating 狀態的 Pod 在 Kubelet 恢復正常運行後一般會自動刪除。但有時也會出現無法刪除的情況,並且通過 kubectl delete pods <pod> --grace-period=0 --force 也無法強制刪除。此時一般是由於 finalizers 導致的,通過 kubectl edit 將 finalizers 刪除即可解決。

Pod 行爲異常

這裏所說的行爲異常是指 Pod 沒有按預期的行爲執行,比如沒有運行 podSpec 裏面設置的命令行參數。這一般是 podSpec yaml 文件內容有誤,可以嘗試使用 --validate 參數重建容器,比如

也可以查看創建後的 podSpec 是否是對的,比如

修改靜態 Pod 的 Manifest 後未自動重建

Kubelet 使用 inotify 機制檢測 /etc/kubernetes/manifests 目錄(可通過 Kubelet 的 --pod-manifest-path 選項指定)中靜態 Pod 的變化,並在文件發生變化後重新創建相應的 Pod。但有時也會發生修改靜態 Pod 的 Manifest 後未自動創建新 Pod 的情景,此時一個簡單的修復方法是重啓 Kubelet。

Nginx 啓動失敗

Nginx 啓動失敗,錯誤消息是 nginx: [emerg] socket() [::]:8000 failed (97: Address family not supported by protocol)。這是由於服務器未開啓 IPv6 導致的,解決方法有兩種:

  • 第一種方法,服務器開啓 IPv6;

  • 或者,第二種方法,刪除或者註釋掉 /etc/nginx/conf.d/default.conf 文件中的 listen [::]:80 default_server;

Namespace 一直處於 terminating 狀態

Namespace 一直處於 terminating 狀態,一般有兩種原因:

  • Namespace 中還有資源正在刪除中

  • Namespace 的 Finalizer 未正常清理

對第一個問題,可以執行下面的命令來查詢所有的資源

而第二個問題則需要手動清理 Namespace 的 Finalizer 列表:

1) 使用 kubectl patch:

2) 使用 kubectl proxy:

3) 使用 kubectl --raw:

Pod 排錯圖解

img

(圖片來自A visual guide on troubleshooting Kubernetes deployments

參考文檔

Last updated