如何在 Kubernetes Pod 內(nèi)進(jìn)行網(wǎng)絡(luò)抓包
使用 Kubernetes 時(shí),經(jīng)常會(huì)遇到一些棘手的網(wǎng)絡(luò)問題需要對(duì) Pod 內(nèi)的流量進(jìn)行抓包分析。然而所使用的鏡像一般不會(huì)帶有 tcpdump 命令,過去常用的做法簡單直接暴力:登錄到節(jié)點(diǎn)所在節(jié)點(diǎn),使用 root 賬號(hào)進(jìn)入容器,然后安裝 tcpdump。抓到的包有時(shí)還需要拉回本地,使用 Wireshark 進(jìn)行分析。而且整個(gè)過程非常繁瑣,跨越幾個(gè)環(huán)境。
正好前幾天也做了一次抓包問題排查,這次就介紹一下快速進(jìn)行網(wǎng)絡(luò)抓包的幾種方法。
TL;DR
幾種方法各有優(yōu)缺點(diǎn),且都不建議在生產(chǎn)環(huán)境使用。假如必須使用,個(gè)人傾向于 kubectl debug 臨時(shí)容器的方案,但這個(gè)方案也有不足。
使用額外容器:這種方案為了 Pod 添加一個(gè)額外的容器,使用了靜態(tài)編譯的 tcpdump 進(jìn)行抓取,借助了多容器共享網(wǎng)絡(luò)空間的特性,適合 distroless 容器。缺點(diǎn)是需要修改原來的 Pod,調(diào)式容器重啟會(huì)引起 Pod 重啟。 kubectl plugin ksniff:一個(gè) kubectl 插件。支持特權(quán)和非特權(quán)容器,可以將捕獲內(nèi)容重定向到 wireshark 或者 tshark。非特權(quán)容器的實(shí)現(xiàn)會(huì)稍微復(fù)雜。 kubectl debug 臨時(shí)容器:該方案對(duì)于 distroless 容器有很好的支持,臨時(shí)容器退出后也不會(huì)導(dǎo)致 Pod 重啟。缺點(diǎn)是 1.23 的版本臨時(shí)容器才進(jìn)入 beta 階段;而且筆者在將捕獲的數(shù)據(jù)重定向到本地的 Wireshark 時(shí)會(huì)報(bào)數(shù)據(jù)格式不支持的錯(cuò)誤。
環(huán)境
使用 k3d 創(chuàng)建 k3s 集群,這里版本選擇 1.23:
$?k3d?cluster?create?test?--image?rancher/k3s:v1.23.4-k3s1
抓包的對(duì)象使用 Pipy[1] 運(yùn)行的一個(gè) echo 服務(wù)(返回請(qǐng)求的 body 內(nèi)容):
$?kubectl?run?echo?--image?addozhang/echo-server?--image-pull-policy?IfNotPresent
為了方便訪問,創(chuàng)建一個(gè) NodePort Service:
$?kubectl?expose?pod?echo?--name?echo?--port?8080?--type?NodePort
掛載容器
在之前的文章我們介紹調(diào)試 distroless 容器的幾種方法時(shí)曾用過修改 Pod 添加額外容器的方式,新的容器使用鏡像 addozhang/static-dump 鏡像。這個(gè)鏡像中加入了靜態(tài)編譯的 tcpdump。
修改后的 Pod:
apiVersion:?v1
kind:?Pod
metadata:
??labels:
????run:?echo
??name:?echo
spec:
??containers:
??-?image:?addozhang/echo-server
????imagePullPolicy:?IfNotPresent
????name:?echo
????resources:?{}?
??-?image:?addozhang/static-dump
????imagePullPolicy:?IfNotPresent
????name:?sniff
????command:?['sleep',?'1d']
??dnsPolicy:?ClusterFirst
??restartPolicy:?Always
status:?{}
重新部署后,就可以使用下面命令將抓取網(wǎng)絡(luò)包并重定向到本地的 Wireshark:
$?kubectl?exec?-i?echo?-c?sniff?--?/static-tcpdump?-i?eth0?-U?-w?-?|?wireshark?-k?-i?-

