Linux开源网络:网络虚拟化

Linux 网络虚拟化的主要技术是网络命名空间以及各类虚拟网络设备。例如要介绍的 veth、Linux bridge、tun/tap 等,这些虚拟网络设备模拟了物理设备的功能,但完全在内核层面由代码实现。容器网络正是基于这些虚拟网络设备,模拟现实世界中的物理设备彼此协作,将各个独立的网络命名空间连接起来,构建出不受物理环境约束的容器之间、容器与宿主机之间乃至跨数据中心的动态网络拓扑架构。

部分内容来源于《Linux开源网络全栈详解:从DPDK到OpenFlow》

网络命名空间

Linux 内核处于对资源隔离的需要,从 2.4.19 版本起,它开始逐步集成多种命名空间技术,以实现对各类资源的隔离。网络命名空间(Network Namespace)是其中最关键的一种,它是各类容器化技术的核心。

有了网络命名空间技术,Linux 系统便能够在一个主机内创建多个独立的网络环境。每个网络命名空间都拥有自己独立的网络资源,包括防火墙规则、网络接口、路由表、ARP 邻居表以及完整的网络协议栈。当一个进程运行在网络命名空间内时,感觉就像独享一台物理主机一样。

在这里插入图片描述

Linux ip 工具的子命令 netns 集成了网络命名空间的增删查改功能。以下使用 ip 命令演示操作网络命名空间,加深理解网络命名空间的隔离性。

首先,创建一个名为 ns1 的网络命名空间。命令如下所示:

$ ip netns add ns1

查询该网络命名空间内的网络设备信息。由于没有经过任何配置,因此该网络命名空间内只有一个名为 lo 的本地回环设备,且该设备的状态为 DOWN。

$ ip netns exec ns1 ip link list 
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

继续查看该网络命名空间下 iptables 规则配置。由于这是一个初始化的网络命名空间,因此它也并没有任何 iptables 规则。

$ ip netns exec ns1 iptables -L -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination 

由于这些命名空间是相互隔离的,它们不能直接相互通信或与宿主机通信。因此,为了实现不同网络命名空间之间或与外界的通信,我们需要创建一些虚拟网络设备和虚拟网络拓扑结构。

一台物理机通过网卡连接到以太网交换机,加入局域网,从而可以与其他设备(如其他计算机、服务器等)进行通信。网络命名空间的通信原理类似于物理机的网络配置,但完全是在虚拟环境中实现的。

下文会介绍实现网络命名空间通信的方法。

虚拟网络设备 tun 和 tap

tun 和 tap 是Linux 内核 2.4.x 版本之后引入的虚拟网卡设备,是一种让用户空间可以和内核空间双向传输数据包的虚拟网络设备。这两种设备的区别与含义为:

  • tun 设备是一个三层网络层设备,从 /dev/net/tun 字符设备文件上读取的是 IP 数据包,写入的也只能是 IP 数据包,因此常用于一些点对点 IP 隧道,例如 OpenVPN,IPSec 等;
  • tap 设备是二层链路层设备,等同于一个以太网设备,也通过同样的字符设备文件 /dev/net/tun 读取 MAC 层数据帧,写入的也只能是 MAC 层数据帧,因此常用来作为虚拟机模拟网卡使用;

在 Linux 中,内核空间和用户空间之间数据传输有多种方式,字符设备文件是其中一种。tap/tun 对应的字符设备文件为 /dev/net/tun。当用户空间的程序 open() 字符设备文件时,会返回一个 fd 句柄,同时字符设备驱动就会创建并注册相应的虚拟网卡网络接口,并以 tunX 或 tapX 命名。当用户空间的程序向 fd 执行 read()/write() 时,就可以和内核网络协议栈读写数据了。

