六、kubernetes网络-linux网络虚拟化(二)

1.6 初识Linux隧道:ipip

前文介绍的tun设备也叫作点对点设备,之所以叫这个名字,是因为tun经常被用来做隧道通信(tunnel)。我们可以通过命令ip tunnel help查看IP隧道的相关操作。Linux原生支持下列5种L3隧道:·ipip:即IPv4 in IPv4,在IPv4报文的基础上封装一个IPv4报文;·GRE:即通用路由封装(Generic Routing Encapsulation),定义了在任意一种网络层协议上封装其他任意一种网络层协议的机制,适用于IPv4和IPv6;·sit:和ipip类似,不同的是sit用IPv4报文封装IPv6报文,即IPv6 over IPv4;·ISATAP:即站内自动隧道寻址协议(Intra-Site Automatic Tunnel Addressing Protocol),与sit类似,也用于IPv6的隧道封装;·VTI:即虚拟隧道接口(Virtual Tunnel Interface),是思科提出的一种IPSec隧道技术。下面我们以ipip为例,介绍Linux隧道通信的基本原理。

注:Linux L3隧道底层实现原理都基于tun设备,因此我们可以将本节看作tun设备的高级应用篇。

1.6.1 测试ipip隧道

要使用ipip隧道,首先需要内核模块ipip.ko的支持。通过lsmod|grep ipip查看内核是否加载,若没有则用modprobe ipip加载,正常加载应该显示:

加载ipip内核模块后,就可以创建隧道了。方法是先创建一个tun设备,然后将该tun设备绑定为一个ipip隧道。ipip隧道网络拓扑如图1-18所示。

创建两个network namespace:

创建两对veth pair,令其一端挂在某个namespace下:

分别给两对veth-pair端点配上IP并启用:

验证一下:v1 ping v2,结果为不通。检查ip_forward的值:

我们已经知道Linux本身就是一台路由器,Linux提供开关/proc/sys/net/ipv4/ip_forward来操作路由功能,默认这个开关是关的,打开只需: 

这种打开方式只是临时的,若想持久,可以修改配置文件/etc/sysctl.conf,添加或修改项net.ipv4.ip_forward为:

在我们的例子中,即使打开了ip_forward选项,v1到v2依然不通。查看ns1的路由表:

只有一条直连路由,没有通往10.10.20.0/24网段的路由,因此手动配置一条路由,如下所示:

再查看路由表:

同理,也给ns2配上通往10.10.10.0/24网段的路由。再ping一次,发现通了!

保证v1和v2能够通信后,再创建tun设备,并设置为ipip隧道。在ns1上创建tun1和ipip tunnel:

上面的命令是在ns1上创建tun设备tun1,并设置隧道模式为ipip,然后设置隧道端点,用remote和local表示,这是隧道外层IP。对应的还有隧道内层IP,用ip addr xx peer xx配置。从tun1发到tun2的原始IP报文和经过隧道封装后的IP报文如图1-19所示。

同理,我们也在ns2上创建tun2和ipip tunnel。

完成上述配置,两个tun设备端点就可以互通了,代码如下:

1.6.2 ipip隧道测试结果复盘 

 我们试着分析上面的实验过程:(1)ping命令构建一个ICMP请求,ICMP报文封装在IP报文中,源和目的IP地址分别是10.10.100.10和10.10.200.10。(2)由于tun1和tun2不在同一网段,所以要查看路由表。通过ip tunnel命令建立ipip隧道后,会自动生成一条路由,如下所示:

以上路由表信息表明:去往目的地10.10.200.10的报文直接从tun1出去了。(3)由于配置了隧道端点,数据包出了tun1,直接到达v1。根据ipip隧道的配置,会封装上一层新的IP头,源和目的IP地址分别为10.10.10.2和10.10.20.2。(4)由于v1和v2同样不在一个网段,查看路由表,发现去往10.10.20.0网段的报文从v1网卡出,去往10.10.10.1网关,即veth pair在主机上的另一端v1_p。(5)Linux打开了ip_forward,它相当于一台路由器,10.10.10.0和10.10.20.0是两条直连路由,所以直接查路由表转发,从这台主机转到另一台主机的v2_p上。(6)根据veth pair的设备特性,数据包到达另一台主机的v2_p上,会直接从ns2的v2出来。内核解封装数据包,发现内层IP报文的目的IP地址是10.10.200.10,这正是自己配置的ipip隧道的tun2地址,于是将报文交给tun2设备。至此,tun1的ping请求包成功到达tun2。 (7)由于ICMP报文的传输特性,有去必有回,所以ns2上会构造ICMP响应报文,并根据以上相同步骤封装和解封装数据包,直至到达tun1,整个ping过程完成。以上便是ipip隧道的通信过程,感兴趣的读者可以自行抓包验证。读者会发现有两层IP报文头,外层使用ipip协议构成隧道的端点,内层是正常的通信报文,封装了ICMP报文作为payload。

现在的Linux内核原生支持5种隧道协议,它们的底层实现都采用tun设备。我们熟知的各种VPN软件,其底层实现都离不开这5种隧道协议。其他隧道实现方式与ipip隧道的大同小异。