kubectl plugin ksniff
ksniff[2] 是一個(gè) kubectl 插件,利用 tcpdump ?和 Wireshark 對(duì) Pod 中的網(wǎng)絡(luò)包實(shí)現(xiàn)遠(yuǎn)程抓取。使用這種方法既可以借助 Wireshark 的強(qiáng)大功能,又能降低對(duì) Pod 的影響。
ksniff 的實(shí)現(xiàn)是上傳一個(gè)靜態(tài)編譯的tcpdump 到 Pod 中,然后將 tcpdump 的輸出重定向到本地的 Wireshark 進(jìn)行調(diào)試。
核心可以理解成 tcpdump -w - | wireshark -k -i -,與前面使用 debug 容器的方案類似。
安裝
通過 krew[3] 安裝:
$?kubectl?krew?install?sniff
或者下載發(fā)布包,手動(dòng)安裝:
$?unzip?ksniff.zip
$?make?install
特權(quán)模式容器
使用說明參考 ksniff 官方說明[4],這里我們只需要執(zhí)行如下命令,默認(rèn)就會(huì)重定向到 Wireshark,不需要顯示地指定:
$?kubectl?sniff?echo?-n?default?-f?"port?8080"

除了使用 Wireshark,可以使用其命令行模式的 tshark:
$?kubectl?sniff?echo?-n?default?-f?"port?8080"?-o?-?|?tshark?-r?-
非特權(quán)模式容器
對(duì)于無特權(quán)的容器,就無法使用上面的方法了,會(huì)收到如下的錯(cuò)誤提示:
INFO[0000]?command:?'[/tmp/static-tcpdump?-i?any?-U?-w?-?port?8080]'?executing?successfully?exitCode:?'1',?stdErr?:'static-tcpdump:?any:?You?don't?have?permission?to?capture?on?that?device
(socket:?Operation?not?permitted)
不過,Ksniff 對(duì)此類容器也提供了支持。通過添加 -p 參數(shù),ksniff 會(huì)創(chuàng)建一個(gè)新的可以訪問節(jié)點(diǎn)上 Docker Daemon 的 pod,然后將容器附加到目標(biāo)容器的網(wǎng)絡(luò)命名空間,并執(zhí)行報(bào)文捕獲。
注意,筆者使用的是 k3s 的環(huán)境,執(zhí)行命令時(shí)需要通過參數(shù)指定 Docker Daemon 的 socket 地址 --socket /run/k3s/containerd/containerd.sock
$?kubectl?sniff?echo?-n?default?-f?"port?8080"?--socket?/run/k3s/containerd/containerd.sock?-p?|?wireshark?-k?-i?-

kubectl debug 臨時(shí)容器
接下來也是之前介紹過的 kubectl debug ,也就是為 Pod 添加臨時(shí)容器[5]。
同樣我們可以通過這種方法對(duì) Pod 的網(wǎng)絡(luò)進(jìn)行抓包,臨時(shí)容器我們使用 addozhang/static-dump 鏡像。
$?kubectl?debug?-i?echo?--image?addozhang/static-dump?--target?echo?--?/static-tcpdump?-i?eth0?

大家能發(fā)現(xiàn)這里將捕獲的內(nèi)容直接輸出在標(biāo)準(zhǔn)輸出中了,而不是重定向到本地的 Wireshark。
原本臨時(shí)容器應(yīng)該是其中最接近完美的方案:不需上傳任何文件目標(biāo)容器、無需修改 Pod、無需重啟、無需特權(quán)、支持 distroless 容器。然而,當(dāng)嘗試重定向到 Wireshark 或者 tshark 的時(shí)候,會(huì)遇到 Data written to the pipe is neither in a supported pcap format nor in pcapng format. 問題。
最后經(jīng)過一番折騰,也未能解決該問題。有解決了問題的朋友,也麻煩評(píng)論告知一下。感謝!
參考資料
Pipy: https://github.com/flomesh-io/pipy
[2]ksniff: https://github.com/eldadru/ksniff
[3]krew: https://github.com/GoogleContainerTools/krew
[4]ksniff 官方說明: https://github.com/eldadru/ksniff#usage
[5]臨時(shí)容器: https://kubernetes.io/zh/docs/concepts/workloads/pods/ephemeral-containers/