tap 和 tap 的工作方式基本相同,只是两者工作的层面不一样。以使用 tap 设备建立的 VPN 隧道为例说明其工作原理:普通的用户程序发起一个网络请求,数据包进入内核协议栈时查找路由,下一跳是 tunX 设备。tunX 发现自己的另一端由 VPN 程序打开,所以收到数据包后传输给 VPN 程序。VPN 程序对数据包进行封装操作,“封装”是指将一个数据包包装在另一个数据包中,就像将一个盒子放在另一个盒子中一样。封装后的数据再次被发送到内核,最后通过 eth0 接口(也就是图中的物理网卡)发出。

在这里插入图片描述

将一个数据包封装到另一个数据包的处理方式被称为 “隧道”,隧道技术是构建虚拟逻辑网络的经典做法。容器网络插件 Flannel 早期曾使用 tun 设备实现了 UDP 模式下的跨主网络相互访问,但使用 tun 设备传输数据需要经过两次协议栈,且有多次的封包/解包过程,产生额外的性能损耗。

虚拟网卡 veth

Linux 内核 2.6 版本支持网络命名空间的同时,也提供了专门的虚拟网卡 veth(Virtual Ethernet,虚拟以太网网卡)。veth 实现原理的本质是一种“反转数据传输方向”的实现,即:在内核中将需要被发送的数据包“反转"为新接收到的数据包,重新进入内核网络协议栈中处理。

veth 的典型应用是连接各类虚拟设备,让原本隔离的网络名称空间可以互相通信。当我们创建一个 veth 设备时,会自动创建另一个关联的 veth 设备(网线的另一头)。将关联的 veth 设备其移动到其他网络命名空间后,两个 veth 设备之间的数据包传输被视为在一个以太网连接上传输。

假设我们已经有两个相互隔离的网络命名空间 ns1 和 ns2,它们的网络拓扑结构如图。下面进行操作演示,实现veth 设备网络命名空间的互通。

在这里插入图片描述

首先,创建一对 Veth 设备,它们的名称分别为 veth1 和 veth2。

$ ip link add veth1 type veth peer name veth2
  • ip link add: 添加一个新的网络设备。
  • veth1: 新创建的虚拟以太网设备的名称(一端)。
  • type veth:指定设备的类型为虚拟以太网(veth),它们是一对相互连接的虚拟网络接口。
  • peer name veth2:指定另一端的虚拟以太网设备的名称为 veth2。

接下来,要做的是把这对设备分别放到 ns1 和 ns2 中。

$ ip link set veth1 netns ns1
$ ip link set veth2 netns ns2

veth 是虚拟的网络设备,因此具备虚拟网卡的特征。即可配置 IP/MAC 地址。接下来,给 veth 设备配置上 ip 地址,它们的 IP 地址处于同一个子网 172.16.0.1/24 中,同时激活 veth 设备。

# 配置命名空间1
$ ip netns exec ns1 ip link set veth1 up
$ ip netns exec ns1 ip addr add 172.16.0.1/24 dev veth1
# 配置命名空间2
$ ip netns exec ns2 ip link set veth2 up
$ ip netns exec ns2 ip addr add 172.16.0.2/24 dev veth2

Veth 设备配置完 ip 之后,每个网络命名空间中会自动生成对应的路由信息。

$ ip netns exec ns1 ip route
172.16.0.0/24 dev veth1  proto kernel  scope link  src 172.16.0.1

上面的路由配置表明,凡是属于 172.16.0.0/24 网段的数据包都会通过 veth1 发送。通过 veth1 接口发送的数据包会被 veth2 接收到。在 ns1 中进行 ping 测试,可以看到两个命名空间已经连通了。

$ ip netns exec ns1 ping -c10 172.16.0.2
PING 172.16.0.2 (172.16.0.2) 56(84) bytes of data.
64 bytes from 172.16.0.2: icmp_seq=1 ttl=64 time=0.121 ms
64 bytes from 172.16.0.2: icmp_seq=2 ttl=64 time=0.063 ms
  • ip netns exec ns1:在网络命名空间ns1中执行后续命令的方式,通常用于隔离网络环境。
  • ping -c10 172.16.0.2:向IP地址172.16.0.2发送10个ICMP回显请求(ping),以测试网络连通性。

