超好用的k8s中pod診斷工具:Kubectl-debug
背景
容器技術(shù)的一個最佳實踐是構(gòu)建盡可能精簡的容器鏡像。但這一實踐卻會給排查問題帶來麻煩:精簡后的容器中普遍缺失常用的排障工具,部分容器里甚至沒有 shell (比如 FROM scratch )。 在這種狀況下,我們只能通過日志或者到宿主機(jī)上通過 docker-cli 或 nsenter 來排查問題,效率很低,在K8s環(huán)境部署應(yīng)用后,經(jīng)常遇到需要進(jìn)入pod進(jìn)行排錯。除了查看pod logs和describe方式之外,傳統(tǒng)的解決方式是在業(yè)務(wù)pod基礎(chǔ)鏡像中提前安裝好procps、net-tools、tcpdump、vim等工具。但這樣既不符合最小化鏡像原則,又徒增Pod安全漏洞風(fēng)險。
今天為大家推薦一款K8s pod診斷工具,kubectl-debug是一個簡單、易用、強(qiáng)大的 kubectl 插件, 能夠幫助你便捷地進(jìn)行 Kubernetes 上的 Pod 排障診斷。它通過啟動一個排錯工具容器,并將其加入到目標(biāo)業(yè)務(wù)容器的pid, network, user 以及 ipc namespace 中,這時我們就可以在新容器中直接用 netstat, tcpdump 這些熟悉的工具來解決問題了, 而業(yè)務(wù)容器可以保持最小化, 不需要預(yù)裝任何額外的排障工具。 kubectl-debug 主要包含以下兩部分:
- kubectl-debug:命令行工具
- debug-agent:部署在K8s的node上,用于啟動關(guān)聯(lián)排錯工具容器
工作原理
我們知道,容器本質(zhì)上是帶有 cgroup 資源限制和 namespace 隔離的一組進(jìn)程。因此,我們只要啟動一個進(jìn)程,并且讓這個進(jìn)程加入到目標(biāo)容器的各種 namespace 中,這個進(jìn)程就能 “進(jìn)入容器內(nèi)部”(注意引號),與容器中的進(jìn)程”看到”相同的根文件系統(tǒng)、虛擬網(wǎng)卡、進(jìn)程空間了——這也正是 docker exec 和 kubectl exec 等命令的運(yùn)行方式。
現(xiàn)在的狀況是,我們不僅要 “進(jìn)入容器內(nèi)部”,還希望帶一套工具集進(jìn)去幫忙排查問題。那么,想要高效管理一套工具集,又要可以跨平臺,最好的辦法就是把工具本身都打包在一個容器鏡像當(dāng)中。 接下來,我們只需要通過這個”工具鏡像”啟動容器,再指定這個容器加入目標(biāo)容器的的各種 namespace,自然就實現(xiàn)了 “攜帶一套工具集進(jìn)入容器內(nèi)部”。事實上,使用 docker-cli 就可以實現(xiàn)這個操作:
- export TARGET_ID=666666666
- # 加入目標(biāo)容器的 network, pid 以及 ipc namespace
- docker run -it --network=container:$TARGET_ID --pid=container:$TARGET_ID --ipc=container:$TARGET_ID busybox
這就是 kubectl-debug 的出發(fā)點(diǎn): 用工具容器來診斷業(yè)務(wù)容器 。背后的設(shè)計思路和 sidecar 等模式是一致的:每個容器只做一件事情。
具體到實現(xiàn)上,一條 kubectl debug命令背后邏輯流程是這樣的:
步驟分別是:
- 插件查詢 ApiServer:demo-pod 是否存在,所在節(jié)點(diǎn)是什么
- ApiServer 返回 demo-pod 所在所在節(jié)點(diǎn)
- 插件請求在目標(biāo)節(jié)點(diǎn)上創(chuàng)建 Debug Agent Pod
- Kubelet 創(chuàng)建 Debug Agent Pod
- 插件發(fā)現(xiàn) Debug Agent 已經(jīng) Ready,發(fā)起 debug 請求(長連接)
- Debug Agent 收到 debug 請求,創(chuàng)建 Debug 容器并加入目標(biāo)容器的各個 Namespace 中,創(chuàng)建完成后,與 Debug 容器的 tty 建立連接
接下來,客戶端就可以開始通過 5,6 這兩個連接開始 debug 操作。操作結(jié)束后,Debug Agent 清理 Debug 容器,插件清理 Debug Agent,一次 Debug 完成
安裝
github地址:https://github.com/aylei/kubectl-debug
Mac 可以直接使用 brew 安裝
- brew install aylei/tap/kubectl-debug
通過下載二進(jìn)制文件安裝
- 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 頁面選擇進(jìn)行下載windows版本,加入環(huán)境變量使用
其中g(shù)ithub上有提供debug agent以DaemonSet的方式安裝在集群中,但是daemonset模式,agent pod預(yù)先部署在所有node上,會始終占用資源,對于排錯調(diào)試頻率不高的環(huán)境造成資源浪費(fèi)
日常用法說明
簡單使用
1、kubectl 1.12.0 或更高的版本, 可以直接使用
- #查看常用命令參數(shù)
- kubectl debug -h
kubectl 從 1.12 版本之后開始支持從 PATH 中自動發(fā)現(xiàn)插件。1.12 版本之前的 kubectl 不支持這種插件機(jī)制,但也可以通過命令名 kubectl-debug 直接調(diào)用。
2、假如安裝了 debug-agent 的 daemonset, 可以略去 --agentless 來加快啟動速度,之后的命令里會略去 --agentless
- kubectl debug POD_NAME --daemonset-ns=default --daemonset-name=debug-agent
其中g(shù)ithub上有提供debug agent以DaemonSet的方式安裝在集群中,但是daemonset模式,agent pod預(yù)先部署在所有node上,會始終占用資源,對于排錯調(diào)試頻率不高的環(huán)境造成資源浪費(fèi),部署方式:kubectl apply -f https://raw.githubusercontent.com/aylei/kubectl-debug/master/scripts/agent_daemonset.yml
3、agentless模式,kubectl-debug執(zhí)行命令后,才創(chuàng)建agent pod和排錯工具容器,并在退出后刪除工具容器和agent pod。由于每次執(zhí)行都要重新拉起agent,啟動會比daemon-set模式稍慢。使用-a, --agentless開啟agentless模式:
- kubectl debug POD_NAME --agentless --port-forward
4、假如 Node 沒有公網(wǎng) IP 或無法直接訪問(防火墻等原因), 請使用 port-forward 模式
- kubectl debug POD_NAME --agentless --port-forward
進(jìn)階使用
1、排錯init-container
- kubectl debug POD_NAME --container=init-pod
2、假如 Pod 處于 CrashLookBackoff 狀態(tài)無法連接, 可以復(fù)制一個完全相同的 Pod 來進(jìn)行診斷
- kubectl debug POD_NAME --fork
自定義鏡像配置
- --image:可自定義排錯工具容器鏡像,改為私有鏡像倉庫,默認(rèn)為nicolaka/netshoot:latest
- --agent-image:在agentless模式下,自定義debug-agent鏡像,默認(rèn)為aylei/debug-agent:latest。在daemon-set模式下,直接將debug-agent daemonset pod template修改為私有倉庫鏡像即可
配置文件
~/.kube/debug-config,通過配置文件修改默認(rèn)參數(shù),免去使用命令時設(shè)置flag。
- # debug agent listening port(outside container)
- default to 10027
- agentPort: 10027
- whether using agentless mode
- default to false
- agentless: true
- namespace of debug-agent pod, used in agentless mode
- default to 'default'
- agentPodNamespace: default
- prefix of debug-agent pod, used in agentless mode
- default to 'debug-agent-pod'
- agentPodNamePrefix: debug-agent-pod
- image of debug-agent pod, used in agentless mode
- default to 'aylei/debug-agent:latest'
- agentImage: aylei/debug-agent:latest
- daemonset name of the debug-agent, used in port-forward
- default to 'debug-agent'
- debugAgentDaemonset: debug-agent
- daemonset namespace of the debug-agent, used in port-forwad
- default to 'default'
- debugAgentNamespace: kube-system
- whether using port-forward when connecting debug-agent
- default false
- portForward: true
- image of the debug container
- default as showed
- image: nicolaka/netshoot:latest
- start command of the debug container
- default ['bash']
- command:
- - '/bin/bash'
- - '-l'
典型案例
使用 iftop查看pod的網(wǎng)絡(luò)流量
比如查看POD_NAME是kube-flannel-ds-amd64-2xwqp的網(wǎng)絡(luò)流量:
- ~ kubectl debug kube-flannel-ds-amd64-2xwqp -n kube-system
- Agent Pod info: [Name:debug-agent-pod-b14bd868-61a9-11ec-bc72-acbc328370f3, Namespace:default, Image:registry.cn-hangzhou.aliyuncs.com/querycapimages/kubectl-debug-agent:latest, HostPort:10027, ContainerPort:10027]
- Waiting for pod debug-agent-pod-b14bd868-61a9-11ec-bc72-acbc328370f3 to run...
- Forwarding from 127.0.0.1:10027 -> 10027
- Forwarding from [::1]:10027 -> 10027
- Handling connection for 10027
- set container procfs correct false ..
- pulling image registry.cn-hangzhou.aliyuncs.com/querycapimages/netshoot:latest, skip TLS false...
- latest: Pulling from querycapimages/netshoot
- Digest: sha256:f0eba49c9bf66600788d58779e57c2d7334708e12cb292ff8ccc9414c1b6730c
- Status: Image is up to date for registry.cn-hangzhou.aliyuncs.com/querycapimages/netshoot:latest
- starting debug container...
- container created, open tty...
- bash-5.0# iftop -i eth0
- interface: eth0
- IP address is: 172.17.3.3
- MAC address is: 52:54:be:83:3a:e4
使用 drill 診斷 DNS 解析
比如查看POD_NAME是kube-flannel-ds-amd64-2xwqp的網(wǎng)絡(luò)流量:
- bash-5.0# drill any www.baidu.com
- ;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 3214
- ;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 5, ADDITIONAL: 3
- ;; QUESTION SECTION:
- ;; www.baidu.com. IN ANY
- ;; ANSWER SECTION:
- www.baidu.com. 803 IN CNAME www.a.shifen.com.
- ;; AUTHORITY SECTION:
- baidu.com. 38993 IN NS ns4.baidu.com.
- baidu.com. 38993 IN NS ns3.baidu.com.
- baidu.com. 38993 IN NS ns7.baidu.com.
- baidu.com. 38993 IN NS dns.baidu.com.
- baidu.com. 38993 IN NS ns2.baidu.com.
- ;; ADDITIONAL SECTION:
- ns2.baidu.com. 19348 IN A 220.181.33.31
- ns3.baidu.com. 23022 IN A 112.80.248.64
- ns7.baidu.com. 20697 IN A 180.76.76.92
- ;; Query time: 1 msec
- ;; SERVER: 100.64.9.5
- ;; WHEN: Mon Dec 20 15:37:35 2021
- ;; MSG SIZE rcvd: 196
drill 命令詳解:https://commandnotfound.cn/linux/1/533/drill-%E5%91%BD%E4%BB%A4
使用 tcpdump 抓包
比如查看POD_NAME是kube-flannel-ds-amd64-2xwqp的網(wǎng)絡(luò)流量:
- bash-5.0# tcpdump -i eth0 -c 1 -Xvv
- tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
- 15:39:27.577342 IP (tos 0x0, ttl 63, id 41476, offset 0, flags [DF], proto TCP (6), length 89)
- 198.19.116.60.16710 > 172.17.3.3.6443: Flags [P.], cksum 0xf831 (correct), seq 677521811:677521848, ack 1388710574, win 1037, options [nop,nop,TS val 2849535414 ecr 1924260089], length 37
- 0x0000: 4500 0059 a204 4000 3f06 b036 c613 743c E..Y..@.?..6..t<
- 0x0010: ac11 0303 4146 192b 2862 2993 52c6 0aae ....AF.+(b).R...
- 0x0020: 8018 040d f831 0000 0101 080a a9d8 75b6 .....1........u.
- 0x0030: 72b1 e0f9 1703 0300 2047 49f1 8fbb 2835 r........GI...(5
- 0x0040: 059a 5e82 0746 afaf bd2d 5af3 c797 16b5 ..^..F...-Z.....
- 0x0050: 8709 4666 7e61 6f5a 0b ..Ff~aoZ.
- 1 packet captured
- 18 packets received by filter
- 0 packets dropped by kernel
- bash-5.0# tcpdump -n -vvv -w /tmp/kube-flannel-ds-amd64-2xwqp.pcap
- tcpdump: listening on veth19416cac, link-type EN10MB (Ethernet), capture size 262144 bytes
- 50 packets captured
- 50 packets received by filter
- 0 packets dropped by kernel
這里需要注意,如果是想拿到-w抓包保存的文件用wireshark工具分析,則需要去POD_NAME對應(yīng)的宿主機(jī)上拷貝出來進(jìn)行分析
- [root@k8s-demo-master-01-2 ~]# docker ps |grep netshoot
- 58b918b67b3f registry.cn-hangzhou.aliyuncs.com/querycapimages/netshoot:latest "bash" 15 minutes ago Up 15 minutes unruffled_fermat
- [root@k8s-demo-master-01-2 ~]# docker cp 58b918b67b3f:/tmp/kube-flannel-ds-amd64-2xwqp.pcap .
- [root@k8s-demo-master-01-2 ~]# ll |grep kube-flannel-ds-amd64-2xwqp.pcap
- -rw-r--r-- 1 root root 5404 12月 20 23:41 kube-flannel-ds-amd64-2xwqp.pcap
診斷 CrashLoopBackoff
排查 CrashLoopBackoff 是一個很麻煩的問題,Pod 可能會不斷重啟, kubectl exec 和 kubectl debug 都沒法穩(wěn)定進(jìn)行排查問題,基本上只能寄希望于 Pod 的日志中打印出了有用的信息。 為了讓針對 CrashLoopBackoff 的排查更方便, kubectl-debug 參考 oc debug 命令,添加了一個 --fork 參數(shù)。當(dāng)指定 --fork 時,插件會復(fù)制當(dāng)前的 Pod Spec,做一些小修改, 再創(chuàng)建一個新 Pod:
- 新 Pod 的所有 Labels 會被刪掉,避免 Service 將流量導(dǎo)到 fork 出的 Pod 上
- 新 Pod 的 ReadinessProbe 和 LivnessProbe 也會被移除,避免 kubelet 殺死 Pod
- 新 Pod 中目標(biāo)容器(待排障的容器)的啟動命令會被改寫,避免新 Pod 繼續(xù) Crash
接下來,我們就可以在新 Pod 中嘗試復(fù)現(xiàn)舊 Pod 中導(dǎo)致 Crash 的問題,示例pod_name為srv-es-driver-7445f6cf48-ff7bq的go服務(wù)。為了保證操作的一致性,可以先 chroot 到目標(biāo)容器的根文件系統(tǒng)中:
- ~ kubectl-debug srv-es-driver-7445f6cf48-ff7bq -n devops --agentless --port-forward
- Agent Pod info: [Name:debug-agent-pod-177482f4-61ad-11ec-b297-acbc328370f3, Namespace:default, Image:registry.cn-hangzhou.aliyuncs.com/querycapimages/kubectl-debug-agent:latest, HostPort:10027, ContainerPort:10027]
- Waiting for pod debug-agent-pod-177482f4-61ad-11ec-b297-acbc328370f3 to run...
- Forwarding from 127.0.0.1:10027 -> 10027
- Forwarding from [::1]:10027 -> 10027
- Handling connection for 10027
- set container procfs correct false ..
- pulling image registry.cn-hangzhou.aliyuncs.com/querycapimages/netshoot:latest, skip TLS false...
- latest: Pulling from querycapimages/netshoot
- Digest: sha256:f0eba49c9bf66600788d58779e57c2d7334708e12cb292ff8ccc9414c1b6730c
- Status: Image is up to date for registry.cn-hangzhou.aliyuncs.com/querycapimages/netshoot:latest
- starting debug container...
- container created, open tty...
- bash-5.0# ls
- bin mnt sys
- dev opt termshark_2.1.1_linux_x64
- etc proc tmp
- home root usr
- lib run var
- lib64 sbin
- media srv
- bash-5.0# chroot /proc/1/root
- root@srv-es-driver-7445f6cf48-ff7bq:/# ls
- bin dev go lib media opt root sbin sys usr
- boot etc home lib64 mnt proc run srv tmp var
- root@srv-es-driver-7445f6cf48-ff7bq:/# cd /go/bin/
- root@srv-es-driver-7445f6cf48-ff7bq:/go/bin# ls
- openapi.json srv-es-driver
- root@srv-es-driver-7445f6cf48-ff7bq:/go/bin# ./srv-es-driver
- # 觀察執(zhí)行啟動腳本時的信息并根據(jù)信息進(jìn)一步排障
自定義image作為sidercar安裝命令行調(diào)試
對于沒有安裝yum,apt-get 的鏡像可以掛載 centos或者ubuntu的sidercar鏡像, 再進(jìn)行操作, 如安裝 redis 命令, 再使用redis-cli 命令
- ~ kubectl-debug srv-es-driver-7445f6cf48-ff7bq -n devops --agentless --port-forward --image centos
- Agent Pod info: [Name:debug-agent-pod-f5077b08-61ad-11ec-8728-acbc328370f3, Namespace:default, Image:registry.cn-hangzhou.aliyuncs.com/querycapimages/kubectl-debug-agent:latest, HostPort:10027, ContainerPort:10027]
- Waiting for pod debug-agent-pod-f5077b08-61ad-11ec-8728-acbc328370f3 to run...
- Forwarding from 127.0.0.1:10027 -> 10027
- Forwarding from [::1]:10027 -> 10027
- Handling connection for 10027
- set container procfs correct false ..
- pulling image centos, skip TLS false...
- latest: Pulling from library/centos
- a1d0c7532777: Pull complete
- Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
- Status: Downloaded newer image for centos:latest
- starting debug container...
- container created, open tty...
- [root@srv-es-driver-7445f6cf48-ff7bq /]# yum install -y redis
參考鏈接:
https://aleiwu.com/post/kubectl-debug-intro/
本文轉(zhuǎn)載自微信公眾號「運(yùn)維開發(fā)故事」