以网桥类型的Flannel插件为例,讲解了Kubernetes中的容器网络和CNI插件的主要工作原理,不过除了这种模式,还有种纯三层网络方案进行注意,其中的典型例子,莫过于Flannel的host-gw和单独的Calico项目了
首先是Flannel的host-gw模式,其模式如下图所示
假设现在,Node 1上的Infra-container-1,要访问Node 2上的Infra-container-2
如果是host-gw的模式,flannel会在宿主机上创建一个规则,如下所示
$ ip route
…
10.244.1.0/24 via 10.168.0.3 dev eth0
这要规则的含义是,目的IP属于10.244.1.0/24网段的IP包,经过本机的eth0设备发出去,并且下一跳next-hop是10.168.0.3
下一跳,就是我们的目的宿主机Node2
一旦配置了下一跳地址,当IP包从网络层进入链路层封装成帧的时候,eth0会使用下一跳地址对应的MAC层,作为这个数据帧目的MAC地址,这个MAC地址,就是Node2的MAC地址
这个数据帧就会从Node1通过宿主机的二层网络顺利到达Node2上面
Node2的内核网络栈从二层数据帧拿到了IP包之后,会看到这个IP包的目的IP地址是10.244.1.3,即为Infra-container-2的IP地址,然后进入宿主机2上的cni0网桥,然后进入Infra-container-2当中
这个host-gw模式的工作原理,就是讲每个Flannel子网的下一跳,设置了该子网对应的宿主机的IP地址
这个主机host会充当这个容器通信中网关的角色,这就是host-gw的含义
然后Flannel子网和主机的信息,都是保存在Etcd中的,Flanneld只需要WATCH这些数据的变化,然后更新路由表即可
而且Flannel和Calico都是使用的APIServer直接访问Etcd的,无须额外的Etcd来配置
在这种模式下,容器通信的过程就避免了额外的封包和解包的性能消耗
当然,host-gw正常工作的核心,在于IP包在封装成帧发送,并且使用了下一跳进行设置目的的MAC地址,这样,就会经过二层网络到达目的宿主机
所以Flannel host-gw要求宿主机之间二层是联通的
需要注意的是,宿主机二层不连通的情况是广泛存在的,宿主机分布在了不同的子网中,这就是二层不连通的问题.但是在一个Kubernetes上,宿主机之间必须要可以通过IP地址进行通信,至少是三层可达的,不然集群会不满足上一篇的宿主机之间IP互通的假设,三层可达可以通过几个子网设置三层转发的实现
三层网络必然是Calico的天下
Calico提供的解决方案,和Flannel的host-gw模式类似,也是添加一个路由规则
<目的容器IP网段> via <网关IP网段> dev eth0
网关的IP地址,就是目的容器宿主机的IP地址
利用这个宿主机的IP地址,当做对应的下一跳网关
不过,不同于Flannel通过Etcd和宿主机上的Flanneld维护路由信息的做法,Calico使用了一个重型武器来自动的在整个集群中分发路由信息
就是对应的BGP,BGP是由Linux内核进行维护的,一个无中心的路由协议
这个概念可能比较吓人,但描述下来如下
这样,就有两个自治系统,分别负责两个宿主机,名为AS1 和 AS2,这样的一个自治系统,管理着宿主机上所有IP网络设备,我们为了让这两个自治系统连接起来进行通信,就必须要使用路由器将这两个自治系统连接起来
我们假设一个从AS 1 主机10.10.0.2,访问AS2上的 172.17.0.3 发出的包,要先到自治系统AS1上的路由器Router 1
Router 1的路由表,有一个规则 目的是 172.17.0.2的包,要经过Router1 的C接口,发往网关Router2
然后IP包就到达了Router2上,经过Router2的路由表,就会发往对应的容器主机
反过来呢,如果2访问1,是否能拿到发往1的路由规则?
将自治系统连接在一起的路由器,形象的称为边界网关,和一般的路由器不同之处在于,路由表具有其他自治系统的主机路由信息
上面的部分原理,理解起来相对容器,毕竟路由器设备本身的主要作用,就是联通不同的网络
但是,因为保存着可以联通的大量路由器的信息,所以会给BGP协议本身带来一个问题,这是最后说的
先说回BGP,因为BGP的大规模维护的能力,所以Calico架构就相对好理解了,这就是由三个部分组成的
1,Calico的CNI插件,和Kubernetes对接的部分
2.Felix,一个DaemonSet,在宿主机上写入路由规则,维护Calico所需网络设备信息
3.BIRD,BGP的客户端,保存其他节点的路由规则
而且Calico并不会维护一个网桥设备,实行过程中的具体示意图如下
这样的绿线,就是一个IP包从Container1,到达Container4的完整路径
Calico的CNI插件会为每个容器设置一个Veth Pari的设备,然后将其中一端放在宿主机上
由于Calico没有使用网桥模式,所以Calico的CNI插件还要为每个容器的Veth Pair配置一个对应的路由规则,用于接收传入的IP包
这样容器发的IP包,必然会直接出现在宿主机上了,这样宿主机网络就会根据路由规则的下一跳,转发给正确的网关,接下来流程就和Flannel host-gw模式完全一致了
下一跳的路由规则,就是交由Calico的Felix进程负责维护的,这些路由信息,是通过BGP Client,使用BGP协议传输而来的
可以简单的理解为如下的格式
[BGP消息]
我是宿主机192.168.1.3
10.233.2.0/24网段的容器都在我这里
这些容器的下一跳地址是我
不难发现,Calico将集群中所有的节点,都当做边界路由器来管理,在一起组成了一个全联通的网络,通过BGP协议进行交换路由规则,这些节点被称为BGP Peer
Calico维护的网络在默认配置下,是一个被称为Node-to-Node Mesh的模式,每台宿主机的Bgp Client 都会和所有其他的BGP节点进行交换路由规则,但是伴随着节点数目N的增加,保存的节点规则数量会以N平方的规模进行增长,所以 Node-to-Node Mesh模式一般是用于少于100节点的集群,在更大的集群中,需要用到的是一个Route Reflector 的模式
在这种模式中,Calico会指定一个或者几个专门的节点,来负责跟所有节点建立BGP连接,学到全局的路由规则,其他节点,和这几个负责统计信息的节点进行交换信息,就可以获得整个集群的路由规则信息
这些专门的节点,就是Route Reflector节点,扮演了中间代理的角色,将BGP的连接规模进一步放大
此外,host-gw的二层联通的限制,对于Calico来说,也同样存在的
举个例子,假如我们有两个不同子网的宿主机Node1和Node2,对应的IP地址分别为192.168.1.2和 192.168.2.2 这两台机器通过路由器实现了三层转发,IP地址是互通的
我们需要Container1访问container4,Calico会尝试在Node1上添加一个如下的路由规则
10.233.2.0/16 via 192.168.2.2 eth0
但是Node1如果对于Node2是二层不通的,那么该怎么办呢?
这样,就需要为Calico打开IPIP模式
这个模式的示意图如下
在IPIP 模式下,Felix进程在Node1的进程中配置的路由规则,将原本的负责设备,由T-U-N-L-0,变为了tunl0,设备的功能是不一样的
Calico使用的tunl0设备,是一个IP隧道 设备,
IP包进入了IP隧道设备后,会被Linux内核的IPIP驱动接管,然后进行重新的封包
将源IP包,封装为一个新的IP包,这样,原本的容器到Node2的IP包,就会封装为一个从Node1到Node2 IP包,宿主机之间已经使用路由器配置了三层转发,设置宿主机的下一跳,所以IP包在离开Node1之后,可以经由路由器,到达Node2
Node2的网络栈经由IPIP驱动解包后,拿到原始的IP包,到达了容器的内部
实际测试中,Calico的IPIP模式和Flannel的VXLAN模式的性能大致相同,所以,在实际使用中,非硬性需求,还是将所有的宿主放在一个子网中,避免IPIP的使用
不过,如果能让宿主机之间的路由设备,学到Calico网络中的路由规则,那么容器中发出的IP包,不就可以通过这些设备路由到目的宿主机了吗?
比如没在Node上添加一个到达Router的路由规则,Router在配置对应的目标IP,这样就可以直接到达Node2上了
上面的流程简单明了,但是在公有云上,如何去配置公有云的网关,是一个难点
所以,可以考虑在私网上,进行相关的配置,
第一种方案,就是所有的宿主机都和宿主机网关建立BGP Peer关系
这种方案下,Node1和Node2主动和宿主机网关Router1,Router2建立关系,进行路由信息的同步,不过这种方式下,Calico要求网关需要支持一种名为Dynamic Neighbors的BGP配置方式,这个BGP配置模式允许给路由器配置一个网段,然后路由器就会自动和这个网段的主机建立BGP Peer关系
第二种方案,就是使用额外组件去搜集路由信息,然后同步给网关,这样的和网关进行沟通的组件,由Route Reflector兼任
我们在本章,说了Flannel host-gw模式和Calico项目 这两种,三层网络方案的工作原理
在私有的部署环境上,Calico能覆盖的场景更多,所以可以去参考如何去组网方案和架构思路
在公有环境上,因为有一个大二层的概念,所以可以考虑使用简单的Flannel host-gw模式
那么,三层网络方案和隧道模式的异同之处,以及各自的优缺点?
三层的优点,就是少了封包和解包的过程,性能会更高
三层的缺点,需要自我去维护这些路由规则,可能较高
隧道的优点,简单,大部分的工作都交给Linux内核模块实现,应用层面少
隧道的缺点,需要拆解包