calico IPIP MODE 同节点通信

环境信息

[root@master ~]# kubectl get node -o wide 
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
master.whale.com Ready control-plane,master 137m v1.23.5 192.168.0.80 <none> CentOS Linux 7 (Core) 5.4.193-1.el7.elrepo.x86_64 docker://20.10.16
node1.whale.com Ready <none> 137m v1.23.5 192.168.0.81 <none> CentOS Linux 7 (Core) 5.4.193-1.el7.elrepo.x86_64 docker://20.10.16
node2.whale.com Ready <none> 137m v1.23.5 192.168.0.82 <none> CentOS Linux 7 (Core) 5.4.193-1.el7.elrepo.x86_64 docker://20.10.16

创建 deployment

kubectl create deployment cni-test --image=burlyluo/nettoolbox --replicas=3

确定 node1 节点上的两个 pod,用于测试

pod1 10.244.42.65
pod2 10.244.42.66

[root@master ~]# kubectl get pod -o wide 
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
cni-test-777bbd57c8-4bqtf 1/1 Running 0 80s 10.244.103.65 node2.whale.com <none> <none>
cni-test-777bbd57c8-4lnwt 1/1 Running 0 80s 10.244.42.65 node1.whale.com <none> <none>
cni-test-777bbd57c8-8zgs9 1/1 Running 0 60s 10.244.42.66 node1.whale.com <none> <none>

查看 pod1 对应 root namespace 网卡

[root@master <sub>]# kubectl exec -it cni-test-777bbd57c8-4lnwt -- ethtool -S eth0
NIC statistics:
peer_ifindex: 7
rx_queue_0_xdp_packets: 0
rx_queue_0_xdp_bytes: 0
rx_queue_0_xdp_drops: 0


[root@node1 </sub>]# ip link show | grep ^7
7: cali59330ed5cb6@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default

查看 pod1 内部路由表

注意

通过 ifconfig 查看 ip 地址,我们可以看到子网掩码为 32位,那么我们就可以确定,pod 出来是需要 走三层路由,因为 32位的掩码它不跟任何 ip 属于同一网段。
通过 route 查看路由表,我们可以确定出去的任何地址都需要经过 网关为 169.254.1.1 的这个网关。

这里暂不解释 这个地址,我们一会抓包分析,这个地址其实是 calico 给我们生成用于 Proxy-ARP功能的地址。

[root@master <sub>]# kubectl exec -it cni-test-777bbd57c8-4lnwt -- ifconfig eth0
eth0 Link encap:Ethernet HWaddr D2:2F:85:72:CC:97
inet addr:10.244.42.65 Bcast:0.0.0.0 Mask:255.255.255.255
UP BROADCAST RUNNING MULTICAST MTU:1480 Metric:1
RX packets:5 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:446 (446.0 B) TX bytes:0 (0.0 B)

[root@master </sub>]# kubectl exec -it cni-test-777bbd57c8-4lnwt -- route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 169.254.1.1 0.0.0.0 UG 0 0 0 eth0
169.254.1.1 0.0.0.0 255.255.255.255 UH 0 0 0 eth0

查看 pod2 对应 root namespace 网卡

[root@master <sub>]# kubectl exec -it cni-test-777bbd57c8-8zgs9 -- ethtool -S eth0
NIC statistics:
peer_ifindex: 8
rx_queue_0_xdp_packets: 0
rx_queue_0_xdp_bytes: 0
rx_queue_0_xdp_drops: 0

[root@node1 </sub>]# ip link show | grep ^8
8: cali072e3706f4d@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default

查看 pod2 内部路由表

内容和 pod1 基本一致

[root@master <sub>]# kubectl exec -it cni-test-777bbd57c8-8zgs9 -- ifconfig eth0
eth0 Link encap:Ethernet HWaddr 56:D8:AC:4F:02:93
inet addr:10.244.42.66 Bcast:0.0.0.0 Mask:255.255.255.255
UP BROADCAST RUNNING MULTICAST MTU:1480 Metric:1
RX packets:5 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:446 (446.0 B) TX bytes:0 (0.0 B)

[root@master </sub>]# kubectl exec -it cni-test-777bbd57c8-8zgs9 -- route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 169.254.1.1 0.0.0.0 UG 0 0 0 eth0
169.254.1.1 0.0.0.0 255.255.255.255 UH 0 0 0 eth0