虽然 veth 设备模拟网卡直连的方式解决了两个容器之间的通信问题,但面对多个容器间通信需求,如果只用 Veth 设备的话,事情就会变得非常麻烦。让每个容器都为与它通信的其他容器建立一对专用的 Veth 设备,根本不切实际。

此刻,就迫切需要有一台虚拟化的交换机,来解决多容器之间的通信问题,这就是 Linux Bridge。

虚拟交换机 Linux bridge

物理网络中,我们如果要将多台主机连接起来,会使用以太网交换机设备将它们组成一个小型局域网。Linux 网络虚拟化系统中,也提供了交换机的虚拟实现 —— Linux bridge(Linux 网桥)。

Linux bridge 作为一个虚拟的交换机,和物理交换机有相似的功能。在 Linux 系统中,将一个或多个网络接口(如物理网卡 eth0、虚拟接口 veth、tap 等)添加到 Linux bridge 之后,它们的通信与物理交换机的转发行为是完全一致的。

当一个数据帧进入 Linux bridge 时,它就会根据数据包的类型和目标 MAC 地址,按照如下规则理:

  • 如果数据包是广播帧,转发给所有桥接到该 Linux bridge 的设备。
  • 如果数据包是单播帧,查看 FDB(地址转发表)中 MAC 地址与网络设备接口的映射:
    • 如找不到记录,则洪泛(Flooding)给所有接入网桥的设备,并把响应设备的网络接口与 MAC 地址记录到 FDB 表中;
    • 如找到记录,则将数据帧转发到对应的接口。

使用 Linux bridge 将两个命名空间接入到同一个二层网络。该例子的网络拓扑结构如下图:

在这里插入图片描述
创建 Linux bridge 与创建其他虚拟网络设备类似,只需要指定 type 参数为 bridge。

$ ip link add name br0 type bridge
$ ip link set br0 up

这样创建出来的 Linux bridge 一端连接着主机协议栈,其他端口没有连接。我们需要将其他设备连接到该 bridge 才能有实际的功能。下面,创建网络命名空间以及 veth 设备。然后将 veth 设备的一端加入到网络命名空间内,另一端桥接到刚创建的 br0。

# 创建网络命名空间
$ ip netns add ns1
$ ip netns add ns2

# 创建 veth 网线
$ ip link add veth0 type veth peer name veth1
$ ip link add veth2 type veth peer name veth3

# 将 veth 网线的一端连接到网络命名空间内
$ ip link set veth0 netns ns1
$ ip link set veth2 netns ns2

# 将 veth 另一端连接到 br0
$ ip link set dev veth1 master br0
$ ip link set dev veth3 master br0

激活命名空间内的虚拟网卡,为它们设置 IP 地址,这些 IP 地址位于同一个子网 172.16.0.0/24 中。

# 配置命名空间1
$ ip netns exec ns1 ip link set veth1 up
$ ip netns exec ns1 ip addr add 172.16.0.1/24 dev veth1
# 配置命名空间2
$ ip netns exec ns2 ip link set veth2 up
$ ip netns exec ns2 ip addr add 172.16.0.2/24 dev veth2

接下来,检查几个命名空间之间是否可达。

ip netns exec ns1 ping 172.16.0.2
PING 172.16.0.2 (172.16.0.2) 56(84) bytes of data.
64 bytes from 172.16.0.1: icmp_seq=1 ttl=64 time=0.153 ms
64 bytes from 172.16.0.1: icmp_seq=2 ttl=64 time=0.148 ms
64 bytes from 172.16.0.1: icmp_seq=3 ttl=64 time=0.116 ms

在分配 IP 地址的时候,只为 veth 在命名空间中那一端的虚拟网卡分配了地址,而没有为加入 bridge 那一端分配地址。这是因为 bridge 是工作在二层(数据链路层)上的,只会处理以太包,包括 ARP 解析、以太数据包的转发和泛洪。