1.7 Linux隧道网络的代表:VXLAN

 VXLAN(Virtual eXtensible LAN,虚拟可扩展的局域网),是一种虚拟化隧道通信技术。它是一种overlay(覆盖网络)技术,通过三层的网络搭建虚拟的二层网络。RFC7348中是这样介绍VXLAN的:A framework for overlaying virtualized layer 2 networks over lay 3networks.简单来讲,VXLAN是在底层物理网络(underlay)之上使用隧道技术,依托UDP层构建的overlay的逻辑网络,使逻辑网络与物理网络解耦,实现灵活的组网需求。它不仅能适配虚拟机环境,还能用于容器环境。由此可见,VXLAN这类隧道网络的一个特点是对原有的网络架构影响小,不需要对原网络做任何改动,就可在原网络的基础上架设一层新的网络。不同于其他隧道协议,VXLAN是一个一对多的网络,并不仅是一对一的隧道协议。一个VXLAN设备能通过像网桥一样的学习方式学习到其他对端的IP地址,也可以直接配置静态转发表。一个典型的数据中心VXLAN网络拓扑图如图1-20所示。

图1-20中的VM指的是虚拟机,Hypervisor指的是节点的虚拟机管理器。VXLAN不仅能用在基于虚拟机的虚拟化系统中,还被广泛应用于容器集群。

1.7.1 为什么需要VXLAN

相信不少读者知道VLAN,VLAN技术的缺陷是VLAN Header预留的长度只有12bit,故最多只能支持2的12次方(4096)子网的划分,无法满足云计算场景下主机数量日益增长的需求。VXLAN能突破VLAN的最多4096个子网的数量限制,以满足大规模云计算数据中心的需求。具体来说,VXLAN技术解决以下几个问题:·当前VXLAN的报文Header内有24 bit,可以支持2的24次方个子网,并通过VNI(Virtual Network Identifier)区分不同的子网,相当于VLAN中的LAN ID;·多租网络隔离。越来越多的数据中心(尤其是公有云服务)需要提供多租户的功能,不同用户之间需要独立地分配IP和MAC地址。如何保证这个功能的扩展性和正确性是待解决的问题;·云计算业务对业务灵活性要求很高,虚拟机可能会大规模迁移,并保证网络一直可用,也就是大二层的概念。解决这个问题同时保证二层的广播域不会过分扩大,这也是云计算网络的要求。传统的二层或三层的网络在应对这些挑战时显得捉襟见肘,虽然很多改进型的技术能够增加二层的范围,但是要做到对网络改动尽量小的同时保证灵活性非常困难。

1.7.2 VXLAN协议原理简介

VXLAN隧道网络方案相比改造传统的二层或三层网络,对原有的网络架构影响小。隧道网络不需要原来的网络做任何改动,可直接在原来的网络基础上架设一层新的网络。图1-21所示为VXLAN的工作模型,它创建在原来的IP网络(三层)上,只要是三层可达(能够通过IP互相通信)的网络就能部署VXLAN。在VXLAN网络的每个端点都有一个VTEP设备,负责VXLAN协议报文的封包和解包,也就是在虚拟报文上封装VTEP通信的报文头部。物理网络上可以创建多个VXLAN网络,可以将这些VXLAN网络看作一个隧道,不同节点上的虚拟机/容器能够通过隧道直连。通过VNI标识不同的VXLAN网络,使得不同的VXLAN可以相互隔离。VXLAN的几个重要概念如下:·VTEP(VXLAN Tunnel Endpoints):VXLAN网络的边缘设备,用来进行VXLAN报文的处理(封包和解包)。VTEP可以是网络设备(例如交换机),也可以是一台机器(例如虚拟化集群中的宿主机); ·VNI(VXLAN Network Identifier):VNI是每个VXLAN的标识,是个24位整数,因此最大值是224=16777216。如果一个VNI对应一个租户,那么理论上VXLAN可以支撑千万级别的租户。·tunnel:隧道是一个逻辑上的概念,在VXLAN模型中并没有具体的物理实体相对应。隧道可以看作一种虚拟通道,VXLAN通信双方(图中的虚拟机)都认为自己在直接通信,并不知道底层网络的存在。从整体看,每个VXLAN网络像是为通信的虚拟机搭建了一个单独的通信通道,也就是隧道。

前文提到,VXLAN其实是在三层网络上构建出来的一个二层网络的隧道。VNI相同的机器逻辑上处于同一个二层网络中。VXLAN封包格式如图1-22所示。

VXLAN的报文就是MAC in UDP,即在三层网络的基础上构建一个虚拟的二层网络。为什么这么说呢?VXLAN的封包格式显示原来的二层以太网帧(包含MAC头部、IP头部和传输层头部的报文),被放在VXLAN包头里进行封装,再套到标准的UDP头部(UDP头部、IP头部和MAC头部),用来在底层网络上传输报文。可以看出,VXLAN报文比原始报文多出了50个字节,包括8个字节的VXLAN协议相关的部分,8个字节的UDP头部,20个字节的IP头部和14个字节的MAC头部。这降低了网络链路传输有效数据的比例,特别是对小包。需要注意的是,UDP目的端口是接收方VTEP设备使用的端口,IANA(Internet Assigned Numbers Authority,互联网号码分配局)分配了4789作为VXLAN的目的UDP端口。VXLAN的配置管理使用iproute2包,这个工具是和VXLAN一起合入内核的,我们常用的ip命令就是iproute2的客户端工具。VXLAN要求Linux内核版本在3.7以上,最好为3.9以上,所以在一些旧版本的Linux上无法使用基于VXLAN的封包技术。

1.7.3 VXLAN组网必要信息

