簡化 Pod 故障診斷: kubectl-debug 介紹

背景

容器技術的一個最佳實踐是構建儘量精簡的容器鏡像。但這一實踐卻會給排查問題帶來麻煩:精簡後的容器中廣泛缺失經常使用的排障工具,部分容器裏甚至沒有 shell (好比 FROM scratch )。 在這種情況下,咱們只能經過日誌或者到宿主機上經過 docker-cli 或 nsenter 來排查問題,效率很低。Kubernetes 社區也早就意識到了這個問題,在 16 年就有相關的 Issue Support for troubleshooting distroless containers 並造成了對應的 Proposal。 遺憾的是,因爲改動的涉及面很廣,相關的實現至今尚未合併到 Kubernetes 上游代碼中。而在 一個偶然的機會下(PingCAP 一面要求實現一個 kubectl 插件實現相似的功能),我開發了 kubectl-debug: 經過啓動一個安裝了各類排障工具的容器,來幫助診斷目標容器java

工做原理

咱們先不着急進入 Quick Start 環節。 kubectl-debug 自己很是簡單,所以只要理解了它的工做原理,你就能徹底掌握這個工具,而且還能用它作 debug 以外的事情。node

咱們知道,容器本質上是帶有 cgroup 資源限制和 namespace 隔離的一組進程。所以,咱們只要啓動一個進程,而且讓這個進程加入到目標容器的各類 namespace 中,這個進程就能 "進入容器內部"(注意引號),與容器中的進程"看到"相同的根文件系統、虛擬網卡、進程空間了——這也正是 docker execkubectl exec 等命令的運行方式。linux

如今的情況是,咱們不只要 "進入容器內部",還但願帶一套工具集進去幫忙排查問題。那麼,想要高效管理一套工具集,又要能夠跨平臺,最好的辦法就是把工具自己都打包在一個容器鏡像當中。 接下來,咱們只須要經過這個"工具鏡像"啓動容器,再指定這個容器加入目標容器的的各類 namespace,天然就實現了 "攜帶一套工具集進入容器內部"。事實上,使用 docker-cli 就能夠實現這個操做:git

export TARGET_ID=666666666
# 加入目標容器的 network, pid 以及 ipc namespace
docker run -it --network=container:$TARGET_ID --pid=container:$TARGET_ID --ipc=container:$TARGET_ID busybox
複製代碼

這就是 kubectl-debug 的出發點: 用工具容器來診斷業務容器 。背後的設計思路和 sidecar 等模式是一致的:每一個容器只作一件事情。github

具體到實現上,一條 kubectl debug <target-pod> 命令背後是這樣的:面試

{{< figure src="/arch-2.jpg" width="800px" >}}docker

步驟分別是:shell

  1. 插件查詢 ApiServer:demo-pod 是否存在,所在節點是什麼
  2. ApiServer 返回 demo-pod 所在所在節點
  3. 插件請求在目標節點上建立 Debug Agent Pod
  4. Kubelet 建立 Debug Agent Pod
  5. 插件發現 Debug Agent 已經 Ready,發起 debug 請求(長鏈接)
  6. Debug Agent 收到 debug 請求,建立 Debug 容器並加入目標容器的各個 Namespace 中,建立完成後,與 Debug 容器的 tty 創建鏈接

接下來,客戶端就能夠開始經過 5,6 這兩個鏈接開始 debug 操做。操做結束後,Debug Agent 清理 Debug 容器,插件清理 Debug Agent,一次 Debug 完成。效果以下圖:macos

開始使用

Mac 能夠直接使用 brew 安裝:安全

brew install aylei/tap/kubectl-debug
複製代碼

全部平臺均可以經過下載 binary 安裝:

export PLUGIN_VERSION=0.1.1
# linux x86_64
curl -Lo kubectl-debug.tar.gz https://github.com/aylei/kubectl-debug/releases/download/v${PLUGIN_VERSION}/kubectl-debug_${PLUGIN_VERSION}_linux_amd64.tar.gz
# macos
curl -Lo kubectl-debug.tar.gz https://github.com/aylei/kubectl-debug/releases/download/v${PLUGIN_VERSION}/kubectl-debug_${PLUGIN_VERSION}_darwin_amd64.tar.gz

tar -zxvf kubectl-debug.tar.gz kubectl-debug
sudo mv kubectl-debug /usr/local/bin/
複製代碼

Windows 用戶能夠在 Release 頁面 進行下載。

下載完以後就能夠開始使用 debug 插件:

kubectl debug target-pod --agentless --port-forward
複製代碼

kubectl 從 1.12 版本以後開始支持從 PATH 中自動發現插件。1.12 版本以前的 kubectl 不支持這種插件機制,但也能夠經過命令名 kubectl-debug 直接調用。

能夠參考項目的 中文 README 來得到更多文檔和幫助信息。

典型案例

基礎排障

kubectl debug 默認使用 nicolaka/netshoot 做爲默認的基礎鏡像,裏面內置了至關多的排障工具,包括:

使用 iftop 查看容器網絡流量:

➜  ~ kubectl debug demo-pod

root @ /
 [2] 🐳  → iftop -i eth0
interface: eth0
IP address is: 10.233.111.78
MAC address is: 86:c3:ae:9d:46:2b
# (圖片略去)
複製代碼

使用 drill 診斷 DNS 解析:

root @ /
 [3] 🐳  → drill -V 5 demo-service
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 0
;; flags: rd ; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;; demo-service.	IN	A

;; ANSWER SECTION:

;; AUTHORITY SECTION:

;; ADDITIONAL SECTION:

