AWS上的Cilium网络拓扑和流量路径
Table of Contents1准备1.1测试环境1.2容器网和nsenter1.3验证基本连接2个出口:Pod->主机-> VPC网络2.1容器内部网络1.2 Veth对连接到主机1.3出口BPF代码1.4主机路由表2入口2.1主机路由表2.2入口BPF代码2.3集装箱收货3小结参考文献相关阅读发表于2019-10-26 |最后更新2019-10-26这篇文章探讨了在AWS上由C
Table of Contents
发表于2019-10-26 | 最后更新2019-10-26
这篇文章探讨了在AWS上由Cilium驱动的K8S集群中两个跨主机Pod之间的网络拓扑和流量路径。我们将使用普通的Linux命令来完成此任务。在这篇文章的结尾,我们将获得如下图片:
1准备
1.1测试环境
我们有两个K8S主机:
- 节点1:
10.5.2.48
- 节点2:
10.5.2.58
和两个豆荚:
- 节点1
10.5.2.11
上的pod1 10.5.2.22
node2上的pod2
节点和Pod在同一VPC中,带有VPC网关10.5.2.1
。
注意:出于安全原因以及易于理解,我用伪造的IP / MAC地址代替了真实的IP / MAC地址。这不应破坏此职位的意义。
1.2容器网和 nsenter
我们将使用nsenter
工具工具在主机上容器的网络名称空间中执行命令,格式为:
$ nsenter -t <pid> -n <command>
哪里,
-t <pid>
:目标过程<pid>
-n
:输入网络名称空间<command>
:执行命令
这等效于docker exec <container> <command>
,但比后者更灵活,因为通常缺少网络工具或容器内部没有特权,因此在主机上执行命令没有此限制。
获取容器进程ID:
root@node1 # docker inspect 04d740a33726 | grep Pid
"Pid": 75869,
75869
就是pid
我们想要的。
1.3验证基本连接
从pod1 ping pod2,确保其可访问:
root@node1 # nsenter -t 75869 -n ping 10.5.2.22 -c 2
64 bytes from 10.5.2.22: icmp_seq=1 ttl=61 time=0.248 ms
64 bytes from 10.5.2.22: icmp_seq=2 ttl=61 time=0.208 ms
好!接下来,我们将探索这些数据包的确切路径,即网络设备,路由表,arp表,BPF挂钩。
2个出口:Pod->主机-> VPC网络
2.1容器内部网络
从pod1开始我们的旅程。检查pod1内的网络设备:
root@node1 # nsenter -t 75869 -n ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
...
42: eth0@if43: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP
link/ether ee:14:d3:9a:62:42 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.5.2.11/32 brd 10.5.2.11 scope global eth0
可以看出,它具有一个loopbak接口lo
和一个eth0
IP地址为IP的网络接口10.5.2.11
。
请注意42: eth0@if43
,此特殊符号表示eth0
具有 ifindex
编号42
,并且设备带有ifindex
42
并43
组成veth对。这ifindex
在主机(在本例中为node1)中是唯一的,稍后我们将再次看到。
接下来,检查容器内的路由表:
root@node1 # nsenter -t 75869 -n route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.5.2.191 0.0.0.0 UG 0 0 0 eth0
10.5.1.191 0.0.0.0 255.255.255.255 UH 0 0 0 eth0
可以看出,该Pod的网关为10.5.1.191
,所有出口流量都将转发到该网关。在主机上查找网关:
root@node1 # ip a | grep 10.5.2.191 -B 2
31: cilium_host@cilium_net: <...> mtu 9001 ... qlen 1000
link/ether 0a:ee:d6:5f:6c:32 brd ff:ff:ff:ff:ff:ff
inet 10.5.2.191/32 scope link cilium_host
我们可以看到容器网关是由device持有的cilium_host
。其实 cilium_host
并cilium_net
撰写另一VETH对,他们都对主机的网络空间,并将于纤毛代理开始创建。
接下来,检查容器内的ARP表:
root@node1 # nsenter -t 75869 -n arp -n
Address HWtype HWaddress Flags Mask Iface
10.5.2.191 ether 86:05:d4:99:a9:f5 C eth0
10.5.2.48 ether 86:05:d4:99:a9:f5 C eth0
可以看出,容器的网关和主机IP都指向相同的MAC地址86:05:d4:99:a9:f5
。让我们进一步确定哪个设备拥有该地址。
可以看出,容器的网关和主机IP都指向相同的MAC地址86:05:d4:99:a9:f5
。让我们进一步确定哪个设备拥有该地址。
1.2 Veth对连接到主机
root@node1 # ip link | grep 86:05:d4:99:a9:f5 -B 1
43: lxc050ba70e11a8@if42: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc ... qlen 1000
link/ether 86:05:d4:99:a9:f5 brd ff:ff:ff:ff:ff:ff link-netnsid 1
而已!设备lxc050ba70e11a8
持有它。请注意43: lxc050ba70e11a8@if42
符号,并回想一下容器的eth0
实际持有ifindex=42
,因此我们现在确保:
- pod1通过veth对(
if42 <--> if43
或名称表示eth0 <--> lxc050ba70e11a8
)连接到主机 - Pod中的默认网关指向
cilium_host
主机上的设备 - Pod生成流量的下一个L3跃点是
cilium_host
- Pod产生流量的下一个L2跳是第ve对(
lxc050ba70e11a8
)的主机端
这正是Pod出口流量从容器流向主机的方式。
1.3出口BPF代码
Cilium的强大功能之一是动态流量操纵。它通过利用BPF来实现。关于此主题的详细说明不在本文的范围之内,如果您有兴趣的话,请参阅官方文档《BPF和XDP参考指南》( 如果您可以阅读中文,请参阅我的 翻译)。
Cilium使用tc
BPF过滤容器的进出流量。让我们看一下出口部分:
root@node1:~ # tc filter show dev lxc050ba70e11a8 egress
filter protocol all pref 1 bpf
filter protocol all pref 1 bpf handle 0x1 bpf_lxc.o:[from-container] direct-action not_in_hw tag db59e2ea8177ded3
注意:如果以上命令的输出未显示该
bpf_lxc.o:[from-container] direct-action not_in_hw tag db59e2ea8177ded3
信息,则可能是您的iproute2
软件包太旧了,请尝试升级到新版本。
列出此主机(节点1)上所有已加载的BPF程序:
root@node1:~ # bpftool prog
288: sched_cls tag a390cb0eda39ede9
loaded_at Oct 22/10:48 uid 0
xlated 808B jited 637B memlock 4096B map_ids 182,169
...
294: sched_cls tag 596c1921e0319e72
loaded_at Oct 22/10:48 uid 0
xlated 176B jited 157B memlock 4096B
297: sched_cls tag db59e2ea8177ded3
loaded_at Oct 22/10:48 uid 0
xlated 19144B jited 11706B memlock 20480B map_ids 285,169,171,286,287,172,277,183,283,173,179,167,180
让我们进一步了解BPF代码/规则是什么样的。转储解释的BPF代码:
root@node1:~ # bpftool prog dump xlated id 297
0: (bf) r6 = r1
1: (b7) r7 = 0
2: (63) *(u32 *)(r6 +60) = r7
3: (63) *(u32 *)(r6 +56) = r7
4: (63) *(u32 *)(r6 +52) = r7
5: (63) *(u32 *)(r6 +48) = r7
6: (63) *(u32 *)(r6 +64) = r7
7: (18) r2 = 0xffffff5a
9: (79) r1 = *(u64 *)(r6 +80)
10: (79) r8 = *(u64 *)(r6 +216)
...
转储JITed BPF代码:
root@node1:~ # bpftool prog dump jited id 297
0: push %rbp
1: mov %rsp,%rbp
4: sub $0x228,%rsp
b: sub $0x28,%rbp
f: mov %rbx,0x0(%rbp)
13: mov %r13,0x8(%rbp)
17: mov %r14,0x10(%rbp)
1b: mov %r15,0x18(%rbp)
1f: xor %eax,%eax
21: mov %rax,0x20(%rbp)
...
好的,无需进一步挖掘。如果出口流量没有被BPF代码/规则丢弃,它将到达主机,主机路由设施将对其进行处理。
1.4主机路由表
查看主机路由表:
root@node1:~ # ip rule list
9: from all fwmark 0x200/0xf00 lookup 2004
100: from all lookup local
32766: from all lookup main
32767: from all lookup default
我们看到,有4个路由表:2004
,local
,main
,default
。检查每个内容:
root@node1:~ # ip route show table 2004
local default dev lo scope host
root@node1 $ ip route show table main
default via 10.5.2.1 dev eth0
10.5.2.0/24 dev eth0 proto kernel scope link src 10.5.2.48
来自Pod1的出口流量将到达main
桌面。
注意:更具体地说,Pod IP是从ENI分配的,每个ENI都有自己的路由表以用于出口流量。在我的情况下,Pod1的IP来自节点1的默认ENI(eth0),因此流量将到达
main
表中。如果您有多个ENI,则此处的路由应该有所不同。
该main
路由表也是节点1的默认路由表(不要”由误导default
表上方,它只是一个名称表default
,而不是主机的默认表)。
node1的默认网关为10.5.2.1
(VPC网关)。因此,pod1的出口流量最终将被发送到VPC网关。
这样就完成了我们的交通旅程的出口部分。
2入口
如果VPC网络将流量正确路由到Node2(供应商的责任),则这些数据包将到达Node2的相应ENI。让我们来看看如何处理这些数据包。
2.1主机路由表
root@node2:~ # ip rule list
9: from all fwmark 0x200/0xf00 lookup 2004
100: from all lookup local
32766: from all lookup main
32767: from all lookup default
root@node2:~ # ip route show table 2004
local default dev lo scope host
node2 $ ip route show table main
default via 10.5.1.1 dev eth0
10.5.2.22 dev lxcd86fc95bf974 scope link
...
可以看出,Pod2有一条专用的路由:
10.5.2.22 dev lxcd86fc95bf974 scope link
这意味着所有发10.5.2.22
往的流量都将转发到 lxcd86fc95bf974
。
2.2入口BPF代码
Cilium将lxcxx
为其创建的每个设备注入入口BPF规则。让我们检查一下这个:
root@node2:~ # tc filter show dev lxcd86fc95bf974 ingress
filter protocol all pref 1 bpf
filter protocol all pref 1 bpf handle 0x1 bpf_lxc.o:[from-container] direct-action not_in_hw tag c17fab4b3f874a54
如果您对确切的BPF代码感兴趣,那么以下步骤与我们之前的出口部分大致相同:
root@node2:~ # bpftool prog
156: sched_cls tag a390cb0eda39ede9
loaded_at Oct 22/10:59 uid 0
xlated 808B jited 637B memlock 4096B map_ids 46,33
...
165: sched_cls tag c17fab4b3f874a54
loaded_at Oct 22/10:59 uid 0
xlated 19144B jited 11706B memlock 20480B map_ids 155,33,35,156,157,36,147,47,153,37,43,31,44
root@node2:~ # bpftool prog dump xlated id 165 | head -n 10
0: (bf) r6 = r1
1: (b7) r7 = 0
2: (63) *(u32 *)(r6 +60) = r7
3: (63) *(u32 *)(r6 +56) = r7
4: (63) *(u32 *)(r6 +52) = r7
5: (63) *(u32 *)(r6 +48) = r7
6: (63) *(u32 *)(r6 +64) = r7
7: (18) r2 = 0xffffff5a
9: (79) r1 = *(u64 *)(r6 +80)
10: (79) r8 = *(u64 *)(r6 +216)
2.3集装箱收货
如果流量没有被Cilium网络策略规则(入口BPF)丢弃,则数据包将通过主机端的veth对,并最终到达 eth0
内部容器-我们旅程的最终目的地。
现在在这里重新描述全局数据流图:
3小结
这篇文章探讨了AWS上由Cilium驱动的K8S集群中两个Pod之间的主机间流量的网络拓扑和数据流。我们使用了常见的Linux命令行工具来完成此任务。希望对您有帮助!
参考文献
相关阅读
《深入理解 Cilium 的 eBPF(XDP)收发包路径:数据包在Linux网络协议栈中的路径》
《eBPF.io eBPF文档:扩展的数据包过滤器(BPF)》
《介绍Calico eBPF数据平面:Linux内核网络、安全性和跟踪(Kubernetes、kube-proxy)》
《Linux eBPF和XDP高速处理数据包;使用EBPF编写XDP网络过滤器;高性能ACL》
《Understanding (and Troubleshooting) the eBPF Datapath in Cilium》
更多推荐
所有评论(0)