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
网友评论