;; Query time: 0 msec
;; WHEN: Sat Jun  1 05:05:39 2019
;; MSG SIZE  rcvd: 0
;; ->>HEADER<<- opcode: QUERY, rcode: NXDOMAIN, id: 62711
;; flags: qr rd ra ; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0
;; QUESTION SECTION:
;; demo-service.	IN	A

;; ANSWER SECTION:

;; AUTHORITY SECTION:
.	30	IN	SOA	a.root-servers.net. nstld.verisign-grs.com. 2019053101 1800 900 604800 86400

;; ADDITIONAL SECTION:

;; Query time: 58 msec
;; SERVER: 10.233.0.10
;; WHEN: Sat Jun  1 05:05:39 2019
;; MSG SIZE  rcvd: 121
複製代碼

使用 tcpdump 抓包:

root @ /
 [4] 🐳  → tcpdump -i eth0 -c 1 -Xvv
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
12:41:49.707470 IP (tos 0x0, ttl 64, id 55201, offset 0, flags [DF], proto TCP (6), length 80)
    demo-pod.default.svc.cluster.local.35054 > 10-233-111-117.demo-service.default.svc.cluster.local.8080: Flags [P.], cksum 0xf4d7 (incorrect -> 0x9307), seq 1374029960:1374029988, ack 1354056341, win 1424, options [nop,nop,TS val 2871874271 ecr 2871873473], length 28
  0x0000:  4500 0050 d7a1 4000 4006 6e71 0ae9 6f4e  E..P..@.@.nq..oN
  0x0010:  0ae9 6f75 88ee 094b 51e6 0888 50b5 4295  ..ou...KQ...P.B.
  0x0020:  8018 0590 f4d7 0000 0101 080a ab2d 52df  .............-R.
  0x0030:  ab2d 4fc1 0000 1300 0000 0000 0100 0000  .-O.............
  0x0040:  000e 0a0a 08a1 86b2 ebe2 ced1 f85c 1001  .............\..
1 packet captured
11 packets received by filter
0 packets dropped by kernel
複製代碼

訪問目標容器的根文件系統:

容器技術(如 Docker)利用了 /proc 文件系統提供的 /proc/{pid}/root/ 目錄實現了爲隔離後的容器進程提供單獨的根文件系統(root filesystem)的能力(就是 chroot 一下)。當咱們想要訪問 目標容器的根文件系統時,能夠直接訪問這個目錄:

root @ /
 [5] 🐳  → tail -f /proc/1/root/log_
Hello, world!
複製代碼

這裏有一個常見的問題是 free top 等依賴 /proc 文件系統的命令會展現宿主機的信息,這也是容器化過程當中開發者須要適應的一點(固然了,各類 runtime 也要去適應,好比臭名昭著的 Java 8u121 以及更早的版本不識別 cgroups 限制 問題就屬此列)。

診斷 CrashLoopBackoff

排查 CrashLoopBackoff 是一個很麻煩的問題,Pod 可能會不斷重啓, kubectl execkubectl debug 都無法穩定進行排查問題,基本上只能寄但願於 Pod 的日誌中打印出了有用的信息。 爲了讓針對 CrashLoopBackoff 的排查更方便, kubectl-debug 參考 oc debug 命令,添加了一個 --fork 參數。當指定 --fork 時,插件會複製當前的 Pod Spec,作一些小修改, 再建立一個新 Pod:

  • 新 Pod 的全部 Labels 會被刪掉,避免 Service 將流量導到 fork 出的 Pod 上
  • 新 Pod 的 ReadinessProbeLivnessProbe 也會被移除,避免 kubelet 殺死 Pod
  • 新 Pod 中目標容器(待排障的容器)的啓動命令會被改寫,避免新 Pod 繼續 Crash

接下來,咱們就能夠在新 Pod 中嘗試復現舊 Pod 中致使 Crash 的問題。爲了保證操做的一致性,能夠先 chroot 到目標容器的根文件系統中:

➜  ~ kubectl debug demo-pod --fork

root @ /
 [4] 🐳  → chroot /proc/1/root

root @ /
 [#] 🐳 → ls
 bin            entrypoint.sh  home           lib64          mnt            root           sbin           sys            tmp            var
 dev            etc            lib            media          proc           run            srv            usr

root @ /
 [#] 🐳 → ./entrypoint.sh
 # 觀察執行啓動腳本時的信息並根據信息進一步排障
複製代碼

結尾的碎碎念

kubectl-debug 一開始只是 PingCAP 在面試時出的 homework,初版完成在去年年末。當時整個項目還很是粗糙,不只文檔缺失,不少功能也都有問題:

  • 不支持診斷 CrashLoopBackoff 中的 Pod
  • 強制要求預先安裝一個 Debug Agent 的 DaemonSet
  • 不支持公有云(節點沒有公網 IP 或公網 IP 由於防火牆緣由沒法訪問時,就沒法 debug)
  • 沒有權限限制,安全風險很大

而讓我很是興奮的是,在我無暇打理項目的狀況下,隔一兩週就會收到 Pull Request 的通知郵件,一直到今天,大部分影響基礎使用體驗的問題都已經被解決, kubectl-debug 也發佈了 4 個版本( 0.0.1, 0.0.2, 0.1.0, 0.1.1 )。尤爲要感謝 @tkanng , TA 在第一個 PR 時還表示以前沒有寫過 Go, 而在 0.1.1 版本中已是這個版本絕大部分 feature 的貢獻者,解決了好幾個持續好久的 issue,感謝!

最後再上一下項目地址: github.com/aylei/kubec…

假如在使用上或者對項目自己有任何問題,歡迎提交 issue,也能夠在 文章評論區個人郵箱 留言討論。

相關文章
相關標籤/搜索