虚拟网络
首先Linux具有丰富的虚拟网络功能,我们先简要介绍几个常用的虚拟网络接口类型:
1. Bridge网桥
Linux Bridge网桥的行为类似于网络交换机。它在与其连接的接口之间转发数据包。它通常用于在路由器、网关上或在主机上的虚拟机和网络命名空间之间转发数据包。它还支持 STP、VLAN 过滤器和多播侦听。
当我们想要在虚拟机、容器和主机之间建立网络通道时,可以使用网桥。如上图所示,创建了一个桥接设备br0
,并将3个VETH 设备(veth1,veth2,veth3)和一个物理设备(eth0
)设置为其从属设备,创建命令示例如下
# ip link add br0 type bridge
# ip link set veth1 master br0
# ip link set veth2 master br0
# ip link set veth3 master br0
# ip link set eth0 master br0
2. Bonded interface网卡绑定
Linux Bonded interface网卡绑定提供了一种将多个网络接口聚合为单个逻辑“绑定”接口的方法,有热备或负载平衡等模式。
当我们想增加服务器网卡速度,或实现网卡硬件高可用时,可以使用网卡绑定。如上图所示,服务器挂载了两块网卡,用热备方式绑定了一个虚拟网卡:
#ip link add bond0 type bond miimon 100 mode active-backup
#ip link set eth0 master bond0
#ip link set eth1 master bond0
3. VLAN
VLAN,也称为虚拟LAN,通过向网络数据包添加标签TAG来分隔广播域。VLAN允许网络管理员将主机分组到同一交换机下或不同交换机之间。
当您想在VM、网络命名空间或主机中分隔子网时,可以考虑使用VLAN。如上图所示,创建命令如下:
# ip link add link eth0 name eth0.1 type vlan id 1
# ip link add link eth0 name eth0.2 type vlan id 2
4.VETH虚拟以太网
VETH(虚拟以太网)设备是一个本地以太网隧道,设备是成对创建的,在一对设备上的某一端传输的数据包会立即在对端被接收。当任一设备关闭时,该对的链路状态为关闭。
当网络命名空间需要与主主机命名空间通信或网络命名空间相互通信时,可以使用VETH,如下命令,创建了两个命名空间netns1和netns2以及一对VETH设备,并将一端veth1分配给命名空间netns1,将另一端veth2分配给命名空间netns2。这两个名称空间用此VETH对进行网络连接。
# ip netns add net1
# ip netns add net2
# ip link add veth1 netns net1 type veth peer name veth2 netns net2
如上示例,再分配一对IP地址,我们就可以在两个命名空间之间ping和通信。示例多次提到网络命名空间,下面我们再介绍网络命名空间。
网络命名空间
Linux命名空间namespace被很多开源技术(如Kubernetes,Openstack,Docker)广泛使用或者成为核心技术之一。Linux Namespaces强大之处,是它能提供一种隔离系统全局资源的方法, 通过这个方法,每个namespace都了有一份独立的资源。这样,不同的进程在各自的namespace里对同一种资源的访问不会发生冲突。每个Namespace看上去就像一个单独的Linux系统,与hypervisor技术(如Vmware ESX、vSphere)比较,这是一个轻量级,完全免费的系统虚拟化解决方案。
其中网络命名空间是非常重要的一项技术,接下来我们通过三个示例来讲解使用方法。
直连互通
#!/bin/bash
set euxo -pipefail
#创建网络命名空间
ip netns add netns1
ip netns add netns2
#显示创建的命名空间
ip netns show
#创建一对veth虚拟设备
ip link add veth1 type veth peer name veth2
#把veth虚拟设备两端挂载到各自的命名空间
ip link set veth1 netns netns1
ip link set veth2 netns netns2
#分配ip网络,并启动虚拟设备
ip netns exec netns1 ip addr add 192.168.10.1/24 dev veth1
ip netns exec netns2 ip addr add 192.168.10.2/24 dev veth2
ip netns exec netns1 ip link set dev veth1 up
ip netns exec netns2 ip link set dev veth2 up
现在我们在netns2命名空间中ping一下veth1看到两个命名空间的网络已经互通:
# ip netns exec netns2 ping 192.168.10.1
PING 192.168.10.1 (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1: icmp_seq=1 ttl=64 time=0.050 ms
64 bytes from 192.168.10.1: icmp_seq=2 ttl=64 time=0.024 ms
64 bytes from 192.168.10.1: icmp_seq=3 ttl=64 time=0.029 ms
64 bytes from 192.168.10.1: icmp_seq=4 ttl=64 time=0.028 ms
桥接互通
如上图示例,两个命令空间通过Bridge网桥进行网络连接,创建过程如下:
#!/bin/bash
set euxo -pipefail
#创建网络命名空间
ip netns add netns1
ip netns add netns2
#显示创建的命名空间
ip netns show
#创建bridge网桥
ip link add br0 type bridge
ip link set dev br0 up
#显示创建的bridge网桥
brctl show
#创建两对veth虚拟设备
ip link add veth1 type veth peer name veth1-br
ip link add veth2 type veth peer name veth2-br
#把veth1虚拟设备一端挂载到netns1命名空间,另一端作为网桥的从属
ip link set veth1 netns netns1
ip link set veth1-br master br0
#把veth2虚拟设备一端挂载到netns2命名空间,另一端作为网桥的从属
ip link set veth2 netns netns2
ip link set veth2-br master br0
#分配ip网络,并启动虚拟设备
ip netns exec netns1 ip addr add 192.168.10.1/24 dev veth1
ip netns exec netns2 ip addr add 192.168.10.2/24 dev veth2
ip netns exec netns1 ip link set dev veth1 up
ip netns exec netns2 ip link set dev veth2 up
ip link set veth1-br up
ip link set veth2-br up
现在我们在netns2命名空间中ping一下veth1看到两个命名空间的网络已经互通:
# ip netns exec netns2 ping 192.168.10.1
PING 192.168.10.1 (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1: icmp_seq=1 ttl=64 time=0.037 ms
64 bytes from 192.168.10.1: icmp_seq=2 ttl=64 time=0.029 ms
64 bytes from 192.168.10.1: icmp_seq=3 ttl=64 time=0.028 ms
64 bytes from 192.168.10.1: icmp_seq=4 ttl=64 time=0.029 ms
64 bytes from 192.168.10.1: icmp_seq=5 ttl=64 time=0.029 ms
现在可以实现多个网络命名空间通过网桥互相网络通信,接下来我们看下跟宿主机之间的通信,发现跟宿主机网络无法连通,原因很明显,网络空间外的网络设备并不知道192.168.10.0/24这个网络,网络空间内的网络设备也不知道172.30.52.0/24和8.8.8.8网络。
# ip netns exec netns2 ping 172.30.52.194
connect: Network is unreachable
# ip netns exec netns2 ping 8.8.8.8
connect: Network is unreachable
# ping 192.168.10.1
PING 192.168.10.1 (192.168.10.1) 56(84) bytes of data.
^C
桥接NAT
接上面提到的问题,我们配置能出网的网络互通环境。首先创建过程跟上节一致,创建之后我们查看下路由:
# ip netns exec netns1 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.10.0 0.0.0.0 255.255.255.0 U 0 0 0 veth1
# ip netns exec netns2 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
192.168.10.0 0.0.0.0 255.255.255.0 U 0 0 0 veth2
配置网关和路由
#桥接网络上设置子网的gateway
ip addr add 192.168.10.10/24 dev br0
#在网络空间里添加路由ip netns exec netns1 ip route add 172.30.52.0/24 via 192.168.10.10
ip netns exec netns2 ip route add 172.30.52.0/24 via 192.168.10.10
查看主机路由和尝试ping操作
# ip route
default via 172.30.63.253 dev eth0
169.254.0.0/16 dev eth0 scope link metric 1002
172.30.48.0/20 dev eth0 proto kernel scope link src 172.30.52.194
192.168.10.0/24 dev br0 proto kernel scope link src 192.168.10.10
# ping 192.168.10.2
PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data.
64 bytes from 192.168.10.2: icmp_seq=1 ttl=64 time=0.023 ms
64 bytes from 192.168.10.2: icmp_seq=2 ttl=64 time=0.077 ms
查看网络命名空间如netns2的路由和尝试ping操作
# ip netns exec netns2 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
172.30.52.0 192.168.10.10 255.255.255.0 UG 0 0 0 veth2
192.168.10.0 0.0.0.0 255.255.255.0 U 0 0 0 veth2
# ip netns exec netns2 ping 172.30.52.194
PING 172.30.52.194 (172.30.52.194) 56(84) bytes of data.
64 bytes from 172.30.52.194: icmp_seq=1 ttl=64 time=0.046 ms
64 bytes from 172.30.52.194: icmp_seq=2 ttl=64 time=0.041 ms
64 bytes from 172.30.52.194: icmp_seq=3 ttl=64 time=0.039 ms
# ip netns exec netns2 ping 8.8.8.8
connect: Network is unreachable
可以看到,宿主机有到192.168.10.0/24的路由,所以宿主机能ping通netns2和netns1的网络。而netns1和netns2中都有172.30.52.0/24的路由,所以能ping通宿主机。
最后一步:访问外网8.8.8.8不通的原因一是因为缺少路由,二是没有设置NAT转发。首先我们添加路由
ip netns exec netns1 ip route add default via 192.168.10.10
ip netns exec netns2 ip route add default via 192.168.10.10
然后测试发现不通
# ip netns exec netns2 ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
^C
添加NAT转发并打开linux的ipv4 forwarding功能
iptables --table nat -A POSTROUTING -s 192.168.10.0/24 -j MASQUERADE
echo 1 > /proc/sys/net/ipv4/ip_forward
再次测试成功:
# ip netns exec netns2 ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=110 time=42.7 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=110 time=42.7 ms