什么是overlay network?

Overlay 技术是在现有的物理网络之上构建一个虚拟网络,上层应用只与虚拟网络相关。

Overlay 在网络技术领域,指的是一种网络架构上叠加的虚拟化技术模式,其大体框架是对基础网络不进行大规模修改的条件下,实现应用在网络上的承载,并能与其它网络业务分离,并且以基于 IP 的基础网络技术为主。

vxlan

VxLAN 使用虚拟隧道端点(Virtual Tunnel End Point、VTEP)设备对服务器发出和收到的数据包进行二次封装和解封。

VXLAN的设计思想是:

在现有的三层网络之上,“覆盖”一层虚拟的、由内核VXLAN模块负责维护的二层网络,使得连接在这个VXLAN二层网络上的“主机”(虚拟机或容器都可以),可以像在同一个局域网(LAN)里那样自由通信。

VXLAN是一种隧道技术。通过将虚拟网络中的数据帧封装在实际物理网络中的报文中进行传输。具体实现方式为:将虚拟网络的数据帧添加VXLAN首部后,封装在物理网络中的UDP报文中,然后以传统网络的通信方式传送该UDP报文,到达目的主机后,去掉物理网络报文的头部信息以及VXLAN首部,将报文交付给目的终端。整个通信过程目的终端不会感知到物理网络的存在。

为什么需要 Overlay Network?

为了不影响隔离性并实现容器间的网络通信,Docker 通过虚拟网桥“连接”容器,使容器得以像物理节点一样经过“交换机”通讯。Docker 在宿主机上创建名为 docker0 的虚拟网桥,对于每一个创建的容器均创建一对虚拟网卡设备,其中一端在 docker0,另一端映射到容器内的 eth0,并对容器内网卡分配一个容器网络 IP。通过这一对虚拟网卡,容器就相当于“连接”到网桥上,虚拟网卡接在网桥上时只负责接受数据包,不再调用网络协议栈进行处理,因此只具有类似端口的作用。当容器 A 要访问容器 B 时,只需要广播 ARP 协议,通过 docker0 转发请求到对应”端口”,就实现了数据的转发。

然而,虽然虚拟网桥解决了同一宿主机下的容器间通信问题,以及容器与外部世界之间的通信,但是跨节点的容器通信依然存在问题。集群中每个节点的 docker0 都是独立的,不同节点分配的容器 IP 之间存在冲突的可能,因此需要有一个具有全局视角的上层网络以实现跨节点的容器网络,这便是 Overlay Network 解决方案的由来。

Flannel的介绍

Flannel是一种基于overlay网络的跨主机容器网络解决方案,也就是将TCP数据包封装在另一种网络包里面进行路由转发和通信。在默认的Docker配置中,每个节点上的Docker服务会分别负责所在节点容器的IP分配。这样导致的一个问题是,不同节点上容器可能获得相同的内外IP地址。并使这些容器之间能够之间通过IP地址相互找到,也就是相互ping通。Flannel设计目的就是为集群中所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得"同属一个内网"且"不重复的"IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。

Flannel UDP模式

TUN设备

TUN设备是工作在三层的虚拟网络设备,功能是:在操作系统内核和用户应用程序之间传递IP包。 相比两台宿主机直接通信,多出了 flanneld 的处理过程,这个过程,使用了 flannel0 这个TUN设备,仅在发出 IP包的过程中经过多次用户态到内核态的数据拷贝(linux的上下文切换代价比较大),所以性能非常差

我们来分析这个图片的流程

  • IP 为 10.1.15.2 的 Pod1 与另外一个 Node 上 IP 为 10.1.20.3 的 Pod1 进行通信;
  • 首先 Pod1 通过 veth 对把数据包发送到 docker0 虚拟网桥,网桥通过查找转发表发现 10.1.20.2 不在自己管理的网段,就会把数据包转发给默认路由(这里为 flannel0 网桥);
  • flannel0是一个tun设备,它是一种工作在三层的虚拟网络设备,而flanneld是一个proxy,它会监听flannel0并转发流量,所以发送给flannel0接口的包将被flanneld进程接收到。flannel0看到Pod1 要访问的IP地址为10.1.20.3 的容器,因为flannel在etcd中存储着子网和宿主机ip的对应关系,所以能够找到10.1.20.3对应的宿主机IP为192.168.0.100,进而开始组装UDP数据包发送数据到目的主机

