如何在 Kubernetes Pod 内进行网络抓包
作者 |?Addo Zhang
來源 |?云原生指北
使用 Kubernetes 時,經常會遇到一些棘手的網絡問題需要對 Pod 內的流量進行抓包分析。然而所使用的鏡像一般不會帶有 tcpdump 命令,過去常用的做法簡單直接暴力:登錄到節點所在節點,使用 root 賬號進入容器,然后安裝 tcpdump。抓到的包有時還需要拉回本地,使用 Wireshark 進行分析。而且整個過程非常繁瑣,跨越幾個環境。
正好前幾天也做了一次抓包問題排查,這次就介紹一下快速進行網絡抓包的幾種方法。
TL;DR
幾種方法各有優缺點,且都不建議在生產環境使用。假如必須使用,個人傾向于 kubectl debug 臨時容器的方案,但這個方案也有不足。
使用額外容器:這種方案為了 Pod 添加一個額外的容器,使用了靜態編譯的 tcpdump 進行抓取,借助了多容器共享網絡空間的特性,適合 distroless 容器。缺點是需要修改原來的 Pod,調式容器重啟會引起 Pod 重啟。
kubectl plugin ksniff:一個 kubectl 插件。支持特權和非特權容器,可以將捕獲內容重定向到 wireshark 或者 tshark。非特權容器的實現會稍微復雜。
kubectl debug 臨時容器:該方案對于 distroless 容器有很好的支持,臨時容器退出后也不會導致 Pod 重啟。缺點是 1.23 的版本臨時容器才進入 beta 階段;而且筆者在將捕獲的數據重定向到本地的 Wireshark 時會報數據格式不支持的錯誤。
環境
使用 k3d 創建 k3s 集群,這里版本選擇 1.23:
$?k3d?cluster?create?test?--image?rancher/k3s:v1.23.4-k3s1抓包的對象使用 Pipy[1] 運行的一個 echo 服務(返回請求的 body 內容):
$?kubectl?run?echo?--image?addozhang/echo-server?--image-pull-policy?IfNotPresent為了方便訪問,創建一個 NodePort Service:
$?kubectl?expose?pod?echo?--name?echo?--port?8080?--type?NodePort掛載容器
在之前的文章我們介紹調試 distroless 容器的幾種方法時曾用過修改 Pod 添加額外容器的方式,新的容器使用鏡像 addozhang/static-dump鏡像。這個鏡像中加入了靜態編譯的 tcpdump。
修改后的 Pod:
apiVersion:?v1 kind:?Pod metadata:labels:run:?echoname:?echo spec:containers:-?image:?addozhang/echo-serverimagePullPolicy:?IfNotPresentname:?echoresources:?{}?-?image:?addozhang/static-dumpimagePullPolicy:?IfNotPresentname:?sniffcommand:?['sleep',?'1d']dnsPolicy:?ClusterFirstrestartPolicy:?Always status:?{}重新部署后,就可以使用下面命令將抓取網絡包并重定向到本地的 Wireshark:
$?kubectl?exec?-i?echo?-c?sniff?--?/static-tcpdump?-i?eth0?-U?-w?-?|?wireshark?-k?-i?-debug-containerkubectl plugin ksniff
ksniff[2]是一個 kubectl 插件,利用 tcpdump ?和 Wireshark 對 Pod 中的網絡包實現遠程抓取。使用這種方法既可以借助 Wireshark 的強大功能,又能降低對 Pod 的影響。
ksniff 的實現是上傳一個靜態編譯的tcpdump 到 Pod 中,然后將 tcpdump 的輸出重定向到本地的 Wireshark 進行調試。
核心可以理解成tcpdump -w - | wireshark -k -i -,與前面使用 debug 容器的方案類似。
安裝
通過 krew[3] 安裝:
$?kubectl?krew?install?sniff或者下載發布包,手動安裝:
$?unzip?ksniff.zip $?make?install特權模式容器
使用說明參考 ksniff 官方說明[4],這里我們只需要執行如下命令,默認就會重定向到 Wireshark,不需要顯示地指定:
$?kubectl?sniff?echo?-n?default?-f?"port?8080"ksniff除了使用 Wireshark,可以使用其命令行模式的 tshark:
$?kubectl?sniff?echo?-n?default?-f?"port?8080"?-o?-?|?tshark?-r?-非特權模式容器
對于無特權的容器,就無法使用上面的方法了,會收到如下的錯誤提示:
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 對此類容器也提供了支持。通過添加 -p 參數,ksniff 會創建一個新的可以訪問節點上 Docker Daemon 的 pod,然后將容器附加到目標容器的網絡命名空間,并執行報文捕獲。
注意,筆者使用的是 k3s 的環境,執行命令時需要通過參數指定 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?-ksniff-priviledgedkubectl debug 臨時容器
接下來也是之前介紹過的 kubectl debug ,也就是為 Pod 添加臨時容器[5]。
同樣我們可以通過這種方法對 Pod 的網絡進行抓包,臨時容器我們使用addozhang/static-dump 鏡像。
$?kubectl?debug?-i?echo?--image?addozhang/static-dump?--target?echo?--?/static-tcpdump?-i?eth0ephermeral-sniff-stdout大家能發現這里將捕獲的內容直接輸出在標準輸出中了,而不是重定向到本地的 Wireshark。
原本臨時容器應該是其中最接近完美的方案:不需上傳任何文件目標容器、無需修改 Pod、無需重啟、無需特權、支持 distroless 容器。然而,當嘗試重定向到 Wireshark 或者 tshark 的時候,會遇到 Data written to the pipe is neither in a supported pcap format nor in pcapng format. 問題。
最后經過一番折騰,也未能解決該問題。有解決了問題的朋友,也麻煩評論告知一下。感謝!
參考資料
[1]
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]臨時容器: https://kubernetes.io/zh/docs/concepts/workloads/pods/ephemeral-containers/
往期推薦
如果讓你來設計網絡
用過留痕,誰動了我的檔案?
一把王者的時間,我就學會了Nginx
明明還有大量內存,為啥報錯“無法分配內存”?
點分享
點收藏
點點贊
點在看
總結
以上是生活随笔為你收集整理的如何在 Kubernetes Pod 内进行网络抓包的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 腾讯自研分布式远程Shuffle服务Fi
- 下一篇: Orion:谷歌的新一代SDN控制器