容器网络 - 三层网络解决方案

容器网络 - 三层网络解决方案

前面两篇了解了kubernetes容器网络和CNI插件的主要工作原理。除了这种模式外,还有一种纯三层的网络方案,如Flannel的host-gw和Calico项目。

先看一下flannel的host-gw 示意图:

image-20230629173437200

你在主机上用ip route可以看到一个规则:

1
10.244.1.0/24 via 10.168.0.3 dev eth0

这个规则的含义就是,ip地址属于10.244.1.0/24网段的ip包,应该通过本机的eth0设备发出去(dev eth0),并且它的下一跳地址是10.168.0.3(via 10.168.0.3)。

这其实也就可以看到了,host-gw模式的工作原理,其实就是将每个Flannel子网(Flannel Subnet ,比如10.244.1.0/24)的”下一跳”,设置成了该子网对应的宿主机的IP地址。 也就是这台主机,充当这条容器通信路径的网关。这正是host-gw的含义。这种好处就是不需要再有额外的封包和解包操作。实际测试host-gw的性能损耗大概是10%, vxlan隧道机制的网络方案,性能损失都在20%~30%左右。

在容器网络这样的三层网络方案中,有一个跟Flannel host-gw类似的解决方案,就是Calico。Calico的模式和flannel-gw几乎一样,也在在每台宿主机上,添加一个格式如下的路由规则:

1
<目的容器IP地址段> via <网关的IP地址> dev eth0

与Flannel host-gw不同的是,Flannel通过etcd和宿主机上的flanneId来维护路由信息,而Calico通过BGP来自动地在整个集群中分发路由信息。

BGP全称是 Border Gateway Protocol,即边界网关协议。它是一个linux内核原生就支持的,专门用在大规模数据中心维护不同的”自治系统”之间路由信息的、无中心的路由协议。

image-20230629174835972

如上图所示。AS1,AS2是两个不同的网络区,Router就是我们的一个边界网关。AS1中的机器要访问AS2中的机器,只需要在Router1中添加路由规则到AS2即可。像这样的把各区连接在一起的路由器,就叫做边界网关。它跟普通路由器的不同之处在于,它的路由表里面拥有其他区的主机路由信息

如果了解了BGP之后,Calico项目的架构就比较容易理解了。它由三部分组成:

  1. Calico的CNI插件。这是与Kubernetes对接的部分。
  2. Felix。他是一个deameonset,负责在宿主机上插入路由规则(即:写入Linux内核的FIB转发信息库),以及维护Calico所需的网络设备等工作。
  3. BIRD。它就是BGP的客户端,专门负责在集群里分发路由规则信息。

除了对路由信息的维护方式之外,Calico项目与Flannel的Host-gw模式另一个不同之处,它不会再宿主机上创建任何网桥设备。它的工作模式如下:

image-20230629175519357

图中绿线是完整的跨主机容器访问图。可以看到,calico的cni插件会为每个容器设置一个veth pair设备,然后把其中的一端放置在宿主机上(它的名字以cali前缀开头)。

由于calico没有使用Cni的网桥模式,Calico的CNI插件还需要在宿主机上位每个容器的VethPair 设备配置一条路由规则,用于接受传入的ip包,可以使用 ip route 看到。

1
2
3
10.233.2.3 dev cali5863f3 scope link

即:发往 10.233.2.3 的 IP 包,应该进入 cali5863f3 设备。

有了veth pair设备之后,容器发出的ip包就回经过veth pair设备出现在宿主机上。然后宿主机网络栈就会根据路由规则的下一跳IP地址,把他们转发给正确的网关。这里的最核心的”下一跳”路由规则,就是有Calico的Felix进程负责维护的。这些路由规则信息,则是通过BGP Client 也就是BIRD组件,使用BGP协议传输而来。

BGP协议的传输格式,可以理解为如下:

1
2
3
4
[BGP消息]
我是宿主机192.168.1.3
10.233.2.0/24网段的容器都在我这里
这些容器的下一跳地址是我

Calico项目实际上将集群的所有节点,都当做是边界路由器来处理,他们一起组成一个全连通的网络,互相之间通过BGP协议交换路由规则。这种模式是默认的”Node-to-Node Mesh”模式,随着节点的数量增加,连接的数量会以N^2的规模快速增长,一般只用于少于100个节点的集群中。如果超过100个节点的,可以使用Route reflector的模式。该模式下,会有一个或者几个专门的节点,负责跟所有节点建立BGP连接从而学习到全局的路由规则,而其它节点,只需要跟这几个节点交换路由信息,就可以获得整个集群的路由规则信息了。

前面的两种模式,都是有一个前提,那就是2层网络是通的。但是如果跨数据中心的话,比如下面的container1要访问container4, 那就需要用到calico的IPIP模式了。

ipip示意图

在calico的IPIP模式下,Felix 进程在Node1 上添加的路由规则,会稍微不同:

1
10.233.2.0/24 via 192.168.2.2 tunl0

尽管这条规则的下一跳地址仍然是node2的ip地址,但这一次,负责将ip包发出去的设备,变成了tunl0。它是一个IP隧道(IP tunnel)设备。

IP包进入IP隧道设备之后,就会被Linux内核的IPIP驱动接管。IPIP驱动会将这个IP包直接封装在一个宿主机网络的IP包中:

封包

经过封装后的新IP报的目的地址,就是原IP包的下一跳地址,即Node2 的IP地址:192.168.2.2。

而原IP包本身,则会被直接封装成新IP包的Payload。这样原先从容器到Node2的ip包,就被伪装成了一个从Node1到node2的ip包,由于宿主机之间已经使用路由器配置了三层转发,也就是设置了宿主机之间的下一跳。所以这个IP包在离开Node1之后,就可以经过路由器,最终”跳”到node2上。

这时,Node2的网络内核占会使用IPIP驱动进行解包,从而拿到原始的IP包,然后,原始IP包就会经过路由规则和VethPair设备到达目的容器内部。

本文标题:容器网络 - 三层网络解决方案

文章作者:

发布时间:2023-06-29 22:05:44

原始链接:http://chenzhijun.top/2023/06/29/kubernetes-container-network-3/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!