区别于物理二层交换机,Linux bridge 的本质是 Linux 系统中的虚拟网络设备,因此也具备网卡的特征,即可配置 MAC/IP 地址。从主机角度来看,配置了 IP 的 Linux bridge 设备就是主机上的一张网卡,工作在 IP 网路层,自然可以参与主机的路由转发。也就是说,将命名空间的缺省网关设置为该 IP 后,便可以让原本隔离的命名空间和主机进行网络通信。

Bridge的实现有一个限制:当一个设备被绑定到Bridge上时,该设备的IP地址会变得无效,Linux不再使用该IP地址在三层接收数据。比如,如果eth0本来具有自己的IP地址192.168.1.1,在绑定到br0之后,它的IP地址会失效,用户程序不再能接收或发送到这个IP地址的数据只有目的地址为br0 IP的数据包才会被Linux接收。

MACVTAP

传统的Linux网络虚拟化技术采用的是TAP+Bridge方式,将虚拟机连接到虚拟的TAP网卡,然后将TAP网卡绑定到Linux Bridge。这种解决方案实际上就是使用软件,用服务器的CPU模拟网络,但这种技术主要有三个缺点:

  • 每台宿主机内都存在Bridge会使网络拓扑变得复杂,相当于增加了交换机的级联层数。
  • 同一宿主机上虚拟机之间的流量直接在Bridge完成交换,使流量监控、监管变得困难。
  • Bridge是软件实现的二层交换技术,会加大服务器的负担。

针对云计算中的复杂网络问题,Linux 引入了新的网络设备模型——MACVTAP,用来简化虚拟化环境下的桥接网络,代替传统的TAP+Bridge组合。

MACVTAP的实现基于传统的 MACVLAN。MACVLAN允许在主机的一个网络接口上配置多个虚拟的网络接口,这些网络接口有自己独立的 MAC 地址,也可以配置 IP 地址进行通信。MACVLAN 下的虚拟机或者容器和主机在同一个网段中,共享同一个广播域。MACVLAN 和 Bridge 比较相似,但因为它省去了 Bridge。

Open vSwitch

Open vSwitch是一个具有产品级质量的虚拟交换机,它使用C语言进行开发,从而充分考虑了在不同虚拟化平台间的移植性,同时它遵循Apache2.0许可。

对于虚拟网络来说,交换设备的虚拟化是很关键的一环,vSwitch负责连接vNIC与物理网卡,同时也桥接同一物理Server内的各个vNIC。

在传统数据中心中,网络管理员通过对交换机的端口进行一定的配置,可以很好地控制物理机的网络接入,完成网络隔离、流量监控、数据包分析、Qos配置、流量优化等一系列工作。

但是在云环境中,仅凭物理交换机的支持,管理员无法区分被桥接的物理网卡上流淌的数据包属于哪个VM、哪个OS及哪个用户,Open vSwitch的引入则使云环境中虚拟网络的管理以及对网络状态和流量的监控变得容易。

我们可以像配置物理交换机一样,将接入到Open vSwitch(Open vSwitch同样会在物理Server上创建一个或多个vSwitch供各个虚拟机接入)上的各个VM分配到不同的VLAN中实现网络的隔离。也可以在Open vSwitch端口上为VM配置Qos,同时Open vSwitch也支持包括NetFlow、sFlow很多标准的管理接口和协议,可以通过这些接口完成流量监控等工作。

一个物理 Server 上的 vSwitch 可以透明地与另一个 Server上的vSwitch连接在一起
在这里插入图片描述

而Open vSwitch软件本身,则由内核态的模块以及用户态的一系列后台程序组成

在这里插入图片描述
其中ovs-vswitchd是最重要的模块,实现了虚拟机交换机的后台,负责与远程的Controller进行通信,例如通过OpenFlow协议与OpenFlow Controller通信,通过sFlow协议同sFlow Trend通信。

