温馨提示,本篇内容较多,主要内容包括:
Linux Namespace
Namespace 是 Linux 内核级别隔离系统资源的解决方案,将网络、进程等资源进行封装隔离,使得各资源彼此透明,互不干扰,这一机制为实现基于容器的虚拟化技术提供了很好的基础。我们可以在 Linux 一个 Host 中创建多个 Namespace, 比如在一个主机中启动若干个 Docker 容器。
Namespace 可隔离的资源有:
Mount: 隔离文件系统挂载点
UTS: 隔离主机名和域名信息
IPC: 隔离进程间通信
PID: 隔离进程的 ID
Network: 隔离网络资源
User: 隔离用户和用户组的 ID
其中 Network 是实现网络虚拟化的重要功能,其会创建多个隔离的网络空间,各自拥有独自的网卡、路由表、iptables、网络协议栈等。下面演示一下 namespace 的简单命令行操作。
Namespace 操作
命令 ip netns add testns
创建一个新的 namespace, ip netns ls
命令查看是否创建成功(namespace 会出现在/var/run/netns 下)。
[root@2030-edu-01-no ~]# 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
[root@2030-edu-01-no ~]# ip netns add testns
[root@2030-edu-01-no ~]# ip netns ls
testns
[root@2030-edu-01-no netns]# ll /var/run/netns/
total 0
-r--r--r-- 1 root root 0 Jul 3 13:49 testns
复制代码
创建好的 namespace 后,使用ip netns exec
在对应的 network namespace 中执行命令,比如查看 namespace 中的 IP 地址即可使用 ip netns exec testns ip addr
[root@2030-edu-01-no netns]# ip netns exec testns ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[root@2030-edu-01-no netns]#
复制代码
也可以使用ip netns exec testns /bin/bash
进入 namespace 窗口执行多个命令
[root@2030-edu-01-no netns]# ip netns exec testns bash
[root@2030-edu-01-no netns]# ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
[root@2030-edu-01-no netns]# exit
exit
复制代码
通过修改 bash 的前缀信息可以区分不同 shell, ip netns exec testns /bin/bash --rcfile <(echo "PS1=\"namespace testns> \"")
通过 ip addr 可以看出,创建 namespace 时自动创建一个 lo 的 interface,但是并有其他网络接口,此时的 namespace 是无法和主机或者其他 namespace 通信的。那么 namespace 之间要如何通信呢,暂时按下不表,先来看一看 Linux 中虚拟网络设备。
虚拟网络设备
Linux 虚拟网络的背后其实是由一个个的虚拟设备所构成的,比如 tap、tun 和 veth-pair。对于一个网络设备来说,就像一个管道(pipe)一样,有两端,从其中任意一端收到的数据将从另一端发送出去。对于物理设备,如网卡 eth0,它的两端分别是内核协议栈和外面的物理网络,从物理网络收到的数据,会转发给内核协议栈,而应用程序从协议栈发过来的数据将会通过物理网络发送出去。对于虚拟网络设备,作为一个网络设备,它也能配置 IP,路由数据,不同的是虚拟网络设备从协议栈接收数据,但怎么发送,发送到哪些是由驱动自身决定的。
tun/tap
在 linux 下,要实现核心态和用户态数据的交互,有多种方式:可以通用 socket 创建特殊套接字,利用套接字实现数据交互;通过 proc 文件系统创建文件来进行数据交互;还可以使用设备文件的方式,访问设备文件会调用设备驱动相应的例程,设备驱动本身就是核心态和用户态的一个接口,Tun/tap 驱动就是利用设备文件实现用户态和核心态的数据交互。
tun/tap 设备的用处是将协议栈中的部分数据包转发给用户空间的应用程序,给用户空间的程序一个处理数据包的机会。设备最常用的场景是 VPN。下图描述了 tun/tap 数据流程示意图,实际的原理比这个更加复杂。
tun/tap 设备实现 VPN 原理,vpn 通过操作系统的接口直接虚拟出一张网卡,后续整个操作系统的网络通讯都将通过这张虚拟的网卡进行收发。这和任何一个代理的实现思路都差不多,应用层并不知道网卡是虚拟的,这样 vpn 虚拟网卡将以中间人的身份对数据进行加工,从而实现各种效果,而虚拟网卡就是 tun 或者 tap。
比如在家里要通过 VPN 访问内网的一个网站,而要访问的内网网址是 172.31.130.23。通常情况下,VPN 客户端拨入 VPN 服务器后,本机的默认网关会改为 VPN 的 IP 地址,当你访问网站时,具体流程如下:
1、数据包 A 到达网关 1,网关 1 接收到请求后,构造新的数据包 B, (数据包 A 根据路由规则指定到 tun 或 tap 设备(假设设备为 tun0)来接收请求,tun0 会进一步将数据传递给连接在另一端的 VPN 应用,应用接收到数据后做一系列处理,将原来数据包封装为新的包 B),数据包 B 指向由原来的 172.31.130.23 变成了 42.62.43.137,接着将数据包交给物理网卡发送到网关 2。
2、网关 2 接收到数据包后,将其还原为包 A(原理和上一步相同,也是利用了 tun 或 tap 将数据传至应用层再解封还原的过程),最终转到目标服务器,
下面演示了如何在 centos7 中创建一个 tun 设备
第一步,确认内核是否有 tun 模块 modinfo tun
, 如果有的话会输出 tun 相关描述信息;
第二步,确定内核模块是否已经加载,lsmod | grep tun
, 如果没有加载使用modprobe tun
命令加载;
[root@2030-edu-01-no ~]# lsmod | grep tun
tun 31740 0
复制代码
0 表示 tun 设备数量
第三步,查看是否安装了 tunctl, 如果没有安装,参考下面的命令
cat << EOF > /etc/yum.repos.d/nux-misc.repo
> [nux-misc]
> name=Nux Misc
> baseurl=http://li.nux.ro/download/nux/misc/el7/x86_64/
> enabled=0
> gpgcheck=1
> gpgkey=http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro
> EOF
yum -y --enablerepo=nux-misc install tunctl
复制代码
第四步,创建虚拟网卡设备 tunctl -t tap0 -u root
, 此时再运行lsmod | grep tun
,数量由 0 变成了 1,ifconfig -a
也可以查看到刚刚创建的设备
tap0: flags=4098<BROADCAST,MULTICAST> mtu 1500
ether fa:91:9d:65:af:13 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
复制代码
第五步,设置虚拟网卡, 设置网关、ip 等,如 ifconfig tap0 192.168.1.5 netmask 255.255.255.0 promisc
[root@2030-edu-01-no ~]# ifconfig tap0 192.168.1.5 netmask 255.255.255.0 promisc
[root@2030-edu-01-no ~]# ping 192.168.1.5
PING 192.168.1.5 (192.168.1.5) 56(84) bytes of data.
64 bytes from 192.168.1.5: icmp_seq=1 ttl=64 time=0.041 ms
64 bytes from 192.168.1.5: icmp_seq=2 ttl=64 time=0.040 ms
复制代码
veth-pair
veth-pair 是一对的虚拟设备接口,和 tap/tun 设备不同的是,它都是成对出现的。一端连着协议栈,一端彼此相连。
当 veth0 接收到协议栈的数据发送请求后,会将数据发送到 veth1 上去, veth 常常充当桥梁,连接各种虚拟网络设备,比如上文说的 namespace 与 host 以及 namespace 之间的通信就可以使用 veth 实现。下面演示一组 host 与 namespace 实现通信的过程。
使用 ip link add veth0 type veth peer name vethtestns
添加一组设备,直接使用 ip link add type veth
则不指定名称,系统自动生成. (删除使用 ip link delete veth0
, 会同时删除 vethtestns)
使用ip l s vethtestns netns testns
将 vethtestns 设备加入 testns namespace 中
配置 IP, 并启用
ip a a 10.1.1.2/24 dev veth0
ip l s veth0 up
ip netns exec testns ip a a 10.1.1.3/24 dev vethtestns
ip netns exec testns ip l s vethtestns up
复制代码
[root@2030-edu-01-no /]# ip netns exec testns ping 10.1.1.2
PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data.
64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.059 ms
64 bytes from 10.1.1.2: icmp_seq=2 ttl=64 time=0.055 ms
64 bytes from 10.1.1.2: icmp_seq=3 ttl=64 time=0.047 ms
复制代码
veth 实现了两两通信,通信过程简单描述如下,左侧是刚刚演示的 namespace 与主机的通信模型,右侧是两个 namespace 之间的通信模型,但是如果需要多个接口互相通信,veth 就无法胜任了。
Bridge
同样 Bridge 也是虚拟网络设备之一,具有网络设备的通性,普通虚拟设备只有两个端口,一进一出,而 Bridge 由多个端口,数据从任一端口进来,再根据 mac 地址从指定端口出去。可以看出 Bridge 是一套虚拟交换设备,和物理交换机的功能相同。
使用演示
通过命令ip link add name bridge0 type bridge && ip link set bridge0 up
创建并启动一个网桥
[root@2030-edu-01-no ~]# ifconfig bridge0
bridge0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::3cb6:45ff:fe15:94f2 prefixlen 64 scopeid 0x20<link>
ether 3e:b6:45:15:94:f2 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 8 bytes 656 (656.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
复制代码
此时网桥一端连接协议栈,而另一端什么也没接。
上文提到了 veth 设备只能实现两两连接,如果有多个接口需要连接,此时就可以借助 Bridge 来实现。我们假设现在有三个 namespace 要互相通信,那么就需要创建三对 veth 设备,每对设备中的一个接到 namespace 中,另一个接入网桥中,具体的模型如下:
具体步骤为
[root@2030-edu-01-no ~]# ip netns add ns1
[root@2030-edu-01-no ~]# ip netns add ns2
[root@2030-edu-01-no ~]# ip netns add ns3
[root@2030-edu-01-no ~]# ip link add veth1 type veth peer name vethb1
[root@2030-edu-01-no ~]# ip link add veth2 type veth peer name vethb2
[root@2030-edu-01-no ~]# ip link add veth3 type veth peer name vethb3
[root@2030-edu-01-no ~]# ip link set veth1 netns ns1
[root@2030-edu-01-no ~]# ip link set veth2 netns ns2
[root@2030-edu-01-no ~]# ip link set veth3 netns ns3
[root@2030-edu-01-no ~]# ip link set vethb1 master bridge0
[root@2030-edu-01-no ~]# ip link set vethb2 master bridge0
[root@2030-edu-01-no ~]# ip link set vethb3 master bridge0
[root@2030-edu-01-no ~]# ip link set vethb1 up
[root@2030-edu-01-no ~]# ip link set vethb2 up
[root@2030-edu-01-no ~]# ip link set vethb3 up
[root@2030-edu-01-no ~]# ip netns exec ns1 ip addr add 10.1.1.2/24 dev veth1
[root@2030-edu-01-no ~]# ip netns exec ns2 ip addr add 10.1.1.3/24 dev veth2
[root@2030-edu-01-no ~]# ip netns exec ns3 ip addr add 10.1.1.4/24 dev veth3
[root@2030-edu-01-no ~]# ip netns exec ns1 ip link set veth1 up
[root@2030-edu-01-no ~]# ip netns exec ns2 ip link set veth2 up
[root@2030-edu-01-no ~]# ip netns exec ns3 ip link set veth3 up
复制代码
[root@2030-edu-01-no ~]# ip netns exec ns3 ping 10.1.1.2 -c 1
PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data.
64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.098 ms
--- 10.1.1.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.098/0.098/0.098/0.000 ms
[root@2030-edu-01-no ~]# ip netns exec ns3 ping 10.1.1.3 -c 1
PING 10.1.1.3 (10.1.1.3) 56(84) bytes of data.
64 bytes from 10.1.1.3: icmp_seq=1 ttl=64 time=0.106 ms
--- 10.1.1.3 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.106/0.106/0.106/0.000 ms
[root@2030-edu-01-no ~]#
复制代码
通常使用 brctl 工具来管理网桥,使用yum install bridge-utils
命令安装,比如查看网桥信息.
[root@2030-edu-01-no ~]# brctl show bridge0
bridge name bridge id STP enabled interfaces
bridge0 8000.127eaae73032 no vethb1
vethb2
vethb3
复制代码
上面演示的案例中,已经实现了三个 namespace 的互相联通,此时如果在和主机联通,只需要给网桥分配一个同网段的 ip 地址即可,ip addr add 10.1.1.1/24 dev bridge0
。
Bridge 应用
Bridge 目前主要用在虚拟机和 Docker 中,Docker 网络基础后续会专门介绍,先来看看网桥在虚拟机的应用模型。虚拟机通过 tun/tap 或者其它类似的虚拟网络设备,将虚拟机内的网卡同网桥连接起来,以此达到和真实交换机一样的效果,虚拟机发出去的数据包先到达网桥,然后由网桥交给物理网卡发送出去,数据包不需要经过 host 机器的协议栈,效率也比较高。
借助 Linux Bridge 功能,同主机或跨主机的虚拟机之间能够轻松实现通信,也能够让虚拟机访问到外网,这就是我们所熟知的桥接模式,一般在装 VMware 虚拟机或者 VirtualBox 虚拟机的时候,都会提示我们要选择哪种模式,其中常用的一种模式是桥接。
OVS
Bridge 充当虚拟交换机已经能满足网络的通信,但是仍有一些不足的地方,比如网络管理和监控的便利性、数据包寻路和转发的高效性、隧道协议支持类型等。OVS(Open vSwitch)是流行的虚拟交换机之一,扩展了很多高级特性。
参考资料
http://www.360doc.com/content/18/0829/07/44856983_782027344.shtml
https://zhuanlan.zhihu.com/p/73248894
https://www.jianshu.com/p/2a14fe583cdf
https://blog.csdn.net/u012707739/article/details/78163354
https://www.ibm.com/developerworks/cn/linux/l-tuntap/
https://segmentfault.com/a/1190000009249039
https://segmentfault.com/a/1190000009491002
https://www.cnblogs.com/bakari/p/10613710.html
https://www.cnblogs.com/bakari/p/8097439.html
评论 (5 条评论)