Kubernetes 故障診斷神器 kubectl-debug 入門教程

運維之美2019-08-18 02:27:06


背景


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

工作原理


我們先不着急進入 Quick Start 環節。kubectl-debug 本身非常簡單,因此只要理解了它的工作原理,你就能完全掌握這個工具,並且還能用它做 debug 之外的事情。

我們知道,容器本質上是帶有 Cgroup 資源限制和 Namespace 隔離的一組進程。因此,我們只要啟動一個進程,並且讓這個進程加入到目標容器的各種 Namespace 中,這個進程就能 “進入容器內部”(注意引號),與容器中的進程”看到”相同的根文件系統、虛擬網卡、進程空間了——這也正是 docker exec 和 kubectl exec 等命令的運行方式。

現在的狀況是,我們不僅要 “進入容器內部”,還希望帶一套工具集進去幫忙排查問題。那麼,想要高效管理一套工具集,又要可以跨平台,最好的辦法就是把工具本身都打包在一個容器鏡像當中。接下來,我們只需要通過這個”工具鏡像”啟動容器,再指定這個容器加入目標容器的的各種 namespace,自然就實現了 “攜帶一套工具集進入容器內部”。事實上,使用 docker-cli 就可以實現這個操作:

  1. export TARGET_ID=666666666

  2. # 加入目標容器的 network, pid 以及 ipc namespace

  3. docker run -it --network=container:$TARGET_ID --pid=container:$TARGET_ID --ipc=container:$TARGET_ID busybox


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

具體到實現上,一條 kubectl debug命令背後是這樣的:


步驟分別是:

  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 完成。效果如下圖:


開始使用


Mac 可以直接使用 brew 安裝:

  1. brew install aylei/tap/kubectl-debug


所有平台都可以通過下載 binary 安裝:

  1. export PLUGIN_VERSION=0.1.1

  2. # linux x86_64

  3. 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

  4. # macos

  5. 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


  6. tar -zxvf kubectl-debug.tar.gz kubectl-debug

  7. sudo mv kubectl-debug /usr/local/bin/


Windows 用户可以在 Release 頁面[4]進行下載。

下載完之後就可以開始使用 debug 插件:

  1. kubectl debug target-pod --agentless --port-forward

kubectl 從 1.12 版本之後開始支持從 PATH 中自動發現插件。1.12 版本之前的 kubectl 不支持這種插件機制,但也可以通過命令名 kubectl-debug 直接調用。
可以參考項目的中文 README[5]來獲得更多文檔和幫助信息。

典型案例


基礎排障

kubectl debug 默認使用 nicolaka/netshoot[6] 作為默認的基礎鏡像,裏面內置了相當多的排障工具,包括:

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

  1. ~ kubectl debug demo-pod


  2. root @ /

  3. [2] 🐳 iftop -i eth0

  4. interface: eth0

  5. IP address is: 10.233.111.78

  6. MAC address is: 86:c3:ae:9d:46:2b

  7. # (圖片略去)


使用 drill 診斷 DNS 解析:

  1. root @ /

  2. [3] 🐳 drill -V 5 demo-service

  3. ;; ->>HEADER opcode: QUERY, rcode: NOERROR, id: 0

  4. ;; flags: rd ; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

  5. ;; QUESTION SECTION:

  6. ;; demo-service. IN A


  7. ;; ANSWER SECTION:


  8. ;; AUTHORITY SECTION:


  9. ;; ADDITIONAL SECTION:


  10. ;; Query time: 0 msec

  11. ;; WHEN: Sat Jun 1 05:05:39 2019

  12. ;; MSG SIZE rcvd: 0

  13. ;; ->>HEADER opcode: QUERY, rcode: NXDOMAIN, id: 62711

  14. ;; flags: qr rd ra ; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0

  15. ;; QUESTION SECTION:

  16. ;; demo-service. IN A


  17. ;; ANSWER SECTION:


  18. ;; AUTHORITY SECTION:

  19. . 30 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2019053101 1800 900 604800 86400


  20. ;; ADDITIONAL SECTION:


  21. ;; Query time: 58 msec

  22. ;; SERVER: 10.233.0.10

  23. ;; WHEN: Sat Jun 1 05:05:39 2019

  24. ;; MSG SIZE rcvd: 121


使用 tcpdump 抓包:

  1. root @ /

  2. [4] 🐳 tcpdump -i eth0 -c 1 -Xvv

  3. tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

  4. 12:41:49.707470 IP (tos 0x0, ttl 64, id 55201, offset 0, flags [DF], proto TCP (6), length 80)

  5. 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

  6. 0x0000: 4500 0050 d7a1 4000 4006 6e71 0ae9 6f4e E..P[email protected]@.nq..oN

  7. 0x0010: 0ae9 6f75 88ee 094b 51e6 0888 50b5 4295 ..ou...KQ...P.B.

  8. 0x0020: 8018 0590 f4d7 0000 0101 080a ab2d 52df .............-R.

  9. 0x0030: ab2d 4fc1 0000 1300 0000 0000 0100 0000 .-O.............

  10. 0x0040: 000e 0a0a 08a1 86b2 ebe2 ced1 f85c 1001 .............\..

  11. 1 packet captured

  12. 11 packets received by filter

  13. 0 packets dropped by kernel


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

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

  1. root @ /

  2. [5] 🐳 tail -f /proc/1/root/log_

  3. Hello, world!


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

診斷 CrashLoopBackoff

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

  • 新 Pod 的所有 Labels 會被刪掉,避免 Service 將流量導到 fork 出的 Pod 上

  • 新 Pod 的 ReadinessProbe 和 LivnessProbe 也會被移除,避免 kubelet 殺死 Pod

  • 新 Pod 中目標容器(待排障的容器)的啟動命令會被改寫,避免新 Pod 繼續 Crash


接下來,我們就可以在新 Pod 中嘗試復現舊 Pod 中導致 Crash 的問題。為了保證操作的一致性,可以先 chroot 到目標容器的根文件系統中:

  1. ~ kubectl debug demo-pod --fork


  2. root @ /

  3. [4] 🐳 chroot /proc/1/root


  4. root @ /

  5. [#] 🐳 ls

  6. bin entrypoint.sh home lib64 mnt root sbin sys tmp var

  7. dev etc lib media proc run srv usr


  8. root @ /

  9. [#] 🐳 ./entrypoint.sh

  10. # 觀察執行啟動腳本時的信息並根據信息進一步排障


結尾的碎碎念


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,感謝!

最後再上一下項目地址:https://github.com/aylei/kubectl-debug

假如在使用上或者對項目本身有任何問題,歡迎提交 issue,也可以在 文章評論區留言討論。

相關鏈接:

  1. https://github.com/kubernetes/kubernetes/issues/27140

  2. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/node/troubleshoot-running-pods.md

  3. https://github.com/aylei/kubectl-debug

  4. https://github.com/aylei/kubectl-debug/releases/tag/v0.1.1

  5. https://github.com/aylei/kubectl-debug/blob/master/docs/zh-cn.md

  6. https://github.com/nicolaka/netshoot

  7. https://blog.softwaremill.com/docker-support-in-new-java-8-finally-fd595df0ca54

來源:Aylei's Blog

原文:http://t.cn/AiWl4YKl

題圖:來自谷歌圖片搜索 

版權:本文版權歸原作者所有

投稿:歡迎投稿,投稿郵箱: [email protected]


你可能還喜歡

點擊下方圖片即可閲讀

談談 Kubernetes 架構


https://hk.wxwenku.com/d/201171388