简化 Pod 故障诊断:kubectl-debug 介绍
背景
工作原理
我們知道,容器本質上是帶有 Cgroup 資源限制和 Namespace 隔離的一組進程。因此,我們只要啟動一個進程,并且讓這個進程加入到目標容器的各種 Namespace 中,這個進程就能 “進入容器內部”(注意引號),與容器中的進程”看到”相同的根文件系統、虛擬網卡、進程空間了——這也正是 docker exec 和 kubectl exec 等命令的運行方式。
現在的狀況是,我們不僅要 “進入容器內部”,還希望帶一套工具集進去幫忙排查問題。那么,想要高效管理一套工具集,又要可以跨平臺,最好的辦法就是把工具本身都打包在一個容器鏡像當中。接下來,我們只需要通過這個”工具鏡像”啟動容器,再指定這個容器加入目標容器的的各種 namespace,自然就實現了 “攜帶一套工具集進入容器內部”。事實上,使用 docker-cli 就可以實現這個操作:
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 等模式是一致的:每個容器只做一件事情。
具體到實現上,一條 kubectl debug命令背后是這樣的:
步驟分別是:
插件查詢 ApiServer:demo-pod 是否存在,所在節點是什么
ApiServer 返回 demo-pod 所在所在節點
插件請求在目標節點上創建 Debug Agent Pod
Kubelet 創建 Debug Agent Pod
插件發現 Debug Agent 已經 Ready,發起 debug 請求(長連接)
Debug Agent 收到 debug 請求,創建 Debug 容器并加入目標容器的各個 Namespace 中,創建完成后,與 Debug 容器的 tty 建立連接
開始使用
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 頁面[4]進行下載。
下載完之后就可以開始使用 debug 插件:
kubectl debug target-pod --agentless --port-forwardkubectl 從 1.12 版本之后開始支持從 PATH 中自動發現插件。1.12 版本之前的 kubectl 不支持這種插件機制,但也可以通過命令名 kubectl-debug 直接調用。可以參考項目的中文 README[5]來獲得更多文檔和幫助信息。
典型案例
kubectl debug 默認使用 nicolaka/netshoot[6] 作為默認的基礎鏡像,里面內置了相當多的排障工具,包括:
使用 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 限制[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
? ~ 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 # 觀察執行啟動腳本時的信息并根據信息進一步排障
結尾的碎碎念
不支持診斷 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,也可以在 文章評論區留言討論。
相關鏈接:
https://github.com/kubernetes/kubernetes/issues/27140
https://github.com/kubernetes/community/blob/master/contributors/design-proposals/node/troubleshoot-running-pods.md
https://github.com/aylei/kubectl-debug
https://github.com/aylei/kubectl-debug/releases/tag/v0.1.1
https://github.com/aylei/kubectl-debug/blob/master/docs/zh-cn.md
https://github.com/nicolaka/netshoot
https://blog.softwaremill.com/docker-support-in-new-java-8-finally-fd595df0ca54
原文鏈接:https://aleiwu.com/post/kubectl-debug-intro/
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?
總結
以上是生活随笔為你收集整理的简化 Pod 故障诊断:kubectl-debug 介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 架构杂谈《二》
- 下一篇: dotnet 新项目格式与对应框架预定义