总的来说,VXLAN报文的转发过程就是:原始报文经过VTEP,被Linux内核添加上VXLAN包头及外层的UDP头部,再发送出去,对端VTEP接收到VXLAN报文后拆除外层UDP头部,并根据VXLAN头部的VNI把原始报文发送到目的服务器。以上过程看似并不复杂,但前提是通信双方已经知道所有通信信息。第一次通信之前有以下问题待解决:·哪些VTEP需要加到一个相同的VNI组?·发送方如何知道对方的MAC地址?·如何知道目的服务器在哪个节点上?第一个问题最好回答,VTEP通常由网络管理员进行配置。另外两个问题可以归结为同一个问题:VXLAN网络的通信双方如何感知彼此并选择正确的路径传输报文?要回答这两个问题,还得回到VXLAN协议报文上,看看一个完整的VXLAN报文需要哪些信息。·内层报文:通信双方的IP地址已经明确,需要VXLAN填充的是对方的MAC地址,VXLAN需要一个机制来实现ARP的功能;·VXLAN头部:只需要知道VNI。它一般是直接配置在VTEP上的,即要么是提前规划的,要么是根据内部报文自动生成的;·UDP头部:最重要的是源地址和目的地址的端口,源地址端口是由系统生成并管理的,目的端口一般固定为IANA分配的4789端口;·IP头部:IP头部关心的是对端VTEP的IP地址,源地址可以用很简单的方式确定,目的地址是虚拟机所在地址宿主机VTEP的IP地址,需要由某种方式来确定;·MAC头部:确定了VTEP的IP地址,MAC地址可以通过经典的ARP方式获取,毕竟VTEP在同一个三层网络内。总结一下,一个VXLAN报文需要确定两个地址信息:内层报文(对应目的虚拟机/容器)的MAC地址和外层报文(对应目的虚拟机/容器所在宿主机上的VTEP)IP地址。如果VNI也是动态感知的,那么VXLAN一共需要知道三个信息:内部MAC、VTEP IP和VNI。

一般有两种方式获得以上VXLAN网络的必要信息:多播和控制中心。多播的概念是同一个VXLAN网络的VTEP加入同一个多播网络。如果需要知道以上信息,就在组内发送多播来查询。控制中心的概念是在某个集中式的地方保存所有虚拟机的上述信息,自动告知VTEP它需要的信息即可。

1.7.4 VXLAN基本配置命令

VXLAN接口的基本管理如下。

1. 创建VXLAN接口

这条命令会创建一个叫作VXLAN0的新接口,它使用在eth0上的多播组239.1.1.1通信。初始化时没有转发表。目的端口号是IANA规定的4789。在VXLAN中,一般将VXLAN接口(在我们的例子中即vxlan0)叫作VTEP(VXLAN tunnel endpoint),VXLAN子网的报文需要从VTEP出去。多播组主要通过ARP洪泛来学习MAC地址,即在VXLAN子网内广播ARP请求,然后对应节点进行响应。但多播不是VXLAN所必需的,下文会进行解释。如果网络不复杂,则可以认为某一节点上所有子网IP的MAC和节点上的VTEP的MAC一致,直接用VTEP MAC封装报文。至于VTEP的MAC地址,可以用bridge命令手工配置。

2. 删除VXLAN接口与普通网络接口一样,ip link delete命令就能删除一个VXLAN接口: 

3. 查看VXLAN接口信息常规命令ip-d link show可以查看VXLAN网卡信息:

4. VXLAN转发表可以使用bridge命令创建、删除或查看VXLAN接口的转发表。创建一条转发表项:

00:17:42:8a:b4:05即对端VTEP的MAC地址,192.19.0.2即对端VTEP的IP地址。删除一条转发表项:

查看VXLAN接口的转发表:

注:网络设备都以MAC地址唯一地标识自己,而交换机要实现设备之间的通信就必须知道自己的哪个端口连接着哪台设备,因此就需要一张MAC地址与端口号一一对应的表,以便在交换机内部实现二层数据转发。这张二层转发表就是FDB表。它主要由MAC地址、VLAN号、端口号和一些标志域等信息组成。如果收到数据帧的目的MAC地址不在FDB地址表中,那么该数据将被发送给除源端口外,该数据包所属VLAN中的其他所有端口。把数据发给其他所有端口的行为称为洪泛。那么,FDB表是怎么形成的呢?很简单,交换机会在收到数据帧时,提取数据帧中的源MAC、VLAN和接收数据帧的端口,组成FDB表的条目。当下次看到相同VLAN时,相同MAC地址的报文就直接从记录的端口“丢”出去。

1.7.5 VXLAN网络实践 

了解了VXLAN必要的背景知识后,我们将通过几个例子说明如何搭建基于VXLAN的overlay网络,顺便阐述上文提到的多播和控制中心这两种方式的原理。

1. 点对点的VXLAN让我们先从最简单的点对点的VXLAN网络说起。点对点VXLAN即两台机器构成一个VXLAN网络,每台机器上有一个VTEP,VTEP之间通过它们的IP地址进行通信。点对点VXLAN网络拓扑如图1-23所示。

首先,使用ip link命令创建VXLAN接口:

上面这条命令创建了一个名为vxlan0,类型为vxlan的网络接口,一些重要的参数如下:·id 42:指定VNI的值,有效值在1到224之间;·dstport:VTEP通信的端口,IANA分配的端口是4789。如果不指定,Linux默认使用8472;·remote 192.168.1.3:对端VTEP的地址;·local 192.168.1.2:当前节点VTEP要使用的IP地址,即当前节点隧道口的IP地址;·dev eth0:当前节点用于VTEP通信的网卡设备,用来获取VTEP IP地址。注意,这个参数和local参数含义相同,读者在使用过程中二选一即可。执行完之后,系统就会创建一个名字为vxlan0的网卡,可以用ip-d link命令查看它的详细信息:

