當前位置: 妍妍網 > 碼農

K8s Pod 故障排查,一個不為人知的技巧!

2024-04-23碼農

原文連結:https://blog.51cto.com/u_15682575/8768364

如果您在 Kubernetes 上執行軟體,您會想要在某些時候去偵錯您所部署的軟體的一些方面。對於習慣於使用虛擬機器 (VMs) 的人來說能自然使用的一種簡單的偵錯方法,就是連線到一個正在執行的 pod,然後進行解譯:

kubectl exec -it podname -c containername -- bash

這通常行之有效,而且非常管用。然而,至少有兩種 Kubernetes "最佳實踐 "限制了 exec 的實用性:

  • 不以 root 使用者身份執行。容器盡可能以最少的特權執行,甚至可能使用隨機的使用者識別元 (UID) 執行。

  • 最小化映像。映像盡可能小,你甚至可以將二進制檔寫入到 distroless image。

  • 當套用這些最佳實踐時,使用 kubectl exec 連線到您的容器要麽不可行,要麽進入到不適合進行偵錯的環境。

    kubectl exec 指令不允許指定使用者標誌或能力以啟動行程,而是會從目標容器的主指令中復制這些設定。

    偵錯容器

    在解決執行容器問題時,Kubernetes 提供了一種原生化偵錯策略,即使用 kubectl debug 。偵錯指令會在執行中的 pod 中啟動一個新的容器。這個新容器能夠以不同的使用者身份以及從您選擇的任何映像去執行。由於偵錯容器與目標容器位於同一個 pod 中執行(因此在同一個節點上),兩者之間不需要絕對的隔離。偵錯容器可以與同一 pod 中執行的其他容器共享系統資源。

    考慮去檢查在 pod postpod 中的容器 postcont 裏執行的 PostgreSQL 資料庫的 CPU 使用情況。這個 pod 並不以 root 使用者身份執行,並且 Postgres 映像沒有安裝類似 top 或 htop 的工具,也就是說, kubectl exec 指令幾乎沒有作用。您可以按照以下的指令去執行:

    kubectldebug -it \ --container=debug-container \ --image=alpine \ --target=postcont \ postpod

    您將以 root 身份登入(這是 Alpine 映像的預設設定),並可以輕松安裝您最喜歡的互動式行程檢視器 htop ( apt add htop )。您與 postcont 容器共享同一個行程名稱空間,可以檢視並甚至終止在此執行的所有行程!當您結束行程時,臨時容器也會終止。

    如果希望偵錯容器與 postcont 共享相同的行程名稱空間,即使 postcont 是在 postpod 中執行的唯一容器,指定 --target 是不具備選擇性的 (non-optional)。

    您可以按 CTRL+P CTRL+D 斷開與臨時容器/bash 會話 (session) 的連線,而無需結束 (終止) 它。再使用 kubectl attach 即可重新連線。

    kubectl debug 提供的功能比這裏概述的更多,比如使用一個修改後的啟動指令復制 pods,或透過存取節點檔案系統的啟動一個 "節點 (node) " pod。

    原理解釋

    以上的 kubectl debug 指令是透過建立臨時容器 (ephemeral container) 來實作。這些容器應在現有 pod 中臨時執行,以支持故障排除等操作。「普通」容器和臨時容器之間的區別很小。而檢視 Kubernetes 在創立之初所做的基礎架構選擇最能讓我們理解使用臨時容器的原因:

    Pod 應該是免洗的、可替換的,並且 Pod 規範也是不可改變的。


    當 Kubernetes 主要用於部署無狀態工作負載時,這一點更加合理——因為此時 pod 本身會被認為是臨時的。在這個 Kubernetes 中它可能會受到限制。Pod 規範保持不變,但 Kubernetes 會將臨時容器作為 Pod 的子資源建模。 與「普通」容器不同,臨時容器不屬於 Pod 規範的一部份


    掛載卷 (volumes)

    內建指令 kubectl debug 非常有用。它允許您在執行的 pod 中添加一個臨時容器,並可選擇與執行中的容器共享行程名稱空間。不過,如果您希望使用 kubectl debug 來檢查或修改執行中容器檔案系統的某個部份,那就不走運了——因為偵錯 pod 的檔案系統與您將其連線到的容器的檔案系統是分離的。幸運的是,我們可以做的更好。原理很簡單:

  • 讀取正在執行的目標容器的規範。

  • 將一個臨時容器填充到 pod 中。將其配置成與目標容器共享相同的行程名稱空間,並包含相同的卷掛載。

  • 因為沒有用於建立臨時容器的 kubectl 命令,所以我們需要構建一個 PATCH 請求到 K8s API 來建立它。 kubectl proxy 指令允許存取 K8s API。這一過程對使用者來說並不太友好,因此將這一過程封裝到指令碼或 kubectl 外掛程式中是合理的。您可以在這裏找到這樣一個指令碼實作範例:

    https://github.com/JonMerlevede/kubectl-superdebug

    需要註意的是,這種方法和指令碼可以很容易地擴充套件到從目標容器中復制環境變量的規範。如果您將此指令碼保存為 kubectl-superdebug ,並將其放在您的路徑上,就可以在任何地方以 kubectl superdebug 的形式執行,如下所示:

    還可以嘗試擴充套件此指令碼,將目標容器的其他方面復制到偵錯容器中,例如環境變量參照。

    至此,Kubernetes 本機偵錯執行中的容器的方法概述就完成了,應該能滿足大多數人的需求。

    非 Kubernetes 原生方法

    Kubernetes 不提供以 root 身份連線到正在執行的容器的方法(除非主行程以 root 身份執行),也不提供從另一個容器存取容器根檔案系統的方法。但這並不意味著這些事情不可能做到。畢竟, Kubernetes 只是一個位於容器化引擎之上的容器編排器。如果出於某種原因,確實有必要的話,您通常可以透過移除抽象層來做任何您想做的事。

    如果您使用的是 Docker 引擎,並且可以直接從節點或透過節點上執行的特權容器存取您的引擎,那麽您就可以執行 docker exec --user ,並以您選擇的使用者身份執行一個行程。

    kubectl ssh kubectl exec-user 等外掛程式實作了這種方法。但遺憾的是,containerd 和 CRI-O 等現代引擎不再提供 --user 這樣標誌功能,這意味著這些外掛程式無法在當下的 Kubernetes 安裝上執行。

    不過,即使是這些現代引擎,通常也只是與 Linux 名稱空間介面。透過輸入相應的 Linux 名稱空間集,您可以在任何您想要的「容器」中執行指令。kpexec 工具實作了這種方法。它在與目標容器相同的節點上啟動一個有許可權的 pod,然後確定要針對哪些 (Linux) 名稱空間,在這些 (Linux) 命名 空間中執行命令,最後將其輸出流式傳輸到您的終端。作為額外的收獲,它還能在目標容器的文 件系統之上疊加一套用於偵錯的工具。

    與 kubectl exec 不同,kpexec 可以使用不同的 uid/gid 執行指令,甚至可以使用與容器主行程不同的功能。它與 containerd 和 cri-o 相容。只是 kpexec 采用的方法有些笨重和脆弱,可能與集群的安全配置不相容。但如果 kubectl (super) 偵錯無法滿足您的需求,則值得考慮它。

    需要註意,kpexec 使用 nsenter 是直接在名稱空間中執行指令的。它與無處不在的容器執行時 runc 相容,但與 Kata Containers 等執行時不相容。

    借助 Appilot 對話式診斷 K8s

    Appilot 是一款面向 DevOps 場景的開源 AI 助手,它可以 充分利用 AI 大語言模型的能力讓使用者直接輸入自然語言進一步簡化套用部署與管理體驗 。使用者可以根據自身的需求和使用習慣,將 Appilot 整合到任意平台,進而實作透過輸入自然語言即可呼叫後端平台的能力,輕松完成 Kubernetes debug 工作。

    Appilot 計畫地址 https://github.com/seal-io/appilot

    往期推薦


    點亮,伺服器三年不宕機