查看 node1 路由表

可以看到路由表中
目的地址 10.244.42.65 对应的是 pod1 的 root namespace下的网卡 cali59330ed5cb6

目的地址 10.244.42.66 对应的是 pod2 的 root namespace下的网卡 cali072e3706f4d

[root@node1 ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.0.1 0.0.0.0 UG 100 0 0 ens33
10.244.42.64 0.0.0.0 255.255.255.192 U 0 0 0 *
10.244.42.65 0.0.0.0 255.255.255.255 UH 0 0 0 cali59330ed5cb6
10.244.42.66 0.0.0.0 255.255.255.255 UH 0 0 0 cali072e3706f4d
10.244.103.64 192.168.0.82 255.255.255.192 UG 0 0 0 tunl0
10.244.152.128 192.168.0.80 255.255.255.192 UG 0 0 0 tunl0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.0.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33

同节点通信数据流向图

calico IPIP 同节点通信_calico

抓包验证

我们在 node 1 上抓取两个 calico 的root namespace 下的网卡
对 pod 的 eth0 抓包

pod1 ping pod2

kubectl exec -it cni-test-777bbd57c8-4lnwt -- ping 10.244.42.66

pod1 eth0

kubectl exec -it cni-test-777bbd57c8-4lnwt -- tcpdump -pne -i eth0 -w pod1.cap

calico IPIP 同节点通信_calico_02

pod1 cali59330ed5cb6

tcpdump -pne -i cali59330ed5cb6 -w pod1-cali.cap

calico IPIP 同节点通信_ipip_03

pod2 eth0

kubectl exec -it cni-test-777bbd57c8-8zgs9 -- tcpdump -pne -i eth0 -w pod2.cap

calico IPIP 同节点通信_ipip_04

pod2 cali072e3706f4d

tcpdump -pne -i cali072e3706f4d -w pod2-cali.cap

calico IPIP 同节点通信_calico_05

通过抓包,我们验证了如下图所示的数据流向图

calico IPIP 同节点通信_ipip_06

问题一:为什么 pod 内部路由网关是 169.254.1.1

calico IPIP 同节点通信_calico_07
在 Calico 中,每个主机对它自身上的 wordload 都充当了一个网关路由器,因为 32 位 的 掩码地址,使之只能成为一个主机地址,通过三层路由的方式,而 169.254.1.1 正是 作为 calico 路由器的地址,同时也节省了 IP地址,避免配置地址的负担。

问题二:为什么 pod 对应 root namespace下的 cali 网卡没有IP地址,但 MAC 地址是 ee:ee:ee:ee:ee:ee,全 e 会不会有影响

calico IPIP 同节点通信_calico_08
我们发现,网关地址 是 169.254.1.1 对应 root namespace 下的cali 网卡并没有这个IP地址,而是有个全 e 的 MAC 地址。
这是因为 calico 在接口上配置了 proxy_arp 的标志,这使主机的行为类似于网关,响应 169.254.1.1 的 ARP 请求,但无需实际将 IP 地址分配给接口。

而且这个 MAC地址时calico 自己生成的,用于使用 calico 点对点的路由接口,流量通过网络层,而不会到达数据链路层,因为从不使用这个 MAC 地址,因为所有的 Cali 网卡都可以使用相同的 MAC 地址,这也正契合了 proxy_arp 的特性,不关心网关接口的 MAC 地址。

Proxy-Arp

我们引用 华为的一篇文档来解释什么是 proxy-arp。
​proxy-arp​

简单来说,就是 同一网段的子网,但是并不属于同一物理网络的情况。
这时可以通过修改网络内主机的路由信息,使发往其它子网的数据先发送到连接不同子网的网关设备上,再由网关设备转发此数据报文。但是这种解决方案需要配置子网中所有主机的路由,并不便于管理和维护。
在网关上部署路由式Proxy ARP功能,可以有效解决子网划分带来的管理和维护方面的问题。路由式Proxy ARP的功能可以使IP地址属于同一网段却不属于同一物理网络的主机间能够相互通信,并且主机上不需要配置缺省网关,便于管理和维护。
calico IPIP 同节点通信_calico_09