接下来,为刚创建的VXLAN网卡配置IP地址并启用它:

执行成功后会发现路由表项多了下面的内容,所有目的地址是172.17.1.0/24网段的包要通过vxlan0转发:

同时,vxlan0的FDB表项中的内容如下:

这个表项的意思是,默认的VTEP对端地址为192.168.1.3。换句话说,原始报文经过vxlan0后会被内核添加上VXLAN头部,而外部UDP头的目的IP地址会被冠上192.168.1.3。在另外一台机器上(192.168.1.3)也进行相同的配置,要保证VNI也是42,dstport也是4789,并修改VTEP的local和remote IP地址到相应的值。测试两个VTEP的连通性,如下所示:

通过点对点组网模型实践了VXLAN网络的基本要点,下面介绍多节点怎么组成VXLAN网络进行通信。

2. 多播模式的VXLAN要组成同一个VXLAN网络,VTEP必须能感知到彼此的存在。多播组本来的功能就是把网络中的某些节点组成一个虚拟的组,所以VXLAN最初想到用多播来实现是很自然的事情。注:如果VXLAN要使用多播模式,那么底层的网络结构需要支持多播的功能。这个实验和前面一个非常相似,只不过主机之间不是点对点的连接,而是通过多播组成一个虚拟的整体。多播模式的VXLAN网络拓扑如图1-24所示。

虽然加入同一个多播组听起来挺复杂,但实际上使用比较简单,和点对点模型相比就多了group参数,命令如下:

同样,为刚创建的VXLAN网卡配置IP地址并启用它:

这里最重要的参数是group 224.1.1.1,它表示将VTEP加入一个多播组,多播地址是224.1.1.1。注:多播地址让源设备能够将分组发送给一组设备。属于多播组的设备将被分配一个多播组IP地址,多播地址范围为224.0.0.0~239.255.255.255。运行上面的命令之后,一样添加了以下路由信息:

不同的是FDB表项的内容:

dst字段的值变成了多播地址224.1.1.1,而不是之前对方的VTEP地址,意思是原始报文经过vxlan0后被内核添加上VXLAN头部,其外部UDP头的目的IP地址会被冠上多播地址224.1.1.1。同理,对所有需要通信的节点进行上述配置,可以验证它们能否通过172.17.1.0/24网络互相通信。配置完成之后,VTEP通过IGMP加入同一个多播组224.1.1.1。我们来分析多播模式下VXLAN通信的全过程:

(1)主机1的vxlan0发送ping报文到主机2的172.17.1.3地址,内核发现源地址和目的IP地址在同一个局域网内。需要知道对方的MAC地址,而本地又没有缓存,故先发送一个ARP查询报文。

(2)ARP报文源MAC地址为主机1上vxlan0的MAC地址,目的MAC地址为255.255.255.255(广播地址),并根据配置添加VXLAN头部VNI=42。

(3)不知道对端VTEP在哪台主机上但又配置了多播组,根据配置,VTEP会往多播地址239.1.1.1发送多播报文。

(4)多播组中所有的主机都会收到这个报文,内核发现是VXLAN报文,根据VNI发送给对应的VTEP。

(5)主机2的VTEP去掉VXLAN头部,取出真正的ARP请求报文。同时,VTEP会记录源MAC地址和IP地址信息到FDB表中,这便是一次学习过程。如果发现ARP不是发送给自己的,则直接丢弃;如果是发送给自己的,则生成ARP应答报文。

(6)应答报文目的MAC地址是发送方VTEP的MAC地址,不需要多播。对端VTEP已经通过源报文学习到了VTEP所在主机的MAC地址,故会直接单播发送给目的VTEP。

(7)应答报文通过底层网络直接返回发送方主机,发送方主机根据VNI把报文转发给VTEP,VTEP解包取出ARP应答报文,添加到VTEP缓存中,并根据报文学习到目的VTEP所在的主机地址,添加到FDB表中。

(8)VTEP双方(隧道网络双方)已经通过一次ARP报文知道了建立ICMP通信需要的所有信息,因此后续的ICMP报文都是在这条逻辑隧道中单播进行的。总结以上过程:一个VXLAN网络的ping报文要经历ARP寻址+ICMP响应两个过程。当然,VTEP设备学习到对方ARP地址后就可以免去ARP寻址的过程。

3. VXLAN+桥接网络

尽管上面的方法能够通过多播实现自动化的overlay网络构建,但是通信的双方只有一个VTEP。在实际生产中,每台主机上都有几十台甚至上百台虚拟机或者容器需要通信,因此需要找到一种方法将这些通信实体组织起来,再经过隧道口VTEP转发出去。通过前面的介绍我们知道,Linux网桥可以连接多块虚拟网卡,因此可以使用网桥把多个虚拟机或者容器放到同一个VXLAN网络中,VXLAN+网桥的网络拓扑如图1-25所示。

和上面的多播模式相比,这里只是多了一块网桥,连接同一个主机上的不同容器的veth pair。我们还没有介绍如何创建容器,因此用前面介绍的network namespace代替容器,其实原理都是一样的。我们将创建一个network namespace,并通过一对veth pair将namespace中的eth0网卡连接到网桥。同时,VXLAN网卡也连接到网桥。首先,创建VXLAN网卡,使用的是多播模式:

然后创建网桥bridge0,把VXLAN网卡vxlan0绑定到上面,并启动它们:

