一、背景介绍
前面一章我们介绍了Node节点上面不同的容器之间的通讯方式,主要是根据docker0(网桥)+Veth Pair的方式来玩起来的。
本章我们来介绍下不同Node节点上面不同的容器之间的通讯方式,我们不妨想一想,如果不同Node节点上面的容器能够共享一个网桥的话,不就跟同一个Node节点上面的通讯方式一样了吗。
其实K8S确实是按照这个思路来玩的,不过这里引入了一个新概念Overlay Network(覆盖网络):通过软件构建一个覆盖在已有宿主机网络之上的、可以把所有容器连通在一起的虚拟网络。
如此以来,这个Overlay Network就需要解决两个问题:
1.Node1节点上面的容器1发送的消息如何正确的发到正确的Node节点?
2.收到这个消息的Node节点如何将这个消息正确的转发给对应的容器上面?
二、通讯过程介绍
K8S解决容器间的网络通讯方案,采用的是CoreOS公司提供的Flannel项目,该项目的实现方式有下面三种,我们会一一介绍。
1. UDP方式,是最早支持的一种方案,最简单也最直接,不过性能也是最差的,目前已经被废弃,不过设计思路挺好的,可以看一下。
核心思想:
通过将Node1节点上面的容器1里的消息通过docker0转发给flannel0设备,经过一次内核态到用户态的转发到flannelId这个进程,通过UDP协议封装好Node1上面的源容器container1的ip和目的容器Node2上面container3的ip,发送给对端的Node2容器。
Node2对应的flannelId收到这个消息之后,解析UDP协议,把消息转到内核态的flannel0上面,进而通过docker0转交到container3上面。
特别注意:这里有两个关键点,如下所示:
1)flannelId的端口是固定的8285。
2)Node1节点是如何找到container3在Node2的呢?
这个实现方式是:flannelId按照节点Node划分成了不同的子网subnet,然后将这些在子网和Node节点的对应关系存储在etcd里面,flannelId可以通过对端容器的IP从etcd里面查找这个对应关系,进而找到是哪一个目的节点Node,进而进行上面的数据转发。
2.VXLAN模式
VXLAN: Virtual Extensible LAN(虚拟可扩展局域网),是 Linux 内核本身就支持的一种网络虚似化技术。VXLAN 可以完全在内核态实现上述封装和解封装的工作,从而通过与前面相似的“隧道”机制,构建出覆盖网络(Overlay Network)。
设计思想是:在现有的三层网络之上,“覆盖”一层虚拟的、由内核 VXLAN 模块负责维护的二层网络,使得连接在这个 VXLAN 二层网络上的“主机”(虚拟机或者容器都可以)之间,可以像在同一个局域网(LAN)里那样自由通信。这些“主机”可能分布在不同的宿主机上,甚至是分布在不同的物理机房里。
VXLAN 技术是借助了虚拟隧道端点VTEP(VXLAN Tunnel End Point)这个设备,它的作用与flannelId的功能类似,不过VTEP封装和解封的是二层数据帧,让这个工作流程在内核层面来完成。对应于宿主机上面的flannel.1 的设备,它既有 IP 地址,也有 MAC 地址。
过程为:
1)container1发出去的IP包消息会先到docker0这个默认网桥。
2)Node1上面的docker0会路由到flannel.1这个vtep设备,并通过目的容器的IP去查找到目的VTEP设备的MAC地址。
3) Node1的flannel.1这个“源 VTEP 设备”收到“原始 IP 包”后,就要想办法把“原始 IP 包”加上一个目的 MAC 地址,封装成一个二层数据帧,然后发送给“目的 VTEP 设备”。(备注:这里的目的MAC地址获取,是通过目的IP查询获取的目的的MAC地址。这里要用到的 ARP 记录(三层 IP 地址对应的二层 MAC 地址),也是 flanneld 进程在 Node 2 节点启动时,自动添加在 Node 1 上的。)
4)在上面3)封装好了内部数据帧之后,linux内核会在内部数据帧前面添加上xvlan这个标识VNI,并封装到UDP协议中。
5)Node1在通过eth0发送数据出去的时候,需要找到目的节点的IP地址,这里获取的方式为:Node1通过flannel.1去查询FDB (Forwarding Database:转发数据库),也就是根据目的VTEP的MAC地址找到它所在Node节点上面的IP地址,并填入发出去的数据报文中,通过Node1的eth0网卡发送出去。
3.CNI插件
K8S里面的网络模型与2中介绍的原理基本一致,只不过用cni0网桥替代了docker0网桥,详细交互过程不在介绍,如下图所示:
CNI 的设计思想:Kubernetes 在启动 Infra 容器之后,就可以直接调用 CNI 网络插件,为这个 Infra 容器的 Network Namespace,配置符合预期的网络栈。