K8s容器debug高階技巧

Seal軟體發表於2023-12-11

使用 kubectl exec 執行指令

如果您在 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 指令幾乎沒有作用。您可以按照以下的指令去執行:
 

kubectl debug -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?source=post_page-----2ba160c47ef5------

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

 
還可以嘗試擴充套件此指令碼,將目標容器的其他方面複製到除錯容器中,例如環境變數引用。

 

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

非 Kubernetes 原生方法

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

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

 

kubectl sshkubectl 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

相關文章