下面创建network namespace和一对veth pair,并把veth pair的其中一端绑定到网桥,然后把另一端放到network namespace并绑定IP地址172.17.1.2:

用同样的方法在另一台主机上配置VXLAN网络,绑定172.17.1.3到另外一个network namespace中的eth0。从172.17.1.2 ping 172.17.1.3发现整个通信过程和前面的实验类似,只不过容器发出的ARP报文会先经过网桥,再转发给vxlan0;然后在vxlan0处由Linux内核添加VXLAN头部;最后通过多播的方式查询通信对端的MAC地址。逻辑上,VXLAN网络下不同主机上的network namespace中的网卡被连接到了同一个网桥上。

1.7.6 分布式控制中心 

因为并不是所有的网络设备都支持多播,再加上多播方式带来的报文浪费,在实际生产中VXLAN的多播模式很少被采用。我们来回顾为什么要引入多播。从多播的流程可以看出,其实隧道网络发送报文最关键的就是要知道对方虚拟机/容器的MAC地址及所在主机的VTEP IP地址。对overlay网络来说,它的网段范围是分布在多个主机上的,因此传统ARP报文的广播无法直接使用。要想做到overlay网络的广播,必须把报文发送到所有VTEP在的节点,这才使用了多播。如果能够事先知道MAC地址和VTEP IP信息,直接告诉发送方VTEP,就不需要多播了。在虚拟机和容器的场景中,当虚拟机或者容器还没有进行网络通信时,我们就可以知道它的IP和MAC(可能是用某种方式获取,也可能是事先控制这两个地址),分布式控制中心保存了这些信息。除此之外,控制中心还保存了每个VXLAN网络的VTEP,以及这些VTEP的地址。有了这些信息,VTEP就能在发送报文时直接查询并添加头部,不需要多播去满网络地“问”。在分布式控制中心,一般情况下,这种架构在每个VTEP所在的节点都运行一个agent,它会和控制中心通信,获取隧道通信需要的信息并以某种方式告诉VTEP。下面我们用一个手动配置VTEP组和FDB表的例子结束本节的讨论。

1.7.7 自维护VTEP组

如果有一种方法能够不通过多播,把overlay的广播报文发送给所有的VTEP主机,则也能完成相同的功能。当然,在维护VTEP网络组之前,必须提前知道哪些VTEP要组成一个网络,以及这些VTEP在哪些主机上。在我们创建VXLAN网卡时,不使用remote或group参数就能达成以上目的,例如:

这个VTEP创建时没有指定多播地址,当第一个ARP请求报文发送时,它也不知道要发送给谁,但是我们可以手动添加默认的FDB表项,例如:

如果不知道对方VTEP的地址,内核就会选择默认的FDB表项将网络包发送到192.168.8.101和192.168.8.102,相当于手动维护了一个VTEP的多播组。在所有节点的VTEP上更新对应的FDB表项,就能实现overlay网络的连通。整个通信流程和多播模式相同,唯一的区别是,VTEP第一次通信时会给所有组内成员发送单播报文,当然也只有一个VTEP会做出应答。使用一些自动化工具,定时更新FDB表项,就能动态地维护VTEP的拓扑结构。这个方案解决了在某些underlay网络中不能使用多播的问题,但并没有解决多播的另外一个问题:每次查找MAC地址要发送大量的无用报文,如果VTEP组节点数量很大,那么每次查询都发送N个报文,其中只有一个报文真正有用。

手动维护FDB表项

如果提前知道目的容器的MAC地址和它所在主机的IP地址,则可以通过更新FDB表项来减少广播的报文数量。这次我们在创建VXLAN设备时添加了nolearning参数,这个参数告诉VTEP不要通过收到的报文学习FDB表项的内容,因为我们自己会进行维护:

然后,添加FDB表项,告诉VTEP容器/虚拟机MAC地址与对应主机IP地址的映射关系:

如果知道了对方的MAC地址,则VTEP搜索FDB表项就知道应该发送到哪个对应的VTEP了。需要注意的是,这个情况还是需要默认的表项(那些全零的表项),在不知道容器IP和MAC的对应关系时,通过默认方式发送ARP报文查询对方的MAC地址。和上一个方法相比,这个方法并没有任何效率上的提升,只是把自动学习FDB表项换成了手动维护(当然,实际情况一般是由自动化程序来维护),第一次发送ARP请求还是会往VTEP组发送大量单播报文。这个方法给了我们很重要的提示:如果事先知道VXLAN网络的信息,则VTEP需要的信息都是可以自动维护的,不需要学习。

手动维护ARP表项

除了FDB表项,ARP表项也是可以维护的。如果能通过某个方式知道容器的IP和MAC地址的对应关系,只要更新到每个节点,就能实现网络的连通。这里有个问题,我们需要维护的是每个容器里的ARP表项,因为最终通信的双方是容器。到每个容器里(所有的network namespace)更新对应的ARP表,是一件工作量很大的事情,而且容器的创建和删除还是动态的。Linux提供了一个解决方案,使VTEP可以作为ARP代理,回复ARP请求。也就是说,只要VTEP知道对应的IP地址和MAC地址的映射关系,在接收到容器发来的ARP请求时就可以直接做出应答。这样的话,我们只需要更新VTEP上的ARP表项就行了。下面这条命令和上面的相比多了proxy参数,这个参数告诉VTEP承担ARP代理的功能,即如果收到ARP请求并且知道结果就直接做出应答。

当然,我们还是要手动更新FDB表项来构建VTEP组:

