namespace(命名空间)和cgroup是软件容器化(想想Docker)趋势中的两个主要内核技术。简单来说,cgroup是一种对进程进行统一的资源监控和限制,它控制着你可以使用多少系统资源(CPU,内存等)。而namespace是对全局系统资源的一种封装隔离,它通过Linux内核对系统资源进行隔离和虚拟化的特性,限制了您可以看到的内容。

Linux 3.8内核提供了6种类型的命名空间:Process ID (pid)、Mount (mnt)、Network (net)、InterProcess Communication (ipc)、UTSUser ID (user)。例如,pid命名空间内的进程只能看到同一命名空间中的进程。mnt命名空间,可以将进程附加到自己的文件系统(如chroot)。在本文中,我只关注网络命名空间 Network (net)。

网络命名空间为命名空间内的所有进程提供了全新隔离的网络协议栈。这包括网络接口,路由表和iptables规则。通过使用网络命名空间就可以实现网络虚拟环境,实现彼此之间的网络隔离,这对于云计算中租户网络隔离非常重要,Docker中的网络隔离也是基于此实现的,如果你已经理解了上面所说的,那么我们进入正题,介绍如何使用网络命名空间。

准备工作以及环境:

系统:centos 7系统(因为namespace是linux 内核3.8开始支持的,所以要想进行本文的实验,请用linux内核3.8及以上版本)

安装iproute(yum install iproute2,其他平台请自行下载iproute2工具)

注意:本文作者使用ip命令,因为它已成为Linux中的首选网络工具,旧的ifconfig,route等命令也已经被淘汰了。请注意,ip命令需要root权限,因此ip命名要以root用户身份运行或预先添加sudo。

1.新增一个网络命名空间(名称netns1)

# ip netns add netns0

解释:从系统的角度来看,当通过clone()系统调用创建一个新进程时,传递标志CLONE_NEWNET将在新进程中创建一个全新的网络命名空间。从用户的角度来看,我们只需使用工具ip(iproute2工具包)来创建一个新的持久网络命名空间:此命令将创建一个名为netns0的新网络命名空间。创建命名空间后,ip命令会在/var/run/netns目录下增加一个netns0文件(In Linux, Everything is a File)。

# ls /var/run/netns
netns0

 

2.使用命令 ip netns show或ip netns list,列出系统中可用的命名空间:

# ip netns show
netns0

 

3.如前所述,网络命名空间包含自己的网络资源:接口,路由表等。默认添加网络命名空间netns1后,在netns1命名空间中会自动添加一个loopback(回环)接口:

首先开启回环口lo网卡

# ip netns exec netns0 ip link set lo up

 

查看网络lo回环口信息:

# ip netns exec netns0 ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
  link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  inet 127.0.0.1/8 scope host lo
      valid_lft forever preferred_lft forever
  inet6 ::1/128 scope host
      valid_lft forever preferred_lft forever

ping 127.0.0.1
# ip netns exec netns0 ping 127.0.0.1 -c 3
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.016 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.033 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.033 ms

--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.016/0.027/0.033/0.009 ms

既然已经了解了网络命名空间后,那我们通过两个隔离的命名空间来实现互通(模拟两台主机直接的互通,作为网络开发人员,有时候需要很多台机器来测试网络,但是只有一台机器,那么有了网络命名空间这个特性,完全就可以在一台主机上模拟多台主机之间互通的网络测试),如下所示:

内核中网络命名空间 NET NAMESPACE 的作用 namespace network_网线

 


 

# ip netns add netns1
# ip netns show
netns0
netns1

  

模拟两台机器,这时候就需要有一根网线连接两台主机,此时就出现了新的神器veth(虚拟以太网),veth是一种总是成对出现的虚拟网络设备。你可以把这对看作一根物理网线,通过这根线一端发送的所有东西都会在另一端出来。(veth详细解释会在后续文章中详细介绍,本文就先不详细介绍了)

模拟创建一根网线:

# ip link add name vnet0 type veth peer name vnet1
# ip a
1: vnet1@vnet0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
  link/ether 52:42:c3:02:54:38 brd ff:ff:ff:ff:ff:ff
2: vnet0@vnet1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
  link/ether a6:20:57:dc:c6:dd brd ff:ff:ff:ff:ff:ff

 

可以看见创建了一个vnet1@vnet0和vnet0@vnet1两张虚拟网卡,现在要把一头插到netns0主机中,另外一套插在netns1主机中,命令如下:

# ip link set vnet0 netns netns0
# ip link set vnet1 netns netns1

  

给两个主机分别配置ip,192.168.0.2和192.168.0.3,然后互ping

# ip netns exec netns0 ip link set vnet0 up
# ip netns exec netns1 ip link set vnet1 up
# ip netns exec netns0 ip a add 192.168.0.2 dev vnet0
# ip netns exec netns1 ip a add 192.168.0.3 dev vnet1
# ip netns exec netns0 ping 192.168.0.3
connect: 网络不可达

  

 
网络不可达???what??为什么会这样?其实是因为没有路由(查看路由命令如下)

# ip netns exec netns0 ip r

 

添加路由

# ip netns exec netns0 ip r add 192.168.0.3 dev vnet0
# ip netns exec netns1 ip r add 192.168.0.2 dev vnet1

 

在netns0中去ping netns1主机

# ip netns exec netns0 ping 192.168.0.3 -c 2
PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data.
64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.021 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.047 ms

--- 192.168.0.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.021/0.034/0.047/0.013 ms