上一章节我们学习节点内容器通信。深入理解K8S节点内容器通信_量子学习法的博客-优快云博客没学过的同学可以先看看上一篇作为本篇的基础。本章节,我们来学习容器跨主机通信。
Flannel是CoreOS公司推出的容器网络方案。Flannel本身是一个框架,真正实现容器跨主机通信的是Flannel的后端实现。目前,后端实现有3种方案:
1,VXLAN;2,host-gw; 3, UDP;
其中,host-gw比较复杂,我们留在写一篇文章。
这里UDP的方式较为原始,现在基本不用了,我们可以当作为后面两种方式为基础来学习。
UDP
同样的我们先上图。上一篇,我们讲解了容器通信到docker这一层。因此,若我们需要节点间容器通信,则需要添加flannel设备。
当100.96.1.2进程发出IP包,目的地址100.96.1.3。由于,100.96.1.3不在docker0网桥中,则IP包将出现在宿主机上。那么这个IP包下一个目的地就取决于宿主机的路由规则了。而宿主机的路由规则则由flannel设备确定。换句话来说,IP到达宿主机上的flannel设备。
flannel设备实际上是一个工作在第三层网络的虚拟设备:TUN (Tunnel设备)。TUN用于操作系统内核和用户程序之间传递IP包。
以上图左边部分为例:当IP到达宿主机上的flannel设备后,flannel设备就会将IP包传递给flanneld(8285端口)进程。这是内核向用户态(flanneld进程)的流动方向。flanneld进程收到包后知道是发向目的地址100.96.1.3后将IP包包装成UDP报文后发往宿主机网卡,网卡将UDP包送往目的地。如下图:
那么这里flanneld进程是如何对IP包进行包装成UDP的呢?
事实上,在由 Flannel 管理的容器网络里,一台宿主机上的所有容器,都属于该宿主机被分配 的一个“子网”。在我们的例子中,Node 1 的子网是 100.96.1.0/24,container-1 的 IP 地 址100.96.1.2。Node 2 的子网是 100.96.2.0/24,container-2 的 IP 地址是 100.96.2.3。而这些字网和宿主机的对应关系都是保存在etcd中的。
所以,flanneld进程拿到目的地IP后,就去etcd找到该子网对应的宿主机IP。再根据目的地宿主机IP将IP报文包装成UDP包发向目的机器。当然,这个请求得以完成的原因是,每台宿主机上的 flanneld,都监听着一个 8285 端口,所 以 flanneld 只要把 UDP 包发往 Node 2 的 8285 端口即可。
而目的宿主机node2的工作就简单了,Node2上的flanneld进程拿到UDP后将其转发给docket0网桥,后面的工作就和上一篇所讲的一样了,docker0 网桥会 扮演二层交换机的角色,将数据包发送给正确的端口,进而通过 Veth Pair 设备进入到 container-2 的 Network Namespace 里。
以上就是flannel的UDP方式。这种方式最大的弊端就是用户态到内核态需要3次切换。
那实际上,将flanneld进程放入内核,我们就可以减少2次切换,而这正是VXLAN所要做的事。
VXLAN
VXLAN,即 Virtual Extensible LAN(虚拟可扩展局域网),是 Linux 内核本身就支持的一种 网络虚似化技术。所以说,VXLAN 可以完全在内核态实现上述flanneld进程封装和解封装的工作,从而通 过与前面相似的“隧道”机制,构建出覆盖网络(Overlay Network)。
VXLAN 会在宿主机上设置一个特殊的网络设备作 为“隧道”的两端。这个设备就叫作 VTEP,即:VXLAN Tunnel End Point(虚拟隧道端 点)。我们先上图:
VTEP设备的作用和flannel进程很相似,只不过,flannel进程分装的是IP包,而VTEP设备封装的是二层数据帧。并且VTEP设备这个过程全在内核态完成。
由图我们可以看到,VXLAN 所需的 VTEP 设备,它既 有 IP 地址,也有 MAC 地址。现在,我们的 container-1 的 IP 地址是 10.1.15.2,要访问的 container-2 的 IP 地址是 10.1.16.3。
VTEP 设备的工作就是封装上node2的 VTEP 设备的mac地址和IP地址。而这个设备的IP地址是由每台主机上的flanneld进程维护的。
当 Node 2 启动并加入 Flannel 网络之后,在 Node 1(以及所有其他节点)上, flanneld 就会添加一条路由规则:凡是发往 10.1.16.0/24 网段(该宿主机下为容器划分的子网)的 IP 包,都需要经过 flannel.1 设备发出, 并且,它最后被发往的网关地是:10.1.16.0。而10.1.16.0 正是 Node 2 上的 VTEP 设备(也就是 flannel.1 设备)的 IP 地址。
那么如何找到目的VTEP的MAC地址呢?
们已经知道了“目的 VTEP 设备”的 IP 地址。而要根据三层 IP 地址查询对应的二层 MAC 地址,这正是 ARP(Address Resolution Protocol )表的功能。而最新版本的 Flannel 并不依赖 L3 MISS 事件和 ARP 学习,而会在每 台节点启动时把它的 VTEP 设备对应的 ARP 记录,直接下放到其他每台宿主机 上。
因此,“源 VTEP 设备”收到“原始 IP 包”后,就要想办法把“原始 IP 包”加上一个目的 MAC地址,封装成一个二层数据帧,然后发送给“目的 VTEP 设备”(当 然,这么做还是因为这个 IP 包的目的地址不是本机)。
述封包过程只是加一个二层头,不会改变“原始 IP 包”的内容。所以图中 的 Inner IP Header 字段,依然是 container-2 的 IP 地址,即 10.1.16.3。
接下来,Linux 内核还需要再把当前这个帧进一步封装成为宿主机网络里的一个普通的数据帧,好让它“载着”“当前数据帧”,通过宿主机的 eth0 网卡进行传输。
为了实现这个机制,我们将当前这个帧前面,加上一个特殊的 VXLAN 头,用来表示这个“乘客”实际上是一个 VXLAN 要使用的数据帧。而这个 VXLAN 头里有一个重要的标志叫作VNI,它是 VTEP 设备识别某个数据帧是不是应该 归自己处理的重要标识。而在 Flannel 中,VNI 的默认值是 1,这也是为何,宿主机上的 VTEP 设备都叫作 flannel.1 的原因,这里的“1”,其实就是 VNI 的值。
UDP 模式类似,在宿主机看来,它会以为自己的 flannel.1 设备只是在向另外一台 宿主机的 flannel.1 设备,发起了一次普通的 UDP 链接。
好,目前我们让一个 flannel.1 设备能够成功发向另一端的 flannel.1 设备的 MAC 地址,却不知道 对应的宿主机地址是什么。
实际上,目的宿主机的地址也是flannel.1 设备维护的。我们可以查看这个flannel.1设备FDB 信息。可以看到发往我们前面提到的“目的 VTEP 设备”(MAC 地址是 5e:f8:4f:00:e3:37)的二层数据帧, 应该通过 flannel.1 设备,发往 IP 地址为 10.168.0.3 的主机。显然,这台主机正是 Node 2,UDP 包要发往的目的地就找到了。
所以接下来的流程,就是一个正常的、宿主机网络上的封包工作。
UDP是一个四层的包。Linux 内核会在它前面加上一个 IP 头,即原理图 中的 Outer IP Header,组成一个 IP 包。并且,在这个 IP 头里,会填上前面通过 FDB 查询 出来的目的主机的 IP 地址,即 Node 2 的 IP 地址 10.168.0.3。
最后,Linux 内核再在这个 IP 包前面加上二层数据帧头,即原理图中的 Outer Ethernet Header,并把 Node 2 的 MAC 地址填进去。这个 MAC 地址本身,是 Node 1 的 ARP 表要 学习的内容,无需 Flannel 维护。这时候,我们封装出来的“外部数据帧”的格式,如下所 示:
至此我们封包工作就宣告完成了。
接下来,Node 1 上的 flannel.1 设备就可以把这个数据帧从 Node 1 的 eth0 网卡发出去。显 然,这个帧会经过宿主机网络来到 Node 2 的 eth0 网卡。
这时候,Node 2 的内核网络栈会发现这个数据帧里有 VXLAN Header,并且 VNI=1。所以 Linux 内核会对它进行拆包,拿到里面的内部数据帧,然后根据 VNI 的值,把它交给 Node 2 上的 flannel.1 设备。
而 flannel.1 设备则会进一步拆包,取出“原始 IP 包”。接下来就回到了我在上一篇文章中分 享的单机容器网络的处理流程。最终,IP 包就进入到了 container-2 容器的 Network Namespace 里。
以上,就是 Flannel VXLAN 模式的具体工作原理了。
总结
在本篇文章中,我为你详细讲解了 Flannel UDP 和 VXLAN 模式的工作原理
下一节,我们将学习Flannel的host-gw模式和Calico。