此外,ovs switchd也负责同内核态模块通信,基于netlink机下发具体的规则和动作到内核态的 datapath。datapath负责执行数据交换,也就是把从接收端口收到的数据包在流表(Flow Table)中进行匹配,并执行匹配到的动作。每个datapath都和一个流表关联,当datapath接收数据后,会在流表中查找可以匹配的Flow,执行对应的动作,比如转发数据到另外的端口。ovsdb-server是一个轻量级的数据库服务器,主要用来记录被ovs-switchd的配置信息。

iptables / NAT

网络地址转换(NAT,Network Address Translation)是一种在IP数据包通过路由器或防火墙时重写来源IP地址或目的IP地址的技术。

Linux中的NAT功能一般通过iptables实现。iptables是基于Linux内核的功能强大的防火墙,netfilter作为iptables内核底层的实现框架。

netfilter提供了一整套对hook函数管理的机制,可以在数据包流经的5处关键地方(Hook点),分别是PREROUTING(路由前)、INPUT(数据包入口)、OUTPUT(数据包出口)、FORWARD(数据包转发)、POSTROUTING(路由后),写入一定的规则对经过的数据包进行处理,规则一般的定义为“如果数据包头符合这样的条件,就这样处理数据包”。

iptables/netfilter是按照规则来工作的,这些规则分别指定了源地址、目的地址、传输协议(如TCP、UDP、CMP)和服务类型(如HTP、FP和SMTP)等。数据包与规则匹配时,iptables 就根据规则定义的方法处理这些数据包,比如放行、拒绝和丢弃等。配置防火墙的主要工作就是添加、修改和删除这些规则。

虚拟网络隔离技术

虚拟机分布在不同的物理主机之上,每一台物理主机都有物理网络互相联通,然而基于物理网络的拓扑结构是相对固定的,很难跟得上云原生时代分布式系统频繁变动的频率,软件定义网络(Software Defined Networking,SDN)的需求变得前所未有的迫切。

SDN 的核心思想是在现有的物理网络之上再新增一层虚拟网络,将控制平面(操作系统和各类软件)和数据平面(交换机和各类通信协议等)解耦,使网络设备的控制平面可直接代码编程控制,将网络服务从底层硬件设备中抽象出来。SDN 里位于下层称 Underlay 网络,它是由多个类型设备互联而成的物理网络,负责网络之间的数据包传输,位于上层的网络称 Overlay 网络,它是采用多种网络虚拟化技术在 Underlay 网络之上创建虚拟网络。

在这里插入图片描述

虚拟局域网 VLAN

基于以太网的通信中(称为广播域),必须在数据帧中指定目标 MAC 地址才能正常通信,因此计算机必须先广播 ARP 请求,来尝试获取目标 MAC 地址。假设当设备非常多时,ARP 请求、DHCP、RIP 都会产生大量的广播帧,很容易形成广播风暴。因此 VLAN 的首要职责是划分广播域,一个 VLAN 一个广播域,把连接在同一个物理网络上的设备区分开来。各个 VLAN 间不能直接互通,广播报文就被限制在一个 VLAN 内。

VLAN 划分子网具体方法是在以太帧的报文头中加入 VLAN Tag,让所有广播只针对具有相同 VLAN Tag 的设备生效。对于支持 VLAN 的交换机,当这个交换机把二层的头取下来的时候,就能够识别这个 VLAN ID。这样只有相同 VLAN 的包,才会互相转发,不同 VLAN 的包,是看不到的。

划分 VLAN 的同时为不同 VLAN 建立互相访问的通道也是必要的。由于两个 VLAN 之间完全隔离的,不存在重合的广播域,因此要通信只能通过三层路由设备。最简单的三层路由模式就是通过单臂路由实现。

单臂路由的网络拓扑如图所示。路由器和交换机之间只有一条线路(称为 Trunk 链路),此线路允许任何 VLAN ID 的数据包通过。与之相对是连接主机多条 access 链路。Trunk 链路在逻辑上是分开的,需要路由的数据包通过这个线路到达路由器,经过路由后再通过此线路返回交换机进行转发。

