容器技術的一個最佳實踐是構建儘量精簡的容器鏡像。但這一實踐卻會給排查問題帶來麻煩:精簡後的容器中廣泛缺失經常使用的排障工具,部分容器裏甚至沒有 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 exec
和 kubectl 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
Debug Agent
PodDebug Agent
PodDebug Agent
已經 Ready,發起 debug 請求(長鏈接)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
是一個很麻煩的問題,Pod 可能會不斷重啓, kubectl exec
和 kubectl debug
都無法穩定進行排查問題,基本上只能寄但願於 Pod 的日誌中打印出了有用的信息。 爲了讓針對 CrashLoopBackoff
的排查更方便, kubectl-debug
參考 oc debug
命令,添加了一個 --fork
參數。當指定 --fork
時,插件會複製當前的 Pod Spec,作一些小修改, 再建立一個新 Pod:
ReadinessProbe
和 LivnessProbe
也會被移除,避免 kubelet 殺死 Pod接下來,咱們就能夠在新 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,初版完成在去年年末。當時整個項目還很是粗糙,不只文檔缺失,不少功能也都有問題:
而讓我很是興奮的是,在我無暇打理項目的狀況下,隔一兩週就會收到 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…