还需要为VTEP添加ARP表项。所有要通信容器的IP地址和MAC地址的映射关系都要加进去:

在要通信的所有节点配置完之后,容器就能互相ping通。当容器要访问彼此,并且第一次发送ARP请求时,这个请求并不会发给所有的VTEP,而是由当前VTEP做出应答,大大减少了网络上的报文。借助自动化的工具做到实时的表项(FDB和ARP)更新,这种方法能高效实现overlay网络的通信。

动态更新FDB和ARP表项

尽管前一种方法通过动态更新FDB和ARP表项避免了多余的网络报文,但还有一个问题:为了让所有的容器能够正常通信,必须提前添加所有容器到ARP和FDB表项中。因为并不是所有的容器都会互相通信,所以添加的有些表项(尤其是ARP表项)是用不到的。Linux提供了另外一种方法,使内核能够动态地通知节点要和哪个容器通信,应用程序可以订阅这些事件。如果内核发现需要的ARP或者FDB表项不存在,则会发送事件给订阅的应用程序,这样应用程序可以从内核拿到这些信息更新表项,做到更精确的控制。要收到L2(FDB)miss,必须满足几个条件:·目的MAC地址未知,即没有对应的FDB表项;·FDB中没有全零的表项,即默认规则;·目的MAC地址不是多播或者广播地址。要实现这种功能,创建VTEP的时候需要加上额外的参数:

这次多了两个参数l2miss和l3miss:·l2miss:如果设备找不到MAC地址需要的VTEP地址,就发送通知事件;·l3miss:如果设备找不到需要IP对应的MAC地址,就发送通知事件。ip monitor命令能监听某个网卡的事件:

如果从本节点的容器ping另外一个节点的容器,就先发送L3 miss,下面就是一个L3 miss的通知事件:

l3miss说明了VTEP不知道它对应的MAC地址,因此要手动添加ARP记录,如下所示:

上面这条命令设置的nud reachable参数的意思是,这条记录有一个超时时间,系统发现它无效一段时间后会自动删除,无须用户手动删除。ARP记录被删除后,当需要通信时,内核会再次发送L3 miss通知事件。nud是Neighbour Unreachability Detection的缩写,根据需要这个参数也可以设置成其他值,比如permanent,表示这个记录永远不会过时,系统不会检查它是否正确,也不会删除它。这时还是不能正常通信,内核出现了L2 miss的通知事件:

类似地,这个事件的含义是不知道这个容器的MAC地址在哪个节点上,所以要手动添加FDB记录:

在通信的另一台机器上执行响应的操作,就会发现两者能ping通。

1.7.8 小结

VXLAN协议给虚拟网络带来了灵活性和扩展性,让云计算网络能够像计算、存储资源那样按需扩展,并灵活分布。与此同时,VXLAN也带来了额外的复杂度和性能开销。性能开销主要包含两个方面:·每个VXLAN报文都有额外的50字节开销,加上VXLAN字段,开销要到54字节。这对于小报文的传输是非常昂贵的操作。试想,如果某个报文应用数据才几个字节,那么原来的网络头部加上VXLAN报文头部就有100字节的控制信息;·每个VXLAN报文的封包和解包操作都是必需的,如果用软件来实现这些步骤,则额外的计算量是不可忽略的影响因素。多播实现很简单,不需要中心化的控制。不是所有的网络都支持多播,需要事先规划多播组和VNI的对应关系,在overlay网络数量比较多时也会很麻烦,多播会导致大量的无用报文出现在网络中。很多云计算的网络通过自动化的方式发现VTEP和MAC地址等信息,避免多播。参考资料:https://cizixs.com/2017/09/28/linux-vxlan/.

1.8 物理网卡的分身术:Macvlan 

最早版本的Docker是不支持容器网络与宿主机网络直接互通的。从Docker 1.12版本开始,为了解决容器的跨机通信问题引入了overlay和Macvlan网络,其中Macvlan支持容器之间使用宿主机所在网段资源。在介绍Kubernetes网络插件的章节会详细讨论overlay网络,本节将重点介绍Macvlan。除了在容器中有着广泛应用,Macvlan/Macvtap设备也大量应用在虚拟机场景。

1.8.1 Macvlan五大工作模式解析

通常,我们在自定义Docker与外部网络通信的网络时会用到NAT,还有Linux bridge、Open vSwitch、Macvlan几种选择,相比之下,Macvlan拥有更好的性能。在Macvlan出现之前,我们可以通过网卡别名(例如eth0:1)的方式为一块以太网卡添加多个IP地址,却不能为其添加多个MAC地址。原因是以太网卡是以MAC地址为唯一识别的,而网卡别名并没有改变这些网卡的MAC地址。Macvlan接口可以看作是物理以太网接口的虚拟子接口。Macvlan允许用户在主机的一个网络接口上配置多个虚拟的网络接口,每个Macvlan接口都有自己的区别于父接口的MAC地址,并且可以像普通网络接口一样分配IP地址。因此,使用Macvlan技术带来的效果是一块物理网卡上可以绑定多个IP地址,每个IP地址都有自己的MAC地址。

用Macvlan虚拟出来的虚拟网卡,在逻辑上和物理网卡是对等的,应用程序可以像使用物理网卡的IP地址那样使用Macvlan设备的IP地址。Macvlan所连接的物理接口通常称为“下部设备”或“上部设备”,方便起见,且与下文要介绍的IPvlan保持一致,称物理接口为父接口,Macvlan设备为子接口。有时,我们也形象地称Macvlan“寄生”在宿主机的物理网卡上。Macvlan的主要用途是网络虚拟化(包括容器和虚拟机)。另外,有一些比较特殊的场景,例如,keepalived使用虚拟MAC地址。需要注意的是,使用Macvlan的虚拟机或者容器网络与主机在同一个网段,即同一个广播域中。Macvlan支持5种模式,分别是bridge、VEPA、Private、Passthru和Source模式。

