root@suse-o11y-01:~# kubectl exec -it busybox-96f94bdb4-m4v56 -- ip -4 a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 2: eth0@if68: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue qlen 1000 inet 10.42.42.253/32 scope global eth0 valid_lft forever preferred_lft forever
查看 Pod 的 ARP 缓存,可以发现网关 169.254.1.1 的 MAC 地址是 ee:ee:ee:ee:ee:ee:
1 2 3
root@suse-o11y-01:~# kubectl exec -it busybox-96f94bdb4-m4v56 -- ip neigh 172.16.16.110 dev eth0 lladdr ee:ee:ee:ee:ee:ee used 0/0/0 probes 0 STALE 169.254.1.1 dev eth0 lladdr ee:ee:ee:ee:ee:ee used 0/0/0 probes 4 STALE
容器网络都是基于 Linux veth-pair 技术实现 Network namespace 间通信,所以在 Pod 和主机节点上使用一对 veth-pair 连接。
在 Pod 内,eth0@xxx 就是 veth-pair 的其中一端,可以通过下面的命令查看:
1 2 3 4 5 6 7
root@suse-o11y-01:~# kubectl exec -it busybox-96f94bdb4-m4v56 -- ip addr show dev eth0 2: eth0@if68: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1450 qdisc noqueue qlen 1000 link/ether ea:44:ad:6c:8c:ea brd ff:ff:ff:ff:ff:ff inet 10.42.42.253/32 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::e844:adff:fe6c:8cea/64 scope link valid_lft forever preferred_lft forever
另一端则是在节点上,节点上的 calixxx@yyy 网卡就是每个 Pod veth-pair 的对端网卡,每 veth-pair 的接口均有相同的 inteface index,如上 Pod 的 interface index 是 68,那么在节点上就可以通过这个 interface index 查找到对端网卡:
1 2 3
root@suse-o11y-01:~# ip link show | egrep '^68:' -A 1 68: cali1d5453727e1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP mode DEFAULT group default qlen 1000 link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netns cni-6b6716bd-6825-e646-5d0a-c571b302d90f
所以 cali1d5453727e1 就是 Pod busybox-96f94bdb4-m4v56 在节点上的对端网卡,它与 Pod 内的 eth0 组成了一个 veth-pair,两张网卡也在同一个广播域,当 eth0 向 169.254.1.1 发出 ARP 请求的时候,就会被 cali1d5453727e1 接收,但从上面的命令查看发现,cali1d5453727e1 也没有配置 IP 169.254.1.1,但依旧能回复 ARP 请求。
这是因为将 /proc/sys/net/ipv4/conf/<interface-dev>/proxy_arp 设置为 1 的时候,该网卡就会看起来像一个网关,会响应所有的 ARP 请求,并将自己的 MAC 地址告诉客户端。
通过命令查看,可以发现 cali1d5453727e1 的 proxy_arp 为 1,说明 cali1d5453727e1 收到 Pod 发出的 ARP 请求的时候,会将自己的 MAC 地址返回给 Pod:
在对端宿主机查看,也可以看到 vxlan.calico 网卡的 IP 就是 10.42.83.192:
1 2 3 4 5
root@suse-o11y-02:~# ip -4 a ... 5: vxlan.calico: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default qlen 1000 inet 10.42.83.192/32 scope global vxlan.calico valid_lft forever preferred_lft forever
内层源/目 MAC:因为 Pod 流量会先到达 vxlan.calico 进行封装,所以源 MAC 就是 vxlan.calico 网卡的 MAC 地址,目的 MAC 同理,不同的是目的 MAC 不是通过 ARP 获取的,而是 Calico 在每个节点预设好的,由 Calico 负责维护,没有过期时间。
1 2 3
root@suse-o11y-01:~# ip n | grep vxlan 10.42.83.192 dev vxlan.calico lladdr 66:8b:ff:1c:ec:cf PERMANENT 10.42.74.128 dev vxlan.calico lladdr 66:7a:20:ab:88:b4 PERMANENT
外层 IP:为源/目节点的物理网卡 IP 地址,源 IP 为当前节点物理网卡 IP 地址,目的 IP 通过 FDB(Forwarding Database 转发数据库)表查询,:
1 2 3 4
root@suse-o11y-01:~# bridge fdb show | grep vxlan.calico # 当有了要发给 66:8b:ff:1c:ec:cf 这个 MAC 地址的包,会进行 VXLAN 封装,然后发送给这个 VTEP IP 172.16.16.111 66:8b:ff:1c:ec:cf dev vxlan.calico dst 172.16.16.111 self permanent 66:7a:20:ab:88:b4 dev vxlan.calico dst 172.16.16.112 self permanent
外层 MAC:源/目节点物理网卡对应的 MAC 地址。
经过封装后的报文的源 IP 地址、目的 IP 地址都是主机的物理机网卡地址,所以只要两个主机节点能够通信,报文即可传输到对端节点。
IP-in-IP 就是把一个 IP 数据包又套在一个 IP 包里,即把 IP 层封装到 IP 层的一个 tunnel 。它的作用其实基本上就相当于一个基于 IP 层的网桥。一般来说,普通的网桥是基于 MAC 层的,根本不需要 IP,而 IPIP 则是通过两端的路由做一个 tunnel,把两个本来不通的网络通过点对点连接起来。
IPIP 报文格式如下:
外层 IP 头:在外层新增的 IP 头,源目地址是两端物理节点网卡的 IP。
内层 IP 头:源目地址是两端 Pod 的 IP。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
0 3 7 15 23 31 +---------------------------------------------------------------+ |Version| IHL |Type of Service| Total Length | +---------------------------------------------------------------+ | Identification | Flags | Fragment Offset | +---------------------------------------------------------------+ | Time to Live | Protocol | Header Checksum | +---------------------------------------------------------------+ | Source Address | +---------------------------------------------------------------+ | Destination Address | +---------------------------------------------------------------+ | Options | Options | +---------------------------------------------------------------+
Calico 会根据配置为每个节点上分配一个子网段,每个节点上的 Calico Pod IP 都在该子网里,所以说每个节点上都会存在到其他节点的子网路由。如果集群中有 n 个节点,那么每个节点都会有 n-1 个类似上述的路由。
通过上述路由可知,所有目标地址在 10.42.83.192/26 范围内的包,都将通过 tunl0 接口发送,网关 IP 为 172.16.16.111,即对端主机物理网卡的 IP,可以发现网关 IP 的表现与 vxlan.calico 不同。
IPIP 模式下同样也会在主机上创建一个 IPIP 设备,默认是 tunl0,tunl0 网卡 IP 地址与本机 Pod IP 属于同一个网段:
1 2 3 4 5
root@suse-o11y-01:~# ip -4 a ... 73: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000 inet 10.42.42.201/32 scope global tunl0 valid_lft forever preferred_lft forever
tunl0 是 Linux 支持的隧道设备接口,当有这个接口时,出这个主机的 IP 包就会本封装成 IPIP 报文。
报文根据路由到达 tunl0 网卡时,原始数据包会作为 IPIP 数据包的负载,同时设置外层 IP 报文,此时只要两个主机之间能够通信,报文就可以正常传输到对端节点。
root@docker-test-0:~# ip -4 a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 2: ens34: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 altname enp2s2 inet 172.16.16.142/24 brd 172.16.16.255 scope global ens34 valid_lft forever preferred_lft forever root@docker-test-0:~# ip a | grep -E "tun|vxlan"
root@docker-test-0:~# calicoctl node status Calico process is running.
IPv4 BGP status +---------------+-------------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +---------------+-------------------+-------+----------+-------------+ | 172.16.16.143 | node-to-node mesh | up | 08:34:36 | Established | +---------------+-------------------+-------+----------+-------------+
IPv6 BGP status No IPv6 peers found.
在节点检查路由,会发现容器网络跨节点的路由,下一跳是对端节点的物理网卡:
检查跨节点通信:
1 2 3 4 5 6 7 8 9 10 11 12 13
root@docker-test-0:~# kubectl get pod -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES busybox-f96f87d5d-5twmn 1/1 Running 0 20m 10.42.5.212 docker-test-1 <none> <none> busybox-f96f87d5d-mgskw 1/1 Running 0 20m 10.42.22.99 docker-test-0 <none> <none> root@docker-test-0:~# kubectl exec -it busybox-f96f87d5d-mgskw -- ping -c 3 10.42.5.212 PING 10.42.5.212 (10.42.5.212): 56 data bytes 64 bytes from 10.42.5.212: seq=0 ttl=62 time=0.436 ms 64 bytes from 10.42.5.212: seq=1 ttl=62 time=0.349 ms 64 bytes from 10.42.5.212: seq=2 ttl=62 time=0.362 ms
--- 10.42.5.212 ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max = 0.349/0.382/0.436 ms