06月10, 2021

Kubernetes CNI系列(2)

继续上一次的内容,讲解Calico的工作原理。

Calico的工作原理

Calico的默认组网方案是一个纯三层方案,所有的包以ip跳转的方式传达。首先看一下Calico所包含的主要组件:

  • Felix: Calico agent,跑在每台需要运行 workload 的节点上,主要负责配置路由及 ACLs 等信息来确保 endpoint 的连通状态;
  • etcd: 分布式键值存储,主要负责网络元数据一致性,确保 Calico 网络状态的准确性;
  • BGPClient(BIRD): 主要负责把 Felix 写入 kernel 的路由信息分发到当前 Calico 网络,确保 workload 间的通信的有效性;
  • BGP Route Reflector(BIRD): 大规模部署时使用,摒弃所有节点互联的 mesh 模式,通过一个或者多个BGP Route Reflector来完成集中式的路由分发; [1]

从组件的功能可以看出,在Calico组织出来的网络模型下,任意两个Pod都以数据包跳转的方式完成通信,而无需重新封包解包。且这里无论是本机还是跨机器访问,都是以路由表跳转的方式完成的。我们来进一步看,Calico到底是怎么做到的。

我们先考虑第一个问题,同机Pod之间如何实现路由。

在Flannel的方案下,同机不同Pod之间直接通过cni0网桥通信即可。Calico中看起来并没有这样的网桥设计。我们先看Calico为容器配置了什么样的网络。[2]

$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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
3: eth0@if771: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1440 qdisc noqueue state UP
    link/ether 66:fb:34:db:c9:b4 brd ff:ff:ff:ff:ff:ff
    inet 172.17.8.2/32 scope global eth0
       valid_lft forever preferred_lft forever

那么,按理来说,容器内流量访问容器外时,应该都会走eth0网卡,默认路由地址应该是走eth0。但是,我们看一下Calico为容器配置了一个什么样的路由规则:

$ ip route
default via 169.254.1.1 dev eth0
169.254.1.1 dev eth0 scope link

嘿嘿,有意思吧,默认网关地址是一个不存在于容器里的地址。这个地址是一个B类地址的保留地址,是不可能在集群里找到的。当一个数据包的目的地址不是本机时,就会查询路由表,从路由表中查到网关后,它首先会通过 ARP 获得网关的 MAC 地址,然后在发出的网络数据包中将目标 MAC 改为网关的 MAC,而网关的 IP 地址不会出现在任何网络包头中。也就是说,没有人在乎这个 IP 地址究竟是什么,只要能找到对应的 MAC 地址,能响应 ARP 就行了。那么,当容器开始通过eth0网卡发送ARP包的时候,首先对端的cali设备会收到请求,但是这个网卡是没有ip地址的,正常来说,也不可能会回应ARP报文。

$ ip addr
...
771: calicba2f87f6bb@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 14
    inet6 fe80::ecee:eeff:feee:eeee/64 scope link
       valid_lft forever preferred_lft forever
...

但是,这张网卡开启了ARP代理功能,这是一种合法的MAC欺诈技术,一般用于多子网通信。可以看引文3和引文4获取更加详细的解释。简单来说,开启了ARP代理功能的网卡在收到非本网段的ARP请求时,会返回自身的MAC地址,之后收到ARP回应的设备在发送包时,将由该网卡根据自身所在的协议栈的路由表来决定下一跳的地址。这里,对端设备将自身的MAC地址返回给了容器。

$ ip neigh
169.254.1.1 dev eth0 lladdr ee:ee:ee:ee:ee:ee REACHABLE

那么容器就会把包发给veth设备对,进而让其来根据路由表决定下一跳的地址。如果不开启ARP代理,那么veth收到这个包会直接丢掉。这实际上是把主机当成了一个网关设备。读到这你可能会疑惑了,为什么要这么做。首先,因为没有网桥,那么当包从容器内传到主机空间的设备对对端的时候,没有网桥设备来处理包的转发,就需要veth自己根据路由表来决定下一跳的地址,因此需要开启ARP代理。那这么做的好处又是什么呢?这个问题是Calico官方常见问题的第三问[5]:

Why does my container have a route to 169.254.1.1?

In a Calico network, each host acts as a gateway router for the workloads that it hosts. In container deployments, Calico uses 169.254.1.1 as the address for the Calico router. By using a link-local address, Calico saves precious IP addresses and avoids burdening the user with configuring a suitable address.

While the routing table may look a little odd to someone who is used to configuring LAN networking, using explicit routes rather than subnet-local gateways is fairly common in WAN networking.

简单理解一下就是,这样所有容器里不需要去根据容器IP去特殊配置路由规则了,直接使用169.254.1.1这一条默认规则就行了。

到这里你也就能理解为什么说Calico是一个纯三层方案了。在Flannel里,本地Pod之间的通信是通过网桥设备完成的,这是一个二层设备,记录的都是Mac地址。而Calico里,本机Pod之间的通信是通过路由表的ip实现的跳转,Mac地址完全用不上。Calico也提供了解释[5]:

In some setups the kernel is unable to generate a persistent MAC address and so Calico assigns a MAC address itself. Since Calico uses point-to-point routed interfaces, traffic does not reach the data link layer so the MAC Address is never used and can therefore be the same for all the cali* interfaces.

而当包到达cali设备上,开始在路由表中寻址的时候,如果是本机Pod,那么路由表中会有相关Pod地址与对应的cali设备信息,直接发送即可。如果不是本机Pod,Calico提供了两种方式实现跨节点通信[1][6]。(6这篇引文前面讲的有点问题,只要看它讲ipip那部分就行了)

  • IPIP 从字面来理解,就是把一个IP数据包又套在一个IP包里,即把 IP 层封装到 IP 层的一个 tunnel,看起来似乎是浪费,实则不然。它的作用其实基本上就相当于一个基于IP层的网桥!一般来说,普通的网桥是基于mac层的,根本不需 IP,而这个 ipip 则是通过两端的路由做一个 tunnel,把两个本来不通的网络通过点对点连接起来。ipip 的源代码在内核 net/ipv4/ipip.c 中可以找到。

  • BGP 边界网关协议(Border Gateway Protocol, BGP)是互联网上一个核心的去中心化自治路由协议。它通过维护IP路由表或‘前缀’表来实现自治系统(AS)之间的可达性,属于矢量路由协议。BGP不使用传统的内部网关协议(IGP)的指标,而使用基于路径、网络策略或规则集来决定路由。因此,它更适合被称为矢量性协议,而不是路由协议。BGP,通俗的讲就是讲接入到机房的多条线路(如电信、联通、移动等)融合为一体,实现多线单IP,BGP 机房的优点:服务器只需要设置一个IP地址,最佳访问路由是由网络上的骨干路由器根据路由跳数与其它技术指标来确定的,不会占用服务器的任何系统

引用

本文链接:https://blog.magichc7.com/post/Kubernetes-cni-series-2.html

-- EOF --

相关评论