1. bridge模式

该模式类似Linux bridge,是Macvlan最常用的模式,比较适合共享同一个父接口的Macvlan网卡进行直接通信的场景。在bridge模式下,拥有相同父接口的两块Macvlan虚拟网卡可以直接通信,不需要把流量通过父接口发送到外部网络,广播帧将会被洪泛到连接在“网桥”上的所有其他子接口和物理接口。网桥带双引号是因为实际上并没有网桥实体的产生,而是指在这些网卡之间数据流可以实现直接转发,这有点类似于Linux网桥。但Macvlan的bridge模式和Linux网桥不是一回事,它不需要学习MAC地址,也不需要生成树协议(STP),因此性能要优于Linux网桥。Macvlan bridge模式如图1-26所示。

bridge模式的缺点是如果父接口故障,所有Macvlan子接口会跟着故障,子接口之间也将无法进行通信。

2. VEPA模式

VEPA(Virtual Ethernet Port Aggregator,虚拟以太网端口聚合)是默认模式。所有从Macvlan接口发出的流量,不管目的地址是什么,全部“一股脑”地发送给父接口——即使流量的目的地是共享同一个父接口的其他Macvlan接口。在二层网络下,由于生成树协议的原因,两个Macvlan接口之间的通信会被阻塞,这时就需要接入的外部交换机支持hairpin,把源和目的地址都是本地Macvlan接口地址的流量,发给相应的接口。在VEPA模式下,从父接口收到的广播包会洪泛给所有的子接口。Macvlan VEPA模式如图1-27所示。

目前,大多数交换机都不支持hairpin模式,但Linux可以通过一种hairpin模式的网桥,让VEPA模式下的Macvlan接口能够直接通信(前文已经提到,我们可以把Linux网桥看作二层交换机)。接下来,配置Linux网桥某个端口的hairpin模式:

以上命令的作用是配置Linux网桥br0,使得从eth0收到包后再从eth0发送出去。以上brctl haripin子命令原型是:

或者使用iproute2直接设置网卡的hairpin模式:

也可以通过写sysfs目录下的设备文件设置网桥某个端口的hairpin模式。下面的例子设置了网桥br0的eth1端口的hairpin:

配置了hairpin后,源地址和目的地址都是本地Macvlan接口地址的流量,会被Linux网桥发回给相应的接口。如果想在物理交换机层面对虚拟机或容器之间的访问流量进行优化设定,VEPA模式是一种比较好的选择。

3. Private模式

Private模式类似于VEPA模式,但又增强了VEPA模式的隔离能力,其完全阻止共享同一父接口的Macvlan虚拟网卡之间的通信。即使配置了hairpin,让从父接口发出的流量返回宿主机,相应的通信流量依然被丢弃。Macvlan Priviate模式如图1-28所示。

Private的具体实现方式是丢弃广播/多播数据,这就意味着以太网地址解析ARP将无法工作。除非手工探测MAC地址,否则通信将无法在同一宿主机下的多个Macvlan网卡间进行。如果需要Macvlan的隔离功能,那么Private模式会非常有用。

4. Passthru模式

Passthru模式翻译过来就是直通模式。在这种模式下,每个父接口只能和一个Macvlan网卡捆绑,并且Macvlan网卡继承父接口的MAC地址。Macvlan Passthru模式如图1-29所示。

在VEPA和Passthru模式下,两个Macvlan网卡之间的通信会经过父接口两次——第一次是发出的时候,第二次是返回的时候。除了限制了Macvlan接口之间的通信性能,还影响物理接口的宽带。

5. Source模式

在这种模式下,寄生在物理设备上,Macvlan设备只接收指定的源Mac地址的数据包,其他数据包一概丢弃。

1.8.2 测试使用Macvlan设备 

在宿主机上创建Macvlan设备:

 

查看该Macvlan网卡的详细信息: 

这时,Macvlan网卡eth0.1的设备状态为DOWN,启用它:

一般情况下,Macvlan设备的MAC地址是Linux系统自动分配的,用户也可以自定义。使用以下命令就可在新建Macvlan网卡的同时指定其MAC地址:

如果要删除Macvlan网卡,则只需简单调用以下命令:

1.8.3 Macvlan的跨机通信 

我们的实验环境包含两个节点,分别是A节点,IP地址为192.168.1.2;B节点,IP地址为192.168.1.3。直接使用Docker容器进行下面的实验。如果读者对Docker还不熟悉,则可以阅读第2章的内容提前获取必要的背景知识。先在A节点上创建一个不带网络初始化的Docker容器:

同时,获取新创建容器对应的PID:

然后创建Macvlan设备:

再将Macvlan设备eth0.1放入容器的网络namespace中:

进入网络namespace中配置Macvlan网卡:

如上所示,Macvlan设备启用后会自动分配MAC地址而没有IP(v4)地址。我们为其分配IP地址并设置网关。现在,我们可以从主机B上访问容器test1,如下所示:

从主机A上ping容器test1,发现不通:

这是什么原因呢?在Macvlan虚拟网络世界中,物理网卡(父接口)相当于一个交换机,对于进出其子Macvlan网卡的数据包,物理网卡只转发数据包而不处理数据包,于是也就造成了使用本机Macvlan网卡的IP无法和物理网卡的IP通信。总结,Macvlan只为虚拟机或容器提供访问外部物理网络的连接。

