## 1.1 网络虚拟化:network namespace
#### 1.1.1 Linux的namespace的作用就是“隔离内核资源”。
主要包括:Mount namespace、UTS namespace、IPC namespace、PID namespace、network namespace和user namespace。
对进程来说,要想使用namespace里面的资源,首先要“进入”到这个namespace,而且还无法跨namespace访问资源。Linux的namespace给里面的进程造成了两个错觉:
1. 它是系统里唯一的进程
2. 它独享系统的所有资源
尽管Linux的namespace隔离技术很早便存在于内核中,而且它就是为Linux的容器技术而设计的,但它一直鲜为人知。直到Docker引领的容器技术革命爆发,它才进入普罗大众的视线——Docker容器作为一项轻量级的虚拟化技术,它的隔离能力来自Linux内核的namespace技术。
network namespace的增删改查功能已经集成到Linux的ip工具的netns子命令中,下面先介绍几条简单的网络namespace管理的命令。
创建一个名为netns1的network namespace可以使用以下命令:
```shell
$ ip netns add netns1
```
当ip命令创建了一个network namespace时,系统会在/var/run/netns路径下面生成一个挂载点。挂载点的作用一方面是方便对namespace管理,另一方面是使namespace即使没有进程运行也能继续存在。
一个network namespace创建出来后,可以使用 ip netns exec 命令进入,做一些网络查询/配置的工作
```shell
$ ip netns exec netns1 ip link list
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
```
如上所示,就是进入netns1这个network namespace查询网卡信息的命令。目前,我们没有任何配置,因此只有一块系统默认的本地回环设备lo。
查看系统中有哪些network namespace,可以通过以下命令实现:
```shell
$ ip netns list
netns1
```
想删除network namespace,可以使用以下命令:
```shell
$ ip netns delete netns1
```
#### 1.1.2 配置network namespace
当namespace里面的进程涉及网络通信时,namespace里面的网络设备就必不可少了。如果我们想与外界进行通信,就需要在namespace里再创建一对虚拟的以太网卡,即所谓的veth pair。
下面命令将创建一对虚拟以太网卡,然后把 veth pair 一端放到 netns1 network namespace
```shell
$ ip link add veth0 type veth name veth1
$ ip link set veth1 netns netns1
```
如上所示,我们创建了veth0和veth1这么一对虚拟以太网卡,默认都在根network namespace中,将其中一块虚拟网卡veth1通过ip link set命令移动到netns1 network namespace。veth0跟veth1之间还不能通信,需要绑定IP地址与修改网卡状态
```shell
$ ip netns exec netns1 ifconfig veth1 10.1.1.1/24 up
$ ifconfig veth0 10.1.1.2/24 up
```
这样一来,我们就可以ping通veth pair的任意一头了。另外,不同network namespace之间的路由表和防火墙规则等也是隔离的,因此netns1 network namespace没法和主机共享路由表和防火墙,通过下面可以验证
```shell
$ ip netns exec netns1 route
$ ip netns exec netns1 iptables -L
```
想连接因特网,有若干解决方法。例如:可以在主机的根networ namespace创建一个Linux网桥并绑定veth pair的一端到网桥上。
用户可以随意将虚拟网络设备分配到自定义的network namespace里,而连接真实硬件的物理设备则只能放在系统的根network namespace中。并且,任何一个网络设备最多只能存在于一个network namespace中。
#### 1.1.3 小结
我们知道通过Linux的network namespace技术可以自定义一个独立 的网络栈,简单到只有loopback设备,复杂到具备系统完整的网络能 力,这就使得network namespace成为Linux网络虚拟化技术的基石——不论是虚拟机还是容器时代。network namespace的另一个隔离功能在 于,系统管理员一旦禁用namespace中的网络设备,即使里面的进程拿 到了一些系统特权,也无法和外界通信。最后,网络对安全较为敏感, 即使network namespace能够提供网络资源隔离的机制,用户还是会结合 其他类型的namespace一起使用,以提供更好的安全隔离能力。
## 2.1 Linux bridge
容器运行在自己单独的network namespace里,因此都有自己单独的协议栈。
Linux bridge在容器场景的组网和上面的虚拟机场景差不多,但也存在一些区别。
以下命令可以查看网桥信息
```shell
$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242b3a9fb43 no veth7dff7cb
$ bridge link
54: veth7dff7cb@if53: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master docker0 state forwarding priority 32 cost 2
```
例如,容器使用的是veth pair设备,而虚拟机使用的是tun/tap设备。在虚拟机场景下,我们给主机物理网卡eth0分配了IP地 址;而在容器场景下,我们一般不会对宿主机eth0进行配置。在虚拟机场景下,虚拟器一般会和主机在同一个网段;而在容器场景下,容器和物理网络不在同一个网段内。
![image.png](https://blog.zs-fighting.cn/upload/2021/08/image-182a6fdffe4540e1a7aaacf35615ea79.png)
在容器中配置其网关地址为br0,在例子中即1.2.3.101(容器网络网段是1.2.3.0/24)。因此,从容器发出去的数据包先到达br0,然后交给host机器的协议栈。由于目的IP是外网IP,且host机器开启了IP forward功能,数据包会通过eth0发送出去。因为容器所分配的网段一般都不在物理网络网段内(在例子中,物理网络网段是 10.20.30.0/24),所以一般发出去之前会先做NAT转换(NAT转换需要 自己配置,可以使用iptables)。
## 3.1 tun/tap设备
tun/tap设备到底是什么?从Linux文件系统的角度看,它是用户可以用文件句柄操作的字符设备;从网络虚拟化角度看,它是虚拟网卡,一端连着网络协议栈,另一端连着用户态程序。
tun表示虚拟的是点对点设备,tap表示虚拟的是以太网设备,这两种设备针对网络包实施不同的封装。
tun/tap设备有什么作用呢?tun/tap设备可以将TCP/IP协议栈处理好的网络包发送给任何一个使用tun/tap驱动的进程,由进程重新处理后发到物理链路中。tun/tap设备就像是埋在用户程序空间的一个钩子,我们可以很方便地将对网络包的处理程序挂在这个钩子上,OpenVPN、Vtun、flannel都是基于它实现隧道包封装的.(flannel的UDP模式就是tun/tap设备)
tun/tap设备的基本原理
![image.png](https://blog.zs-fighting.cn/upload/2021/08/image-929818dd223f492c834204ce2a8ebbc6.png)
是一个经典的、通过Socket调用实现用户态和内核态数据交互的过程。物理网卡从网线接收数据后送达网络协议栈,而进程通过Socket创建特殊套接字,从网络协议栈读取数据。
从网络协议栈的角度看,tun/tap设备这类虚拟网卡与物理网卡并无区别。只是对tun/tap设备而言,它与物理网卡的不同表现在它的数据源不是物理链路,而是来自用户态!
## 4.1 VXLAN
![image.png](https://blog.zs-fighting.cn/upload/2021/08/image-5cd299de4c8f4664bddd247e168efa58.png)
上图为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封包格式如下:
![image.png](https://blog.zs-fighting.cn/upload/2021/08/image-2a05de7ef69f4385baf5f460ab0e5eef.png)
- VXLAN Header:增加VXLAN头(8字节),其中包含24比特的VNI字段,用来定义VXLAN网络中不同的租户。此外,还包含VXLAN Flags(8比特,取值为00001000)和两个保留字段(分别为24比特和8比特)。
- UDP Header:VXLAN头和原始以太帧一起作为UDP的数据。UDP头中,目的端口号(VXLAN Port)固定为4789,源端口号(UDP Src. Port)是原始以太帧通过哈希算法计算后的值。
- Outer IP Header:封装外层IP头。其中,源IP地址(Outer Src. IP)为源VM所属VTEP的IP地址,目的IP地址(Outer Dst. IP)为目的VM所属VTEP的IP地址。
- Outer MAC Header:封装外层以太头。其中,源MAC地址(Src. MAC Addr.)为源VM所属VTEP的MAC地址,目的MAC地址(Dst. MAC Addr.)为到达目的VTEP的路径中下一跳设备的MAC地址。
VXLAN的报文就是MAC in UDP,即在三层网络的基础上构建一个虚拟的二层网络。
Linux网络虚拟化