这里有一个问题就是flanneld怎么知道10.1.20.2这个容器到底是在哪个节点上呢?

flanneld在启动时会将该节点的网络信息保存到etcd当中,故在发送报文时可以通过查询etcd得到192.168.0.100这个容器的IP属于Node2 ,且Node2 的IP为192.168.0.100。

  • flanneld将封装好的UDP报文经eth0发出
  • 网络包经Node1和Node2之间的网络连接到达Node2
  • Node2 收到UDP报文后经Linux内核通过UDP端口号8285将包交给正在监听的应用flanneld。
  • 运行在Node2 当中的flanneld将UDP包解包后得到:10.1.15.2 -> 10.1.20.3 。
  • 解包后的包匹配到Node2 上的路由规则(10.10.20.0),内核将包发送到docker0 虚拟网桥,继而由 docker0 传输到 Pod 容器。

UDP模式性能差的原因

以flanel0为例,操作系统将一个IP包发给flanel0,flanel0把IP包发给创建这个设备的应用程序:flanel进程(内核态->用户态)

相反,flanel进程向flanel0发送一个IP包,IP包会出现在宿主机的网络栈中,然后根据宿主机的路由表进行下一步处理(用户态->内核态)

当IP包从容器经过docker0出现在宿主机,又根据路由表进入flanel0设备后,宿主机上的flanneld进程就会收到这个IP包

总结

可以看出flannel UDP模式提供了一个三层OverLay网络,这就好比在不同宿主机的容器上打通了一条隧道,容器不用关心IP地址即可直接通信。它首先对发出端的数据包进行UDP封装,然后在接收端进行解包,进而把包发送到目的容器地址。但是这里面有一个非常严重的问题,就是flannel UDP进程运行在用户态,而数据的交互和传递则在内核态完成,这就造成了为了传递数据,需要内核态和用户态的频繁切换,这个切换过程有一定性能损耗代价,所以UDP模式已经废弃。

Flannel VXLAN模式

VxLAN,即Virtual Extensible LAN(虚拟可扩展局域网),是Linux本身支持的一网种网络虚拟化技术。VxLAN可以完全在内核态实现封装和解封装工作,从而通过“隧道”机制,构建出 Overlay 网络(Overlay Network)。

Flannel host-gw模式

这是一种纯三层网络的方案,性能最高

如下图所示,node0和node1之间可以通过eth0互连,但container0和container2无法互连,如果想让它们之间能通信,可以怎么做呢?

container0和container2无法互连的原因在于:当container0发往container2的包(container2向container0发包也是类似的情况)经过eth0时,内核将查找route表规则,显然不能找到符合192.168.1.2/24(container2的ip)的规则,因此只能发往默认路由(一般是gateway网关),事实上gateway也不能找到符合192.168.1.2/24的规则,最终所有的路由器都不知道目标ip地址为192.168.1.2/24的包该发到哪个主机上。

知道这个原因后,你可能会想:如果在node0上创建一条route表规则,让目标地址为192.168.1.2/24的包全部发往node1,那不就解决问题了?是的,host-gw正是这样解决的:在node0上创建route表规则(ip route add 192.168.1.0/24 via 10.20.0.2 dev eth0,让目标地址为192.168.1.0/24的包全部发往node1,在node1上创建route表规则(ip route add 192.168.0.0/24 via 10.20.0.1 dev eth0),让目标地址为192.168.0.0/24的包全部发往node0,这样node0和node1上的conta iner就可以互相通信了。

问题

所有的主机都在一个子网内,即二层可达,否则就无法将目的主机当做网关,直接路由。

总结

总的来说,flannel更像是经典的桥接模式的扩展。我们知道,在桥接模式中,每台主机的容器都将使用一个默认的网段,容器与容器之间,主机与容器之间都能互相通信。要是,我们能手动配置每台主机的网段,使它们互不冲突。接着再想点办法,将目的地址为非本机容器的流量送到相应主机:如果 集群的主机都在一个子网内,就搞一条路由转发过去;若是不在一个子网内,就搞一条隧道转发过去。这样以来,容器的跨网络通信问题就解决了。而flannel做的,其实就是将这些工作自动化了而已。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