在这里插入图片描述
VLAN 固然通过划分子网的形式解决广播风暴,但它的缺陷也非常明显:

  • 第一个缺陷:在于 VLAN Tag 的设计。当时的网络工程师完全未料及云计算会发展得会如此普及,只设计了 12 位来存储 VLAN ID,导致一个 VLAN 子网内的设备数量局限在 4000 个左右,这显然无法支持大型数据中心数以万计的设备数。
  • 第二个缺陷:跨数据中心通信非常麻烦。VLAN 是一个二层网络技术,但是在两个独立数据中心之间信息只能够通过三层网络传递,云计算的发展普及很多业务有跨数据中心运作的需求,所以数据中心间传递 VLAN Tag 又是一件比较麻烦的事情;并且在虚拟网络中,一台物理机会有多个虚拟机和容器,容器和虚拟机相比也是呈数量级增长,每个容器都有独立的 IP 地址和 MAC 地址,这样带给交换机的压力也是成倍增加。

虚拟可扩展局域网 VXLAN

为了解决上述 VLAN 的设计缺陷,IETF 又重新定义了 VXLAN(Virtual eXtensible Local Area Network,虚拟可扩展局域网) 规范。从名字上看,VXLAN 像是 VLAN 的一种扩展协议,但其实 VXLAN 的设计与 VLAN 有着本质的不同。

VXLAN 属于 NVO3(Network Virtualization over Layer 3,三层虚拟化网络)的标准技术规范之一。它实际是一种隧道封装技术,它使用 TCP/IP 协议栈的惯用手法“封装/解封装技术”,将 L2(链路层)以太网帧封装成 L4(传输层)的 UDP 报文,然后在 L3(网络层)网络中传输,效果就像一个普通以太网帧在一个广播域中传输一样。

从图中可以看到 VXLAN 报文对原始以太网帧(Original Layer2 Frame)进行了包装:

  • VXLAN Header:其中包含 24 Byte 的 VNI 字段,用来定义 VXLAN 网络中不同的租户,可以存储 1677 万个不同的取值。
  • UDP Header:其中 VXLAN 头和原始以太帧一起作为 UDP 的数据,头中目的端口号(VXLAN Port)固定为 4789,源端口按流随机分配(通过 MAC、IP 和四层端口号进行 hash 操作)。
  • Outer IP Header:封装外层 IP 头,封装了目的 IP 地址和源IP地址,这里 IP 指的是宿主机的 IP 地址。
  • Outer MAC Header:封装外层以太头,封装了源 MAC 地址,目的 MAC 地址,这里 MAC 地址指的是宿主机 MAC 地址。

在这里插入图片描述
在 VXLAN 隧道网络中,负责“封装/解封”的设备称为 VTEP 设备(VXLAN Tunnel Endpoints,VXLAN 隧道端点),它在 Linux 系统中实际上是一个虚拟 VXLAN 网络接口。当源服务器内的容器发出原始数据帧后,首先在隧道的起点(VTEP 设备)被封装成 VXLAN 格式的报文,然后通过主机 IP 网络传递到隧道的终点(也就是目标服务器中的 VTEP 设备)。目标服务器 VETP 设备解封 VXLAN 报文,得到原始的数据帧,最后转发至目标服务器内的某一个容器。

在这里插入图片描述
从上述,我们看到 VXLAN 完美地弥补了 VLAN 的不足:

  • 通过 VXLAN 中的 24 byte VNI 字段提供多达 16 百万的标识能力,远大于 VLAN 的 4000;
  • VXLAN 本质上在两台交换机之间构建了一条穿越数据中心基础 IP 网络的虚拟隧道,将数据中心网络虚拟成一个巨型“二层交换机”。虚拟机或者容器无论是迁移到 VLAN B 还是 VLAN C,仍然处于同一个二层网络,IP 的配置都不需要任何变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值