Kubernetes中解决网络跨主机通信的一个经典插件就是Flannel。Flannel实质上只是一个框架,真正为我们提供网络功能的是后端的Flannel实现,目前Flannel后端实现的方式有三种:
UDP是最早的实现方式,但是由于其性能原因,现已经被废弃,但是UDP模式是最直接,也最容易理解的跨主机实现方式。
假如有两台Node,如下:
那么现在nginx01要访问nginx02,其流程应该是怎么样的呢?
其流程图如下:
注: 1、flannel0是一个TUN设备,它的作用是在操作系统和应用程序之间传递IP包; 2、Flannel是根据子网(Subnet)来查看IP地址对应的容器是运行在那个Node上; 3、这些子网和Node的对应关系,是保存在Etcd中(仅限UDP模式); 4、UDP模式其实是一个三层的Overlay网络;它首先对发出的IP包进行UDP封装,然后接收端对包进行解封拿到原始IP,进而把这个包转发给目标容器。这就好比Flannel在不同的宿主机上的两容器之间打通了一条隧道,使得这个两个IP可以通信,而无需关心容器和宿主机的分布情况;
UDP之所以被废弃是主要是由于其仅在发包的过程中就在用户态和内核态进行来回的数据交换,这样的性能代价是很高的。如下:
VXLAN:Virtual Extensible LAN(虚拟可扩展局域网),是Linux内核本身就支持的一种虚拟化网络技术,它可以完全在内核态实现上述的封装和解封装过程,减少用户态到内核态的切换次数,把核心的处理逻辑都放到内核态,其通过与前面相似的隧道技术,构建出覆盖网络或者叠加网络(Overlay Network)。
其设计思想为在现有的三层网络下,叠加一层虚拟的并由内核VXLAN维护的二层网络,使得连接在这个二层网络上的主机可以像在局域网一样通信。
为了能够在二层网络中打通隧道,VXLAN会在宿主机上设置一个特殊的网络设备作为隧道的两端,这个隧道就叫VTEP(Virtual Tunnel End Point 虚拟隧道端点)。而VTEP的作用跟上面的flanneld进程非常相似,只不过它进行封装和解封的对象是二层的数据帧,而且这个工作的执行流程全部在内核中完成。 其流程如下:
我们可以看到每台Node上都有一个flannel.1的网卡,它就是VXLAN所需要的VTEP设备,它既有IP地址,也有MAC地址。 现在我们nginx01要访问nginx02,其流程如下:
在这种场景下,flannel.1设备实际扮演的是一个网桥的角色,在二层网络进行UDP包的转发,在Linux内核中,网桥设备进行转发的依据是一个叫做FDB(Foewarding Database)的转发数据库,它的内容可以通过bridge fdb命令可以查看。
前面的两种模式都是二层网络的解决方案,对于三层网络,Flannel提供host-gw解决方案。 以下是host-gw示意图:
如上所示,如果我nginx01要访问nginx02,流程如下:
其工作流程比较简单,主要是会在节点上生成许多路由规则。 host-gw的工作原理就是将Flannel的所有子网的下一跳设置成该子网对应的宿主机的IP地址,也就是说Host会充当这条容器通信路径的网关,当然,Flannel子网和主机的信息会保存在Etcd中,flanneld进程只需要WATCH这个数据的变化,然后实时更新路由表。
在这种模式下,就免除了额外的封包解包的性能损耗,在这种模式下,性能损耗大约在10%左右,而XVLAN隧道的机制,性能损耗大约在20%~30%。
从上面可以知道,host-gw的工作核心为IP包在封装成帧发送出去的时候会在使用路由表中写下一跳来设置目的的MAC地址,这样它就会经过二层转发到达目的宿主机。这就要求集群宿主机必须是二层连通的。
要修改flannel模式就修改如下配置:
net-conf.json: |
{
"Network": "172.20.0.0/16",
"Backend": {
"Type": "host-gw"
}
}
net-conf.json: |
{
"Network": "172.20.0.0/16",
"Backend": {
"Type": "vxlan",
"Directrouting": true
}
}