1.背景
??從一開始接觸k8s,就被其復雜的網絡架構虐的體無完膚。不同的網絡組建使用了不同的虛擬網絡設備,要理解整個k8s網絡,會設計許多層面的知識。即使通過學習,實驗,測試。從理論層面明白了k8s網絡的數據流路徑。但是沒有真切的看到,還是會心里打一個大大的問號?k8s的數據包真的是經過了那么多層的虛擬設備,那么多層的iptables規則鏈嗎?從我接觸到的資料而言,大多都是從理論層面講解k8s的網絡。沒有一個直觀的體驗。今天記住了。明天處理問題又忘了。確實讓人難受。
??在此之前,這種問題沒找到什么好的方法。或許是我太淺薄的緣故。不過,總算是遇到了一個技術讓我想看到的東西都可以清晰的呈現在眼前。這個技術就是EBPF.
2.EBPF
??EBPF有許多的使用場景,作為一個運維,目前最關注的就是其在動態跟蹤方面的實踐。我想知道一些之前無法直觀看到的東西。動態跟蹤技術可以幫助我實現。如果未曾了解過什么事動態跟蹤。可以看章亦春大佬的這篇文章動態跟蹤技術漫談.寫的相當之詳細,所以此處就不再說明。文章中其實沒有太多篇幅提及EBPF。提到更多的是SystemTap。也許比EBPF更強大吧。待以后再驗證吧。
??繼續說EBPF,至于什么是BPF,然后又如何來的EBPF,此處依然不贅述,貼出一篇文章來:eBPF簡史,理論性的東西實在不太擅長。也就只能記錄下自己的思考,以及實踐了。直接操作eBPF實在是不太容易,所以目前比較好的方式是使用框架來進行。我目前主要使用iovisor出的BCC,內核層的代碼用c語言,用戶層的代碼用python。簡單又高效。
3.BCC工具安裝
?? 此處使用的工具為tracepkt,此工具基于ping命令實現。使用方式就是把ping 127.0.0.1
改為python tracepkt.py 127.0.0.1
. ping命令的問題在于,其只是一個網絡的連通性和延遲測試。沒有跟多的信息展示出來。而tracepkt工具,可以展示出icmp數據經過了哪些iptables鏈和虛擬/真實網卡設備。對于容器網絡而言,其實只是在不能的namespace而已。服務器在一個namespace中,容器網絡在另一個nemesapce中。此工具就可以輸出數據包所處于哪個namespace,簡直是恐怖如斯。仿佛是夢中的工具一般。
?? tracepkt主要依賴BCC。BCC對操作系統內核是有要求的。centos7需要升級內核。ubuntu18.04直接就可以使用。內核版本需要大于4.1.對內核的配置參數也有要求。不過ubuntu18.04默認就開啟了。
??此處僅展示ubuntu18.04中的安裝過程,其他系統安裝請參考官方文檔: BCC 安裝
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD
echo "deb https://repo.iovisor.org/apt/$(lsb_release -cs) $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/iovisor.list
sudo apt-get update
sudo apt-get install bcc-tools libbcc-examples linux-headers-$(uname -r)
?? 安裝完成后就可以運行基于BCC的工具了。其實bcc-tools官方本身就包含了許多強大的工具。這些工具我們后面在表。在目錄/usr/share/bcc/tools
下。大家也可以自行測試著玩。
?? 安裝完BCC環境之后就可以下載tracepkt工具了
git clone https://github.com/smartxff/tracepkt.git
?? 這是我自己維護的版本,目前之添加一個小小的功能。可以展示出當前數據包在哪個內核函數/TRACEPOINT。
?? 在某些服務器上由于沒開啟ipv6可能會報錯。
python3 tracepkt.py
...
...
Traceback (most recent call last):
File "tracepkt.py", line 127, in <module>
b = BPF(src_file='tracepkt.c')
File "/usr/lib/python3.8/site-packages/bcc/__init__.py", line 364, in __init__
self._trace_autoload()
File "/usr/lib/python3.8/site-packages/bcc/__init__.py", line 1183, in _trace_autoload
fn = self.load_func(func_name, BPF.KPROBE)
File "/usr/lib/python3.8/site-packages/bcc/__init__.py", line 403, in load_func
raise Exception("Failed to load BPF program %s: %s" %
Exception: Failed to load BPF program b'kretprobe__ipt_do_table': Invalid argument
?? 可以注釋掉ipv6相關的代碼在tracepkt.c文件中的最后幾行注釋掉就可以了
/*
int kprobe__ip6t_do_table(struct pt_regs *ctx, struct sk_buff *skb, const struct nf_hook_state *state, struct xt_table *table)
{
return __ipt_do_table_in(ctx, skb, state, table);
};
int kretprobe__ip6t_do_table(struct pt_regs *ctx)
{
return __ipt_do_table_out(ctx);
}
*/
4.跟蹤開始---服務器
?? 我們先直接運行命令看會有什么輸出
python tracepkt.py
TRACEPOINT NETWORK NS INTERFACE TYPE ADDRESSES IPTABLES
ipt_do_table [ 0] request 127.0.0.1 -> 127.0.0.1 nat.OUTPUT :ACCEPT
ipt_do_table [ 0] request 127.0.0.1 -> 127.0.0.1 filter.OUTPUT :ACCEPT
ipt_do_table [ 4026531993] lo request 127.0.0.1 -> 127.0.0.1 nat.POSTROUTING :ACCEPT
net:net_dev_queue [ 4026531993] lo request 127.0.0.1 -> 127.0.0.1
net:netif_rx [ 4026531993] lo request 127.0.0.1 -> 127.0.0.1
ipt_do_table [ 4026531993] lo request 127.0.0.1 -> 127.0.0.1 filter.INPUT :ACCEPT
ipt_do_table [ 0] reply 127.0.0.1 -> 127.0.0.1 filter.OUTPUT :ACCEPT
net:net_dev_queue [ 4026531993] lo reply 127.0.0.1 -> 127.0.0.1
net:netif_rx [ 4026531993] lo reply 127.0.0.1 -> 127.0.0.1
ipt_do_table [ 4026531993] lo reply 127.0.0.1 -> 127.0.0.1 filter.INPUT :ACCEPT
輸出內容主要有6列,我們做一下解釋:
* TRACEPOINT: 此時數據包在哪個內核函數中抓取到了。這是我自己加的列。原版沒有這一列。
* NETWORK NS: 此時數據包所處于的哪個名字空間中。可以把不同的名字空間想象成不同的網絡。
* INTERFACE: 表示此數據包在在哪個接口上。命令默認是ping 127.0.0.1 .所以所有數據包在回環口上。
* TYPE: 表示icmp的request和reply兩種類型的包
* ADDRESS: 數據包的流向。源地址 -> 目標地址
* IPTABLES: 數據包經過的iptables鏈,及執行的動作。此處全是ACCEPT。
額外的一些信息
* ipt_do_table:是一個內核函數。主要是在經過netfilter HOOK的時候進行調用。然后就可以從此函數中抓取,iptables的信息。
* net: * : 都是內核中的一些tracepoint。就是開放出來用來觀測的。屬于eBPF最建議使用的一種方式。
?? 這樣的展示是不是特別清晰明了?從本地發出的數據包最新經過OUTPUT鏈,而且nat比filter優先級更高。接著經過nat的POSTROUTING鏈。4026531993 是我們當前所處于的名字空間。數據包經過net_dev_queue發送出去,netif_rx收到request數據包開始進行處理,數據包進過filter.INPUT進入。收到request數據包后構造reply數據包。經過filter的OUTPUT鏈,和net_dev_queue發出。netif_rx收到數據包進過filter.INPUT進入。
?? 因為只發送了一個數據包。所以就只有一個ICMP的請求。其實回環口的數據包不夠清晰。如果不了解內核函數,也不太能分清哪里是發送,哪里是接收。
?? 我們在ping一個外部的ip地址吧。
python tracepkt.py 172.17.0.6
TRACEPOINT NETWORK NS INTERFACE TYPE ADDRESSES IPTABLES
ipt_do_table [ 0] request 172.17.2.65 -> 172.17.0.6 nat.OUTPUT :ACCEPT
ipt_do_table [ 0] request 172.17.2.65 -> 172.17.0.6 filter.OUTPUT :ACCEPT
ipt_do_table [ 4026531993] eth0 request 172.17.2.65 -> 172.17.0.6 nat.POSTROUTING :ACCEPT
net:net_dev_queue [ 4026531993] eth0 request 172.17.2.65 -> 172.17.0.6
net:napi_gro_receive_entry [ 4026531993] eth0 reply 172.17.0.6 -> 172.17.2.65
ipt_do_table [ 4026531993] eth0 reply 172.17.0.6 -> 172.17.2.65 filter.INPUT :ACCEPT
?? napi和gro都是內核層面的東西。數據包到達網卡后會觸發一個硬中斷。這個硬中斷做一個很簡單的操作。就是觸發一個軟中斷,就是napi這個東東持續接收數據包。
?? 我們看一個有趣的例子.172.17.2.65是我自己服務器的ip
python tracepkt.py 172.17.2.65
TRACEPOINT NETWORK NS INTERFACE TYPE ADDRESSES IPTABLES
ipt_do_table [ 0] request 172.17.2.65 -> 172.17.2.65 nat.OUTPUT :ACCEPT
ipt_do_table [ 0] request 172.17.2.65 -> 172.17.2.65 filter.OUTPUT :ACCEPT
ipt_do_table [ 4026531993] lo request 172.17.2.65 -> 172.17.2.65 nat.POSTROUTING :ACCEPT
net:net_dev_queue [ 4026531993] lo request 172.17.2.65 -> 172.17.2.65
net:netif_rx [ 4026531993] lo request 172.17.2.65 -> 172.17.2.65
ipt_do_table [ 4026531993] lo request 172.17.2.65 -> 172.17.2.65 filter.INPUT :DROP
?? 之前遇到過一個特別詭異的問題。就是這臺機器訪問其他機器是可以痛的。其他機器訪問這臺機器也是可以通的。就是自己訪問不了自己。抓破腦袋都想不明白。本來以為是網卡問題。然后就吧ping換成tracepkt命令。得!清晰明了。數據包在filter的INPUT鏈上被DROP了。因為之前很少使用iptables,所以沒往這方面想。所以,還是工具靠譜!然后就可以解決問題了。
iptables -D INPUT -s 172.17.2.65/32 -j DROP
?? 刪除這條規則即可。
4 跟蹤開始--容器和k8s
?? 172.18.0.2是我當前機器上運行的一個容器的ip地址。我們看看本機訪問這個容器經過了哪些路徑
TRACEPOINT NETWORK NS INTERFACE TYPE ADDRESSES IPTABLES
ipt_do_table [ 0] request 172.18.0.1 -> 172.18.0.2 nat.OUTPUT :ACCEPT
ipt_do_table [ 0] request 172.18.0.1 -> 172.18.0.2 filter.OUTPUT :ACCEPT
ipt_do_table [ 4026531993] docker0 request 172.18.0.1 -> 172.18.0.2 nat.POSTROUTING :ACCEPT
net:net_dev_queue [ 4026531993] docker0 request 172.18.0.1 -> 172.18.0.2
net:net_dev_queue [ 4026531993] veth29f2c28 request 172.18.0.1 -> 172.18.0.2
net:netif_rx [ 4026532328] eth0 request 172.18.0.1 -> 172.18.0.2
net:net_dev_queue [ 4026532328] eth0 reply 172.18.0.2 -> 172.18.0.1
net:netif_rx [ 4026531993] veth29f2c28 reply 172.18.0.2 -> 172.18.0.1
net:netif_receive_skb_entry [ 4026531993] docker0 reply 172.18.0.2 -> 172.18.0.1
ipt_do_table [ 4026531993] docker0 reply 172.18.0.2 -> 172.18.0.1 filter.INPUT :ACCEPT
?? 此處主要看名字空間的變化。4026531993是機器的名字空間。4026532328是容器的名字空間。所以eth0是容器內部的接口名。docker0是進入docker網絡的網管。veth29f2c28是在此服務器上的虛擬設備接口。類似網線的一端,另一端就是容器內的eth0.兩個docker0不是說經過docker0兩次。而只是在不同的內核函數中抓到的包的信息。注意前面的TRACEPOINT。
?? 上面是從機器訪問容器的路徑。如果的容器之間的請求呢?如果重新構造一個容器內部的BCC環境實在是復雜,對于這種我們。我們有其他思路。容器的網絡其實只是在某個namespace罷了。那我們是否可以進入某個容器的namespace內呢?還真有。
docker ps -qa
4cee6b4d5215
78cd2c04cff2
# 78cd2c04cff2是其中一個容器,ip是172.18.0.3
# 4cee6b4d5215的ip是172.18.0.2.
# 我們進入78cd2c04cff2
nsenter -n --target `docker inspect -f {{.State.Pid}} 4cee6b4d5215`
# 此時看網卡名已經變了
ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
14: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:12:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.18.0.3/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
?? 進入容器的名字空間后。我們就可以在此名字空間下,執行服務上命令了。是不是很方便!
python tracepkt.py 172.18.0.2
TRACEPOINT NETWORK NS INTERFACE TYPE ADDRESSES IPTABLES
net:net_dev_queue [ 4026532398] eth0 request 172.18.0.3 -> 172.18.0.2
net:netif_rx [ 4026531993] vethc3163ed request 172.18.0.3 -> 172.18.0.2
ipt_do_table [ 4026531993] docker0 request 172.18.0.3 -> 172.18.0.2 nat.PREROUTING :ACCEPT
ipt_do_table [ 4026531993] veth29f2c28 request 172.18.0.3 -> 172.18.0.2 filter.FORWARD :ACCEPT
ipt_do_table [ 4026531993] veth29f2c28 request 172.18.0.3 -> 172.18.0.2 nat.POSTROUTING :ACCEPT
net:net_dev_queue [ 4026531993] veth29f2c28 request 172.18.0.3 -> 172.18.0.2
net:netif_rx [ 4026532328] eth0 request 172.18.0.3 -> 172.18.0.2
net:net_dev_queue [ 4026532328] eth0 reply 172.18.0.2 -> 172.18.0.3
net:netif_rx [ 4026531993] veth29f2c28 reply 172.18.0.2 -> 172.18.0.3
ipt_do_table [ 4026531993] vethc3163ed reply 172.18.0.2 -> 172.18.0.3 filter.FORWARD :ACCEPT
net:net_dev_queue [ 4026531993] vethc3163ed reply 172.18.0.2 -> 172.18.0.3
net:netif_rx [ 4026532398] eth0 reply 172.18.0.2 -> 172.18.0.3
?? 原來容器之間一次簡單的請求這么復雜。經過了三個名字空間,五個網卡。注意兩個不同名字空間下的eth0,是不同名字空間下的網卡。此處也可以對比從服務器訪問和容器間訪問。經過的netfilter HOOK是不同的。
?? 接下來就是最終的k8s環境的跟蹤了。此命令的局限性在于只能跟蹤當前服務器內核的各種事件。所以我們先搭建一個allinone的k8s。就是所有組建運行在一臺服務器上。k8s的環境如下。
kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
service-a-6bd6fd88b5-h6z9z 1/1 Running 0 6s 10.58.0.12 172.17.2.66 <none> <none>
service-c-6fc688b9cf-fs5hc 1/1 Running 0 18s 10.58.0.11 172.17.2.66 <none> <none>
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service-a ClusterIP 10.68.147.120 <none> 80/TCP 73s
service-c ClusterIP 10.68.104.62 <none> 80/TCP 101s
?? 這連個容器都在同一個服務器上。我們現在需要進入service-a容器,然后ping service-c的ip,
?? 我們先進入service-a容器的名字空間。
function e() {
set -eu
ns=${2-"default"}
pod=`kubectl -n $ns describe pod $1 | grep -Eo 'docker://.*$' | head -n 1 | sed 's/docker:\/\/\(.*\)$/\1/'`
pid=`docker inspect -f {{.State.Pid}} $pod`
echo "enter pod netns successfully for $ns/$1"
nsenter -n --target $pid
}
# default是namespace
e service-a-6bd6fd88b5-h6z9z default
python tracepkt.py 10.58.0.11
TRACEPOINT NETWORK NS INTERFACE TYPE ADDRESSES IPTABLES
Possibly lost 79 samples
net:net_dev_queue [ 4026532930] eth0 request 10.58.0.12 -> 10.58.0.11
net:netif_rx [ 4026531993] veth3998aa4b request 10.58.0.12 -> 10.58.0.11
ipt_do_table [ 4026531993] cni0 request 10.58.0.12 -> 10.58.0.11 raw.PREROUTING :ACCEPT
ipt_do_table [ 4026531993] cni0 request 10.58.0.12 -> 10.58.0.11 mangle.PREROUTING :ACCEPT
ipt_do_table [ 4026531993] cni0 request 10.58.0.12 -> 10.58.0.11 nat.PREROUTING :ACCEPT
ipt_do_table [ 4026531993] veth8309a74e request 10.58.0.12 -> 10.58.0.11 mangle.FORWARD :ACCEPT
ipt_do_table [ 4026531993] veth8309a74e request 10.58.0.12 -> 10.58.0.11 filter.FORWARD :ACCEPT
ipt_do_table [ 4026531993] veth8309a74e request 10.58.0.12 -> 10.58.0.11 mangle.POSTROUTING :ACCEPT
ipt_do_table [ 4026531993] veth8309a74e request 10.58.0.12 -> 10.58.0.11 nat.POSTROUTING :ACCEPT
net:net_dev_queue [ 4026531993] veth8309a74e request 10.58.0.12 -> 10.58.0.11
net:netif_rx [ 4026532857] eth0 request 10.58.0.12 -> 10.58.0.11
net:net_dev_queue [ 4026532857] eth0 reply 10.58.0.11 -> 10.58.0.12
net:netif_rx [ 4026531993] veth8309a74e reply 10.58.0.11 -> 10.58.0.12
ipt_do_table [ 4026531993] cni0 reply 10.58.0.11 -> 10.58.0.12 raw.PREROUTING :ACCEPT
ipt_do_table [ 4026531993] cni0 reply 10.58.0.11 -> 10.58.0.12 mangle.PREROUTING :ACCEPT
ipt_do_table [ 4026531993] veth3998aa4b reply 10.58.0.11 -> 10.58.0.12 mangle.FORWARD :ACCEPT
ipt_do_table [ 4026531993] veth3998aa4b reply 10.58.0.11 -> 10.58.0.12 filter.FORWARD :ACCEPT
ipt_do_table [ 4026531993] veth3998aa4b reply 10.58.0.11 -> 10.58.0.12 mangle.POSTROUTING :ACCEPT
net:net_dev_queue [ 4026531993] veth3998aa4b reply 10.58.0.11 -> 10.58.0.12
net:netif_rx [ 4026532930] eth0 reply 10.58.0.11 -> 10.58.0.12
出來的結果和docker的沒差太多。主要原因在于一些跟細節的信息沒有獲取到。
其實tracepkt還有一個另一個版本。主要來自tracepkt作者的博客。有國內大佬已經翻譯成中文了 [使用 Linux tracepoint、perf 和 eBPF 跟蹤數據包](http://arthurchiao.art/blog/trace-packet-with-tracepoint-perf-ebpf-zh/).這篇文章中實現的tracepkt沒有iptables部分,也不支持ipv6.但是可以抓取到所有icmp包。因為是持續抓取,所以得占用一個終端。然后在另一個終端執行ping命令。
# 這是抓取終端
python tracepkt.py
version [namespace id] dev icmptype pid icmpseq src dst
4 [4026531993] cni0 Echo request 12027 1 10.58.0.1 10.58.0.12 net:net_dev_queue
4 [4026531993] veth3998aa4b Echo request 12027 1 10.58.0.1 10.58.0.12 net:net_dev_queue
4 [4026532930] eth0 Echo request 12027 1 10.58.0.1 10.58.0.12 net:netif_rx
4 [4026532930] eth0 Echo Reply 12027 1 10.58.0.12 10.58.0.1 net:net_dev_queue
4 [4026531993] veth3998aa4b Echo Reply 12027 1 10.58.0.12 10.58.0.1 net:netif_rx
4 [4026531993] cni0 Echo Reply 12027 1 10.58.0.12 10.58.0.1 net:netif_receive_skb_entry
4 [4026532930] eth0 Echo request 13192 1 10.58.0.12 10.58.0.11 net:net_dev_queue
4 [4026531993] veth3998aa4b Echo request 13192 1 10.58.0.12 10.58.0.11 net:netif_rx
4 [4026531993] veth3998aa4b Echo request 13192 1 10.58.0.12 10.58.0.11 net:net_dev_queue
4 [4026532930] eth0 Echo request 13192 1 10.58.0.12 10.58.0.11 net:netif_rx
4 [4026531993] veth8309a74e Echo request 13192 1 10.58.0.12 10.58.0.11 net:net_dev_queue
4 [4026532857] eth0 Echo request 13192 1 10.58.0.12 10.58.0.11 net:netif_rx
4 [4026531993] veth409f6ead Echo request 13192 1 10.58.0.12 10.58.0.11 net:net_dev_queue
4 [4026532782] eth0 Echo request 13192 1 10.58.0.12 10.58.0.11 net:netif_rx
4 [4026531993] veth7765b5f5 Echo request 13192 1 10.58.0.12 10.58.0.11 net:net_dev_queue
4 [4026532717] eth0 Echo request 13192 1 10.58.0.12 10.58.0.11 net:netif_rx
4 [4026531993] veth22af838f Echo request 13192 1 10.58.0.12 10.58.0.11 net:net_dev_queue
4 [4026532644] eth0 Echo request 13192 1 10.58.0.12 10.58.0.11 net:netif_rx
4 [4026531993] vethf7954c62 Echo request 13192 1 10.58.0.12 10.58.0.11 net:net_dev_queue
4 [4026532577] eth0 Echo request 13192 1 10.58.0.12 10.58.0.11 net:netif_rx
4 [4026531993] vethe980d1cc Echo request 13192 1 10.58.0.12 10.58.0.11 net:net_dev_queue
4 [4026532507] eth0 Echo request 13192 1 10.58.0.12 10.58.0.11 net:netif_rx
4 [4026531993] veth7740529c Echo request 13192 1 10.58.0.12 10.58.0.11 net:net_dev_queue
4 [4026532432] eth0 Echo request 13192 1 10.58.0.12 10.58.0.11 net:netif_rx
4 [4026531993] veth2697c574 Echo request 13192 1 10.58.0.12 10.58.0.11 net:net_dev_queue
4 [4026532366] eth0 Echo request 13192 1 10.58.0.12 10.58.0.11 net:netif_rx
4 [4026532857] eth0 Echo Reply 13192 1 10.58.0.11 10.58.0.12 net:net_dev_queue
4 [4026531993] veth8309a74e Echo Reply 13192 1 10.58.0.11 10.58.0.12 net:netif_rx
4 [4026531993] veth3998aa4b Echo Reply 13192 1 10.58.0.11 10.58.0.12 net:net_dev_queue
4 [4026532930] eth0 Echo Reply 13192 1 10.58.0.11 10.58.0.12 net:netif_rx
4 [4026532930] eth0 Echo request 14251 1 10.58.0.12 10.58.0.11 net:net_dev_queue
4 [4026531993] veth3998aa4b Echo request 14251 1 10.58.0.12 10.58.0.11 net:netif_rx
4 [4026531993] veth8309a74e Echo request 14251 1 10.58.0.12 10.58.0.11 net:net_dev_queue
4 [4026532857] eth0 Echo request 14251 1 10.58.0.12 10.58.0.11 net:netif_rx
4 [4026532857] eth0 Echo Reply 14251 1 10.58.0.11 10.58.0.12 net:net_dev_queue
4 [4026531993] veth8309a74e Echo Reply 14251 1 10.58.0.11 10.58.0.12 net:netif_rx
4 [4026531993] veth3998aa4b Echo Reply 14251 1 10.58.0.11 10.58.0.12 net:net_dev_queue
4 [4026532930] eth0 Echo Reply 14251 1 10.58.0.11 10.58.0.12 net:netif_rx
# 這是命令執行終端
# 直接在機器上ping 容器ip
ping 10.58.0.12 -c1
# 進入容器a ping 容器c
function e() { set -eu; ns=${2-"default"}; pod=`kubectl -n $ns describe pod $1 | grep -Eo 'docker://.*$' | head -n 1 | sed 's/docker:\/\/\(.*\)$/\1/'`; pid=`docker inspect -f {{.State.Pid}} $pod`; echo "enter pod netns successfully for $ns/$1"; nsenter -n --target $pid; }
e service-a-6bd6fd88b5-h6z9z defaults
ping 10.58.0.11 -c1
#再ping一次容器
ping 10.58.0.11 -c1
?? 有沒有發現在容器內的第一次ping居然抓取了這么多包。為什么呢?當發送第一個ping包的時候,并不知道目標ip的mac地址是多少。所以得發送一個廣播包。死知識有清晰的展現出來了。是不是很有趣?
5.結束語
?? 其實對于跟蹤k8s而言,這個工具還有很多不完善的地址。并沒有展現成更細節的東西。比如哪里做的dnat。哪里snat。像基于iptables的kube-proxy不支持ping怎么辦?
?? 問題有許多。但就目前情況來看,都是可以解決的。希望有一天。對于k8s的跟蹤能更全面。
?? eBPF相關的文章倒是有幾篇。也有不少公司有相關實踐。奈何沒看見什么普羅大眾交流的途徑。所以小弟在此建了一個qq群。希望有這方面經驗,或者有興趣一起探討的。有一個交流的地址:
后續更新
默認的tracepkt工具只能執行當前ping命令的數據包。如果我想在對端抓取icmp包就不行了。我們只用把當前代碼進行簡單改造就可以達成目標。在內核代碼,只有確定是icmp數據包時再發送perf event,用戶層代碼就一直死循環讀取事件并輸出。其實還可以繼續改進向tcpdump看齊。但是目前已經夠用了。就先這樣把。
?? qq群:747399875