1.8.4 Macvlan与overlay对比 

Macvlan和overlay都是Docker原生提供的网络方案,但是Macvlan和overlay网络的工作方式有所不同。首先,Macvlan和overlay的网络作用范围不一样。overlay是全局作用范围类型的网络,而Macvlan的作用范围只是本地。举例来说,全局类型的网络可以作用于一个Docker集群,而本地类型的网络只作用于一个Docker节点。其次,每个宿主机创建的Macvlan网络都是独立的,一台机器上创建的Macvlan网络并不影响另一台机器上的网络。但这并不意味着两台Macvlan的主机不能通信,当同时满足以下两个条件时,可以实现Macvlan网络的跨主机通信:

(1)通信两端的主机在网卡配置混杂模式。(2)两台主机上的Macvlan子网IP段没有重叠。

1.8.5 小结

Macvlan是将虚拟机或容器通过二层连接到物理网络的一个不错的方案,但它也有一些局限性,例如:·因为每个虚拟网卡都要有自己的MAC地址,所以Macvlan需要大量的MAC地址,而Linux主机连接的交换机可能会限制一个物理端口的MAC地址数量上限,而且许多物理网卡的MAC地址数量也有限制,超过这个限制就会影响到系统的性能;·IEEE 802.11标准(即无线网络)不喜欢同一个客户端上有多个MAC地址,这意味着你的Macvlan子接口没法在无线网卡上通信。我们可以通过复杂的办法突破以上这些限制,但还有一种更简单的办法。那就是使用IPvlan。 

1.9 Macvlan的救护员:IPvlan

 前文我们详细讨论了Macvlan,而且总结了Macvlan的一些局限性,也提到了IPvlan可以解决Macvlan的一些限制,因此本节将介绍IPvlan。Macvlan和IPvlan虚拟网络模型提供的功能看起来差不多,那么,什么时候需要用到IPvlan呢?要回答这个问题,先来看看Macvlan先天存在的不足:·无法支持大量的MAC地址;·无法工作在无线网络环境中。

1.9.1 IPvlan简介

 与Macvlan类似,IPvlan也是从一个主机接口虚拟出多个虚拟网络接口。区别在于IPvlan所有的虚拟接口都有相同的MAC地址,而IP地址却各不相同。因为所有的IPvlan虚拟接口共享MAC地址,所以特别需要注意DHCP使用的场景。DHCP分配IP地址的时候一般会用MAC地址作为机器的标识。因此,在使用Macvlan的情况下,客户端动态获取IP的时候需要配置唯一的Client ID,并且DHCP服务器也要使用该字段作为机器标识,而不是使用MAC地址。Linux内核3.19版本才开始支持IPvlan,Docker从4.2版本起能够稳定支持IPvlan。IPvlan有两种不同的模式,分别是L2和L3。一个父接口只能选择其中一种模式,依附于它的所有子虚拟接口都运行在该模式下。

1. L2模式

IPvlan L2模式和Macvlan bridge模式的工作原理很相似,父接口作为交换机转发子接口的数据。同一个网络的子接口可以通过父接口转发数据,如果想发送到其他网络,则报文会通过父接口的路由转发出去。

2. L3模式

L3模式下,IPvlan有点像路由器的功能。如图1-30所示,IPvlan在各个虚拟网络和主机网络之间进行不同网络报文的路由转发工作。只要父接口相同,即使虚拟机/容器不在同一个网络,也可以互相ping通对方,因为IPvlan会在中间做报文的转发工作。

L3模式下的虚拟接口不会接收多播或者广播的报文,原因是所有的网络都会发送给父接口,所有的ARP过程或者其他多播报文都是在底层的父接口完成的。需要注意的是,外部网络默认情况下是不知道IPvlan虚拟出来的网络的,如果不在外部路由器上配置好对应的路由规则,那么IPvlan的网络是不能被外部直接访问的。

1.9.2 测试IPvlan 

先创建两个测试用的network namespace:

然后创建IPvlan的虚拟网卡接口,因为L2和Macvlan功能相同,所以这里测试L3模式。创建IPvlan虚拟接口的命令和Macvlan格式相同:

把IPvlan接口放到前面创建好的network namespace中:

给两个虚拟网卡接口配置不同网络IP地址,并配置好路由项:

最后测试两个网络的连通性:

1.9.3 Docker IPvlan网络 

从Docker 1.13版本开始加入了对IPvlan的支持,测试过程如下所示。读者可以先阅读第2章,获得关于Docker的背景知识。首先,创建IPvlan的网络,这需要在创建Docker网络时把网络驱动(-d)设置成ipvlan,同时设置IPvlan的工作模式为L3:

然后,启动两个容器,发现在同一个IPvlan网络的两个容器可以互相ping通:

再创建另外一个IPvlan网络,和前面的网络不在同一个广播域:

最后,在新建的网络中运行容器,发现可以ping通前面网络中的容器:

1.9.4 小结

我们将IPvlan称为Macvlan的“救护员”是因为IPvlan除了能够完美解决以上问题,还允许用户基于IPvlan搭建比较复杂的网络拓扑,不再基于Macvlan的简单的二层网络,而是能够与BGP(Boader Gateway Protocol,边界网关协议)等协议扩展我们的网络边界。 

 

posted @ 2023-06-25 15:13  good-good-luck  阅读(591)  评论(0)    收藏  举报