Linux虚拟网络技术
Network Namespace
Network Namespace 是 Linux 内核提供的功能,是实现网络虚拟化的重要功能,它能创建多个隔离的网络空间,它们有独自网络栈信息。不管是虚拟机还是容器,运行的时候仿佛自己都在独立的网络中。而且不同Network Namespace的资源相互不可见,彼此透明。
这篇文章介绍 Network Namespace 的基本概念和用法。这篇文件借助ip
命令来完成各种操作。ip
命令来自于iproute2
安装包,一般系统会默认安装,如果没有的话,读者自行安装。
注意:ip
命令修改网络配置时需要 sudo 权限。
可以通过ip netns
命令完成对Network Namespace 的相关操作,可以通过ip netns help
查看命令帮助信息:
$ ip netns help
Usage: ip netns list
ip netns add NAME
ip netns set NAME NETNSID
ip [-all] netns delete [NAME]
ip netns identify [PID]
ip netns pids NAME
ip [-all] netns exec [NAME] cmd ...
ip netns monitor
ip netns list-id
默认情况下,Linux系统中是没有任何 Network Namespace的,所以ip netns list
命令不会返回任何信息。
创建Network Namespace
下面,我们通过命令创建一个名为ns1
的命名空间:
$ ip netns add ns1
$ ip netns list
ns1
新创建的 Network Namespace 会出现在/var/run/netns/
目录下。如果相同名字的 namespace 已经存在,命令会报Cannot create namespace file "/var/run/netns/ns1": File exists
的错误。
对于每个 Network Namespace 来说,它会有自己独立的网卡、路由表、ARP 表、iptables 等和网络相关的资源。
操作Network Namespace
ip
命令提供了ip netns exec
子命令可以在对应的 Network Namespace 中执行命令。
- 查看新创建 Network Namespace 的网卡信息
$ ip netns exec ns1 ip addr
sudo: unable to resolve host zormshogu
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
可以看到,新创建的Network Namespace中会默认创建一个lo
回环网卡,此时网卡处于关闭
状态。此时,尝试去 ping 该lo
回环网卡,会提示Network is unreachable
$ ip netns exec ns1 ping 127.0.0.1
connect: Network is unreachable
通过下面的命令启用lo
回环网卡:
ip netns exec ns1 ip link set lo up
然后再次尝试去 ping 该lo
回环网卡:
$ ip netns exec ns1 ping -c 3 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.048 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.031 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.029 ms
--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.029/0.036/0.048/0.008 ms
veth
veth设备的特点
- veth和其它的网络设备都一样,一端连接的是内核协议栈;
- veth设备是成对出现的,另一端两个设备彼此连接;
- 一个设备收到协议栈的数据发送请求后,会将数据发送到另一个设备上去。
+----------------------------------------------------------------+
| |
| +------------------------------------------------+ |
| | Newwork Protocol Stack | |
| +------------------------------------------------+ |
| ↑ ↑ ↑ |
|..............|...............|...............|.................|
| ↓ ↓ ↓ |
| +----------+ +-----------+ +-----------+ |
| | eth0 | | veth0 | | veth1 | |
| +----------+ +-----------+ +-----------+ |
|192.168.1.11 ↑ ↑ ↑ |
| | +---------------+ |
| | 192.168.2.11 192.168.2.1 |
+--------------|-------------------------------------------------+
↓
Physical Network
上图中,我们给物理网卡eth0配置的IP为192.168.1.11, 而veth0和veth1的IP分别是192.168.2.11和192.168.2.1。
veth pair
veth pair 全称是 Virtual Ethernet Pair,是一个成对的端口,所有从这对端口一 端进入的数据包都将从另一端出来,反之也是一样。
veth pair
创建veth pair
$ sudo ip link add type veth
$ ip addr
61: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether e6:39:e1:e0:3a:a0 brd ff:ff:ff:ff:ff:ff
62: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether be:41:49:42:23:6a brd ff:ff:ff:ff:ff:ff
可以看到,此时系统中新增了一对veth pair,将veth0和veth1两个虚拟网卡连接了起来,此时这对 veth pair 处于”未启用“状态。
如果我们想指定 veth pair 两个端点的名称,可以使用下面的命令:
ip link add vethfoo type veth peer name vethbar
实现Network Namespace间通信
下面我们利用veth pair实现两个不同的 Network Namespace 之间的通信。刚才我们已经创建了一个名为ns1
的 Network Namespace,下面再创建一个信息Network Namespace,命名为ns2
$ ip netns add ns2
$ ip netns list
ns2
ns1
然后我们将veth0加入到ns1,将veth1加入到ns2,如下所示:
$ ip link set veth0 netns ns1
$ ip link set veth1 netns ns2
然后我们分别为这对veth pair配置上ip地址,并启用它们:
$ ip netns exec ns1 iplink set veth0 up
$ ip netns exec ns1 ip addr add 10.0.1.1/24 dev veth0
$ ip netns exec ns2 iplink set veth1 up
$ ip netns exec ns2 ip addr add 10.0.1.2/24 dev veth1
查看这对veth pair的状态
$ ip netns exec ns1 ip addr
61: veth0@if62: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether e6:39:e1:e0:3a:a0 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet 10.0.1.1/24 scope global veth0
valid_lft forever preferred_lft forever
inet6 fe80::e439:e1ff:fee0:3aa0/64 scope link
valid_lft forever preferred_lft forever
$ ip netns exec ns2 ip addr
62: veth1@if61: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether be:41:49:42:23:6a brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.0.1.2/24 scope global veth1
valid_lft forever preferred_lft forever
inet6 fe80::bc41:49ff:fe42:236a/64 scope link
valid_lft forever preferred_lft forever
从上面可以看出,我们已经成功启用了这个veth pair,并为每个veth设备分配了对应的ip地址。我们尝试在ns2
中访问ns1
中的ip地址:
$ ip netns exec ns2 ping -c 3 10.0.1.1
sudo: unable to resolve host zormshogu
PING 10.0.1.1 (10.0.1.1) 56(84) bytes of data.
64 bytes from 10.0.1.1: icmp_seq=1 ttl=64 time=0.091 ms
64 bytes from 10.0.1.1: icmp_seq=2 ttl=64 time=0.035 ms
64 bytes from 10.0.1.1: icmp_seq=3 ttl=64 time=0.037 ms
--- 10.0.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.035/0.054/0.091/0.026 ms
可以看到,veth pair成功实现了两个不同Network Namespace之间的网络交互。
网桥
veth pair打破了 Network Namespace 的限制,实现了不同 Network Namespace 之间的通信。但veth pair有一个明显的缺陷,就是只能实现两个网络接口之间的通信。
如果我们想实现多个网络接口之间的通信,就可以使用下面介绍的网桥(Bridge)技术。
简单来说,网桥就是把一台机器上的若干个网络接口“连接”起来。其结果是,其中一个网口收到的报文会被复制给其他网口并发送出去。以使得网口之间的报文能够互相转发。
网桥的工作原理
网桥的工作过程
注意:
和 bridge 有关的操作也可以使用命令 brctl,这个命令来自 bridge-utils 这个包
bridge-utils安装
- CentOS
sudo yum install -y bridge-utils
- Ubuntu
sudo apt-get install bridge-utils
创建网桥
$ ip link add br0 type bridge
$ ip link set dev br0 up
网桥网络拓扑图