Linux命名空间是一个相对较新的内核功能,对于实现容器至关重要。 命名空间将全局系统资源包装到一个抽象中,该抽象只会与命名空间中的进程绑定,从而提供资源隔离。 在本文中,我将讨论网络命名空间并展示一个实际的例子。

      命名空间和cgroups是软件集装箱化(Docker)的大部分新趋势的主要内核技术之一。 简单来说,cgroups是一种计量和限制机制,它们控制您可以使用多少系统资源(CPU,内存)。 另一方面,命名空间限制了您可以看到的内容。 由于命名空间进程有自己的系统资源视图。

       Linux内核提供了6种类型的命名空间:pid,net,mnt,uts,ipc和user。 例如,pid命名空间中的进程只能看到同一命名空间中的进程。 感谢mnt命名空间,可以将进程附加到自己的文件系统(如chroot)。 在本文中,我仅关注网络命名空间。

       如果您已经掌握了命名空间的概念,您可能在这一点上可以直观地了解网络命名空间可能提供的内容。 网络命名空间为命名空间中的所有进程提供了全新的网络堆栈。 这包括网络接口,路由表和iptables规则。


网络命名空间

   从系统的角度来看,当通过clone()syscall创建新进程时,传递标志CLONE_NEWNET将在新进程中创建一个全新的网络命名空间。 从用户的角度来看,我们只需使用工具ip(package is iproute2)来创建一个新的持久网络命名空间:

$ ip netns add ns1

        该命令将创建一个名为ns1的新网络命名空间。 当创建命名空间时,ip命令在/ var / run / netns下为其添加绑定挂载点。 这允许命名空间持续存在,即使没有进程附加它。 列出系统中可用的命名空间:

$ ls /var/run/netns
ns1

或者 使用 ip:

$ ip netns
ns1
   如前所述,网络命名空间包含自己的网络资源:接口,路由表等。我们为ns1添加一个环回接口:
$ ip netns exec ns1 ip link set dev lo up
$ ip netns exec ns1 ping 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.115 ms
  • 第1行在网络名称空间ns1中显示回环接口
  • 第2行在网络命名空间内执行ping 127.0.0.1命令。
启动环回接口的另一种语法可能是:
$ ip netns exec ns1 ifconfig lo up
        然而,我倾向于使用ip命令,因为它已成为Linux中首选的网络工具,废弃旧的但更熟悉的命令ifconfig,route等。请注意,ip需要root权限,因此以root或prepend sudo运行。
        网络命名空间也有自己的路由表:

$ ip netns exec ns1 ip route show

在这一点上,什么也没有返回,因为我们还没有添加任何路由表规则。 一般来说,在网络命名空间中运行的任何命令都由前序开始:

$ ip netns exec <network-namespace>
一个实际的例子
网络命名空间的后果之一是一次只能将一个接口分配给命名空间。 如果根命名空间拥有eth0,它提供对外部世界的访问,则只有根命名空间内的程序可以访问Internet。 解决方案是通过veth对将命名空间与根命名空间进行通信。 vuit对的工作方式就像一条连接两边的跳线。 它由两个虚拟接口组成,其中一个分配给根网络命名空间,另一个生命在网络命名空间中。 相应地设置其IP地址和路由规则,以及在主机端启用NAT将足以提供Internet访问网络命名空间。
      此外,我觉得我现在需要澄清。 我已经阅读了关于物理设备接口只能生活在根命名空间中的网络命名空间的几篇文章。 至少这不是我现在的内核(Linux 3.13)。 我可以将eth0分配给除root之外的命名空间,并且在正确设置时可以从命名空间访问Internet。 然而,一次只能在单个命名空间中生活的界面的限制仍然适用,这是强大到足以通过veth对连接网络命名空间的原因。
       首先,我们来创建一个名为ns1的新的网络命名空间。
# Remove namespace if it exists.
ip netns del ns1 &>/dev/null

# Create namespace
ip netns add ns1
接下来,创建一个veth对。 接口v-eth1将保留在根网络名称空间内,而其对等体v-peer1将被移动到ns1命名空间。
    
# Create veth link.
ip link add v-eth1 type veth peer name v-peer1

# Add peer-1 to NS.
ip link set v-peer1 netns ns1
接下来,为两个接口设置IPv4地址,并将其启动。
# Setup IP address of v-eth1.
ip addr add 10.200.1.1/24 dev v-eth1
ip link set v-eth1 up

# Setup IP address of v-peer1.
ip netns exec ns1 ip addr add 10.200.1.2/24 dev v-peer1
ip netns exec ns1 ip link set v-peer1 up
ip netns exec ns1 ip link set lo up
另外我提出了ns1内的loopback接口。
   现在有必要让离开ns1的所有外部流量通过v-eth1。
ip netns exec ns1 ip route add default via 10.200.1.1
但是这还不够。 与任何主机共享其互联网连接,必须在主机中启用IPv4转发,并启用伪装。
# Share internet access between host and NS.

# Enable IP-forwarding.
echo 1 > /proc/sys/net/ipv4/ip_forward

# Flush forward rules, policy DROP by default.
iptables -P FORWARD DROP
iptables -F FORWARD

# Flush nat rules.
iptables -t nat -F

# Enable masquerading of 10.200.1.0.
iptables -t nat -A POSTROUTING -s 10.200.1.0/255.255.255.0 -o eth0 -j MASQUERADE

# Allow forwarding between eth0 and v-eth1.
iptables -A FORWARD -i eth0 -o v-eth1 -j ACCEPT
iptables -A FORWARD -o eth0 -i v-eth1 -j ACCEPT
如果一切正常,可以从ns1 ping一个外部主机。

$ ip netns exec ns1 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=50 time=48.5 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=50 time=50.8 ms
这是ns1中的路由表在安装后如何看起来:
$ ip netns exec ns1 ip route sh
default via 10.200.1.1 dev v-peer1 
10.200.1.0/24 dev v-peer1  proto kernel  scope link  src 10.200.1.2 

为命名空间运行的每个命令准备ip netns exec序言可能有点乏味。 一旦命名空间中最基本的功能被设置,一个更有趣的可能性是运行一个bash shell并将其附加到网络命名空间中:
$ ip netns exec ns1 /bin/bash --rcfile <(echo "PS1=\"namespace ns1> \"")
namespace ns1> ping www.google.com
PING www.google.com (178.60.128.38) 56(84) bytes of data.
64 bytes from cache.google.com (178.60.128.38): icmp_seq=1 ttl=58 time=17.6 ms
键入exit以结束bash进程并离开网络命名空间。
结论
网络命名空间以及Linux内核提供的其他容器化技术是资源隔离的轻量级机制。 连接到网络命名空间的进程可以看到自己的网络堆栈,而不会干扰系统的其他网络堆栈。
网络名称空间也很容易使用。 可以使用VM设置类似的网络级别隔离。 然而,这似乎是一个更加昂贵的解决方案,在系统资源和时间投入方面来建立这样的环境。 如果您只需要网络级别的进程隔离,则网络命名空间是必须考虑的。
         完整的脚本: 待续





Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