本文中,笔者主要结合自己使用flannel
心得,以及flannel
的技术演进,介绍下flannel
网络实现方案。在没有介绍flannel overlay
网络实现方案之前,先回顾下docker
网络实现方案。
★
docker
网络模式有哪些?
bridge、host、none
新版本docker
出现了macvlan、overlay
跨主机网络通信方案。
★ 桥接模式是如何实现的呢?
首先docker
在默认安装情况下,启动之后会默认建立docker0 linux
网桥设备、该网桥设备拥有一个私有网络地址以及子网,通常使用子网中第一个没有被占用的地址。比如:172.16.0.1
;
然后docker
容器在启动的时候会连接到网桥设备上,并分配一个子网地址。容器连接到网桥的网络接口会把docker0
设备作为网关。创建容器时,docker
会创建一对网络设备接口,并把他们放到独立的命名空间中:一个网络设备放到容器的网络命名空间中eth0
,另一个网络设备会放到宿主机网络空间中,例如:veth80025c52
,并连接到docker0
网桥设备上。所以如果仔细观察容器内部网卡设备信息和宿主机的网卡信息,你会发现这一对网络设备是一一对应的。当然宿主机上其它容器也会连接到docker0
网桥设备上,这样就实现了宿主机内容器的通信;
最后容器绑定到网桥设备之后,如果需要访问外网,那么借助于linux
的IP
转发规则,以及docker
引擎管理的防火墙规则以及nat
功能,实现了外部网络的访问。
flannel三层网络实现方式
★ 需求描述:
node1(11.101.1.2)
上的container1(10.244.0.13)
需要跨主机访问node2(11.101.1.3)
上的container2(10.244.1.14)
。对于flannel
是如何实现的呢?
UDP跨主通信模式
如上图所示:首先flannel
会在各个节点上创建路由规则,这些路由规则存储在etcd
中,跟宿主机节点IP一一对应。
然后在node1
上,container1
跨主访问node2
上的container2
,因为container2
的IP
地址为10.244.1.14
,根据路由规则,从而进入到flannel0
设备中。
最后flannel0
看到container1
要访问的IP
地址为10.244.1.14
的容器,因为flannel
在etcd
中存储着子网和宿主机ip
的对应关系,所以能够找到10.244.1.14
对应的宿主机IP
为11.101.1.3
,进而开始组装UDP
数据包发送数据到目的主机。当然这个请求得以完成的原因每个节点上都启动着一个flanneld udp
进程,都监听着8285
端口,所以node1
通过flanneld
进程把数据包发送给node2
的flanneld
进程的相应端口即可。
可以看出flannel UDP
模式提供了一个三层OverLay
网络,这就好比在不同宿主机的容器上打通了一条隧道,容器不用关心IP
地址即可直接通信。它首先对发出端的数据包进行UDP
封装,然后在接收端进行解包,进而把包发送到目的容器地址。但是这里面有一个非常严重的问题,就是flannel UDP
进程运行在用户态,而数据的交互和传递则在内核态完成,这就造成了为了传递数据,需要内核态和用户态的频繁切换,这个切换过程有一定性能损耗代价,所以UDP
模式已经废弃。
VXLAN跨主通信模式
VXLAN(Vitrue Extension lan
虚拟可扩展局域网)Linux
本身支持的一种虚拟可扩展局域网。VXLAN
完全在内核态完成上述封包和解包的过程。从而通过前面的隧道机制完成overlay
覆盖网络。rfc7348
详细地介绍了VXLAN
的实现机制。本质上VXLAN
是一种隧道技术。通过将虚拟网络中的数据帧封装在实际物理网络中的报文中进行传输。具体实现方式为:将虚拟网络的数据帧添加VXLAN
首部后,封装在物理网络中的UDP
报文中,然后以传统网络的通信方式传送该UDP
报文,到达目的主机后,去掉物理网络报文的头部信息以及VXLAN
首部,将报文交付给目的终端。整个通信过程目的终端不会感知到物理网络的存在。
- VXLAN技术组网过程!
图中两台终端T1和T2位于不同的网络中,二者通过路由器来实现互通,通过VXLAN
可以使得这两台终端在“逻辑上”位于“同一个”链路层网络中而与两台终端直接相连的路由器也在逻辑上构建了一条在虚拟链路中的通道vxlan tunnel
,这样的路由器我们称之为vxlan
隧道终端(VXLAN Tunnel End Point, VTEP)
。在包含VXLAN
的网络中,VXLAN
的实现机制仅仅对VTEP
节点可见。
- VXLAN通信原理
VXLAN
通过将逻辑网络中通信的数据帧封装在物理网络中进行传输,封装和解封装的过程由VTEP
节点完成。VXLAN
将逻辑网络中的数据帧添加VXLAN
首部后,封装在物理网络中的UDP报文中传送,VXLAN
首部的格式如下:VXLAN
首部由8个字节组成,第1个字节为标志位,其中标志位I设为1表示是一个合法的VXLAN
首部,其余标志则保留,在传输过程中必须置为0;第2-4字节为保留部分,第5-7
字节为VXLAN
标识符,用来表示唯一的一个逻辑网络;第8个字节同样为保留字段,暂未使用。VXLAN
传输过程中,将逻辑链路网络的数据帧添加VXLAN首部后,依次添加UDP首部,IP首部,以太网帧首部后,在物理网络中传输,数据帧的封装格式可以用下图来描述:
需要注意的是,外部UDP
首部的目的端口号为4789
,该数值为默认VXLAN
解析程序的端口,外层IP
首部中的源IP
和目的IP
地址均填写通信双方的VTEP
地址,协议的其余部分和传统网络相同。
- 通信过程
对于处于同一个VXLAN
的两台虚拟终端,其通信过程可以概括为如下的步骤:
发送方向接收方发送数据帧,帧中包含了发送方和接收方的虚拟
MAC
地址。发送方连接的
VTEP
节点收到了数据帧,通过查找发送方所在的VXLAN
以及接收方所连接的VTEP
节点,将该报文添加VXLAN
首部、外部UDP
首部、外部IP首部后,发送给目的VTEP
节点。报文经过物理网络传输到达目的
VTEP
节点。目的
VTEP
节点接收到报文后,拆除报文的外部IP
首部和外部UDP
首部,检查报文的VNI
以及内部数据帧的目的MAC
地址,确认接收方与本VTEP
节点相连后,拆除VXLAN
首部,将内部数据帧交付给接收方。接收方收到数据帧,传输完成。
通过以上的步骤可以看出:VXLAN
的实现细节以及通信过程对于处于VXLAN
中的发送方和接收方是不可见的,基于发送方和接收方的视角,其通信过程和二者真实处于同一链路层网络中的情况完全相同。
对于Kubernetes flannel
也是完全依赖linux vxlan
实现了overlay
的跨主网络通信,如下图所示:
从形式和流程上看,这个通信过程和上面基于UDP
的通信方式是非常类似的,只不过flannel UDP
进程换成了VTEP
设备,通过VTEP
设备完成封包和解包的过程,另外一点这个过程完全在内核中完成。flannel.1
充当网桥的角色,进行UDP
数据包的转发。其中vxlan
的通信过程也是flannel
网络插件中默认的通信方式,如下图所示:
巨人的肩膀:
1 | https://blog.csdn.net/jsh13417/article/details/80303098 |
host-gw跨主通信方式
host-gw
的工作原理就是将每个flannel
子网转发地址设置成了该子网对应的宿主机的IP
地址,通过这个过程,容器在通信过程中就减少了封包和解包的性能损耗。如下图所示:
同
UDP、VXLAN
模式一致,通过容器A的路由表IP
包到达cni0
,到达cni0
的IP
包匹配到node1
中的路由规则(10.244.1.1/24)
,且网关为11.101.1.3
,即主机node2
,所以内核将IP
包发送给node2
,IP
包通过物理网络到达node2
的eth0
;到达
node2
的eth0
的IP
包匹配到node2
上的路由表(10.244.0.1/24)
,IP
包转发给cni0
,cni0
将IP
包转发给连接在cni0
上的Container2
。
通过上述这个过程可以看出这台主机的host
就充当了容器通信路径里的网关,IP
封装成帧的时候,会使路由表中的下一跳来设置目的MAC
地址,它会经过二层网络达到宿主机,但同时这个限制也是有问题的,首先要求我们必须保证集群内部所有主机二层网络必须是连通的,然后在大规模集群路由表的动态更新也存在一定压力。采用host-gw
模式后,flanneld
的唯一作用就是负责主机上路由表的动态更新。当然这个限制也是有解决方案的,这里不在过多介绍,详细可以了解Calico
。
总结
通过如上过程,我们可以看到flannel
从UDP、wxlan、host-gw
网络通信方案的技术演进过程,通过一系列的演进使得架构更简单、同时性能得到了提高。如有问题请留言或者关注后拉你进群讨论。希望能够帮助到大家,谢谢阅读!