事实上,无需关注 IP 169.254.1.1 ,这就是一个莫须有的私网 IP ,仅仅是为了给 eth0 –> cali.xyz 建立路由关系。IP 报文装填上源 IP 地址和目的 IP 地址之后,交由二层协议继续装填上源 MAC 和目的 MAC 。重新整理下目标,网络包需要从容器的 eth0 发送到宿主机的 cali.xyz 。由于三层路由表明下一跳是 169.254.1.1 ,而目标是给二层协议的目的 MAC 装上 cali.xyz 的 MAC 地址。如何实现 IP -> MAC 解析呢?ARP 协议专业做这件事,唯一的问题是 cali.xyz 的 IP 不是 169.254.1.1 ,不过没关系,配置上 proxy_arp 就可以让 cali.xyz 不关心 ARP 请求的 who-is 169.254.1.1,对任何 ARP 请求都直接响应自己的 MAC 地址 ee:ee:ee:ee:ee:ee。至于所有 cali* 网卡的 MAC 地址都是 ee:ee:ee:ee:ee:ee 潜在的冲突问题?其实根本不存在,网络包到达宿主机后,该目的 MAC 地址即被卸下,准备装填一个新的下一跳继续转发。
# 宿主机侧网卡 cali.xyz $ ip addr 14: cali96417d7dcac@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 2 inet6 fe80::ecee:eeff:feee:eeee/64 scope link valid_lft forever preferred_lft forever # 宿主机侧网卡 cali.xyz 配置 $ cat /proc/sys/net/ipv4/conf/cali96417d7dcac/proxy_arp 1
# 容器侧网卡 eth0 $ ip addr eth0@if14: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 8e:f0:89:10:19:9e brd ff:ff:ff:ff:ff:ff inet 172.20.250.3/32 scope global eth0 valid_lft forever preferred_lft forever
# 容器侧路由 $ ip route default via 169.254.1.1 dev eth0 169.254.1.1 dev eth0 scope link
容器间通信
虽然容器侧发送的网络包已经达到了宿主机,但容器与容器通信尚未完成?继续跟踪达到宿主机的网络包。容器 A 向容器 B 发出的网络包已经到达了宿主机,因为未达到目的地,还需要寻找下一跳。还是走三层路由协议,宿主机侧有关于该物理主机上所有容器的路由信息,其中一条 172.20.251.4 dev calib7124528292 scope link 为这个网络包指明了下一跳的方向,走 calib7124528292 网卡,具体的二层封包 MAC 地址已经被 Calico 永久写了一条邻接条目。
跨物理主机的容器间通信与同一主机下的容器间通信并不大的区别。需要关注的就是物理主机间关于容器路由记录的同步。Calico 一般是将 Pod CIDR 划分成若干段,每台物理主机持有一个 IP 段。需要跨主机的网络包,根据目的 IP 先被被宿主机侧的路由信息路由到另一个物理主机上的宿主机,然后是宿主机将网络包路由到目的容器。
1 2 3
172.20.24.0/23 via 172.16.254.113 dev em1 proto bird 172.20.112.0/23 via 172.16.254.115 dev em1 proto bird 172.20.148.0/23 via 172.16.254.112 dev em1 proto bird