作者|中国移动云能力中心PaaS产品部张永熙对于很多刚接触K8S,甚至接触K8S很久的同学来说,K8S的网络模型可以说是一个非常神秘的东西。那么对于这部分同学来说,恭喜你发现了这篇文章。只要花上20分钟,保证你能够轻松掌握K8S网络模型的原理。01知识储备首先,让我们提前预热,学习一点网络基础知识。1.1维基百科将网络命名空间定义为“Networknamespacesvirtualizethenetworkstack”,意思是linux网络命名空间虚拟化网络栈。什么是网络堆栈?网络堆栈包括网络接口、环回设备、路由表和iptables规则。例如,当您登录到linux服务器时,您默认使用Host网络堆栈。1.2网桥设备网桥是在内核中虚拟出来的,可以桥接真实的物理网卡(如eth0、eth1)或者宿主机上的虚拟网卡。桥接网卡相当于桥上的端口,端口接收到的数据包提交给这个虚拟的“桥”进行转发。1.3VethPair设备创建后,总是以两张虚拟网卡(VethPeer)的形式成对出现,其中一张“网卡”发送的数据包可以直接出现在对应的“网卡”上.VethPair常用作连接不同网络命名空间的网线。1.4VXLANVXLAN(VirtualextensibleLocalAreaNetwork,虚拟扩展局域网),是IETF定义的NVO3(NetworkVirtualizationoverLayer3)标准技术之一,是传统VLAN协议的扩展。VXLAN的特点是将L2以太网帧封装成UDP数据包(即L2overL4)并在L3网络上传输。VXLAN本质上是一种隧道技术。源网络设备和目的网络设备之间在IP网络上建立逻辑隧道,用户侧报文经过特定封装后通过该隧道转发。从用户的角度来看,连接到网络中的服务器就像连接到一个虚拟二层交换机的不同端口上,可以方便地进行通信。1.5BGP(BorderGatewayProtocol)边界网关协议(BorderGatewayProtocol,简称:BGP)是互联网上一个核心的去中心化自治路由协议。它通过维护IP路由表或“前缀”表来实现自治系统(AS)之间的可达性,是一种矢量路由协议。BGP不使用传统的内部网关协议(IGP)指标,而是使用路径、网络策略或规则集来确定路由。02什么是微服务的可观察性单机容器网络好了,经过上一章的热身,你应该对网络的基础知识有了一个大概的了解。那么我们先来尝试探索一下单机容器网络模型,这也是docker默认使用的单机容器网络模型。图1.主机上的不同容器通过网桥相互通信。一个独立的容器网络示意图如图1所示,图中有以下几个关键点:每个容器(Container)都有自己的NetworkNamespace。容器通过设备连接到主机的主机网络命名空间。容器NetworkNamespace端设备的“网卡”是eth0,在eth0上配置的ip就是容器的ip。将连接到HostNamespace的设备端挂载到网桥设备docker0上。桥接设备docker0将所有容器挂载在设备的HostNamespace端。而且,挂载在网桥上的设备会降级为网桥上的一个端口,该端口的唯一作用就是将数据包从网桥或另一端转发给设备。从Container1发送到Container2的数据包首先经过Container1中的eth0,到达docker0网桥。docker0网桥将数据包经过二层转发后发送到Container2对应的端口(Container2到设备的docker0网桥端)。这样数据包就直接发给了Container2。以上就是单机容器网络的基本原理。对单机容器网络模型有了初步的了解后,我们就进入正题,开始探索K8S网络模型。03K8S网络模型接触过K8S的同学一般都听说过Flannel和Calico这两种网络模型。但是,很多同学可能对这两种网络模型长什么样子,它们的工作原理是什么一头雾水。没关系,接下来我们就开始探索k8S网络模型吧!3.1Flannel网络模型上一章介绍了单机容器网络的原理。那么,要了解容器“跨主机通信”的原理,就要从Flannel这个项目说起。Flannel项目是CoreOS推出的容器网络解决方案。目前支持三种后端实现:UDPVXLANHost-gw3.1.1Flannel-UDPUPDmodeFlannel最早的实现,也是性能最差的,已经弃用。但是这种方式也是最直接、最容易理解的方式,所以我们就从这种方式入手。图2Flannel-UDP模式跨主机通信示意图图2是Flannel-UDP模式的示意图。与单机容器网络相比,这里新增了一个flannel0设备和flanneld进程。flannel0设备是一个TUN设备。它的功能很简单,就是在系统内核和用户应用程序之间传递数据包;flanneld进程的职责就是封装和解封装。数据包是如何从Node1中的container-1容器发送到Node2中的container-2容器的?数据包从container-1到达网桥docker0。由于数据包的目的地址不属于网桥的网段,所以数据包通过docker0网桥出现在宿主机上。在宿主机的路由表中,发往100.96.0.0/16网段的报文都是经过flannel0处理的。flannel0收到数据包后,将数据包发送给flanneld进程。flanneld进程将数据包封装成UDP数据包,src和dst地址为两个容器对应的hosts地址。这样数据包就可以到达Node2了。当数据包到达Node2的8285端口,也就是Node2上的flanneld进程,会进行解封装,然后将数据包发送给TUN设备,也就是flannel0设备。其余的很简单。数据包通过docker0网桥到达container-2。3.1.2Flannel-VXLAN经过上一节的介绍,大家对Flannel-UDP模式有了一个大概的了解,大家也已经猜到了。为什么不推荐使用Flannel-UDP?没错,因为效率太低了,每一个数据包经过flannel0设备,都会经历内核态-用户态-内核态。图3TUN设备示意图有没有办法不这么大惊小怪呢?是的,Flannel-VXLAN方案解决了这个问题。Flannel-VXLAN方案将flannel0设备替换为VXLAN技术,实现数据包在内核态的封装和解封装。图4Flannel-VXLAN网络模型示意图Flannel-VXLAN网络模型的原理如图4所示,你会发现它与Flannel-UDP基本相同。事实的确如此,Flannel-VXLAN是Flannel-UDP的升级版。这里需要说明一下它们之间的区别。Flannel-UDP的TUN设备flannel0升级为VXLAN的VTEP设备。数据包的封装和解封装都可以在内核态完成。在数据包的格式中,添加了VXLANHeader。这个Header的作用和Flannel-UDP数据包中的dport:8285是一样的。当数据包来到Node2时,操作系统可以根据VXLANHeader发送数据。包直接给flannel.1设备。3.1.3Flannel-host-gw这个时候,如果你聪明,肯定会说,虽然Flannel-VXLAN的效率提高了,但是还是用到了隧道技术,效率还是会受到影响。隧道技术不能用吗?答案是肯定的。接下来,我们继续探索Flannel-host-gw网络模型,一种基于三层的网络方案。老规矩,上图。图5Flannel-host-gw网络模型示意图图5是Flannel-host-gw网络模型。与前面两种网络模型相比,隧道设备确实没有了,取而代之的是一堆路由规则。那么,数据包是如何从container1到container2的呢?数据包从container1到达网桥后,通过Host网络栈的路由表,发现已经指定了到container2的路径,通过eth0到达Node2(10.168.0.3/24)即可。当数据包到达Node2时,通过Host网络栈的路由表找到了cni0网桥,自然就找到了container2。肉眼可以看出Flannel-host-gw的性能确实提升了不少,那为什么要用Flannel-VXLAN呢?原因很明显。flannel-host-gw只支持二层连接宿主机的网络,K8S的规模不能太大,否则每台机器的路由表太多。3.2Calico网络模型经过上一节的介绍,你应该对Flannel有了一个大概的了解。有人会问,除了Flannel,K8S还有其他网络模型吗?当然,让我们开始探索Calico网络模型。3.2.1Calico(非IPIP模式)Calico网络模型的解决方案其实和Flannel-host-gw差不多。不同的是,Flannel-host-gw使用etcd维护主机的路由表,而Calico使用BGP(边界网关协议)维护主机的路由表。BGP协议的定义似乎有点高级。通俗地说,你可以理解为每个边界网关都会运行一个小程序,它们会交换自己的路由信息??,将需要的信息更新到自己的路由表中。里面。BGP能力刚好可以替代Flannel-host-gw使用Etcd维护主机上路由表的功能,更加强大。除了BGP,Calico还有一个不同之处就是不需要维护网桥。Calico网络模型如图6所示:图6Calico网络模型图图6是Calico网络模型图,其中BGPClient和Felix的作用是与K8S集群的其他节点交换路由信息,并更新Host网络栈的路由信息??。由于没有桥接设备,因此需要在每一对设备的主机网络栈的这一端配置路由规则,将目的地址为对应Container的数据包转发给这对设备。对应路由如下:10.233.1.2devcali9c02e56scopelink包是如何从Container1到Container3的?流程和Flannel-host-gw基本一样。唯一不同的是,数据包进出容器,不再依赖网桥,而是直接通过主机路由表找到容器的另一端到设备。3.2.2Calico(IPIP模式)Calico听上去很强大,其实和Flannel-host-gw一样,只支持宿主机二层联通的情况。假设Container1和Container3的主机在不同的子网,数据包无法通过二层网络到达下一跳地址。如图7所示,Calico会在Node1上创建这样一条路由规则:10.233.2.0/16via192.168.2.2eth0此时问题来了,下一跳是192.168.2.2,与Node1不在同一个子网.只是找不到它。图7Calico(IPIP模式)网络模型图Calico的IPIP模式解决了以上问题。在每台主机上,都会增加一个tunl0设备(IP隧道设备),并相应增加如下路由策略:10.233.2.0/16via192.168.2.2tunl0这样就处理了Container1到Container3的数据包通过tunl0设备,tunl0设备将在源IP标头之外添加外部IP标头。本例中,外部IP头的src和dst分别是Node1和Node2的IP,这样数据包就伪装成了从Node1发送到Node2的数据包。当数据包到达Node2时,Node2上的tunl0会去掉外部IP头,得到原始IP包。我知道,聪明的你此时肯定会有更好的想法,为什么不在Router1和Router2上也使用BGP协议来同步容器的IP路由信息呢?这样,tunl0设备就可以在宿主机上使用了。这个方法确实不错,已经在一些场景中应用了。03CNI网络插件最后再介绍一下CNI网络插件。顾名思义,CNI(ContainerNetworkInterface)就是K8S的网络接口。该接口的作用是当K8Skubelet创建Pod时,dockershim会提前调用DockerAPI创建并启动Infra容器,并执行SetUpPod方法。该方法会为CNI网络插件准备参数和环境变量,然后调用CNI插件为Infra容器配置网络。CNI网络插件只需要实现ADD和DEL这两个方法,分别对应Pod加入K8S网络和Pod移出K8S网络。再用大白话解释一下,在创建Pod的时候,需要进行一些网络设置,包括上面提到的创建配对设备,将配对设备的一端挂载到桥上,添加Pod和NetworkNamespacehostRoutingrules等。当然这些操作可以由运维人员手动完成(如果不觉得累的话),CNI网络插件是一个自动配置网络的脚本。Flannel和Calico各自有专门的CNI插件,大家可以去Github研究一下源码,自己部署试试。这里就不多介绍了。毕竟,看再多的资料,也不如自己亲自动手去深入理解。
