命名空间

Namespace是Linux提供的一种对于系统全局资源的隔离机制;从进程的视角来看,同一个namespace中的进程看到的是该namespace自己独立的一份全局资源,这些资源的变化只在本namespace中可见,对其他namespace没有影响。容器就是采用namespace机制实现了对网络,进程空间等的隔离。不同的Container(在K8S中是以Pod为单位)属于不同namespace,实现了Container或Pod之间的资源互相隔离,互不影响。

命名空间将全局系统资源包装在一个抽象中,使命名空间中的进程看起来拥有自己的全局资源的独立实例。对全局资源的更改对作为命名空间成员的其他进程可见,但对其他进程不可见。名称空间的一个用途是实现容器。

Linux提供了以下七种namespace:

  • Cgroup:Cgroup root directory
  • IPC:System V IPC, POSIX message queues
  • Network:Network devices, stacks, ports, etc.
  • Mount:Mount points
  • PID:Process IDs
  • User:User and group IDs
  • UTS:Hostname and NIS domain name

Network namespace允许你在Linux中创建相互隔离的网络视图,每个网络命名空间都有自己独立的网络配置,包括:网络设备、路由表、IPTables规则,路由表、网络协议栈等。

新建的网络名字空间与主机默认网络名字空间之间是隔离的。我们平时默认操作的是主机的默认网络名字空间。

不同的Network Namespace的资源互相不可见,彼此之间无法通信。

VETH

VETH(Virtual Ethernet)是Linux提供的另外一种特殊的网络设备,中文称为虚拟网卡接口。它总是成对出现,要创建就创建一个pair。一个Pair中的veth就像一个网络线缆的两个端点,数据从一个端点进入,必然从另外一个端点流出。每个veth都可以被赋予IP地址。

veth设备是虚拟以太网设备。它们可以充当网络命名空间之间的隧道,以创建到另一个命名空间中的物理网络设备的桥,但也可以用作独立的网络设备。veth设备总是成对互连。可以使用以下命令创建配对:

# ip link add <p1-name> type veth peer name <p2-name> 

在上面的例子中,p1-name和p2-name是分配给两个相连端点的名称。在设备对中的一台设备上传输的数据包会立即被另一台设备接收。当任一设备出现故障时,设备对的链路状态为故障。

veth和tap/tun类似,也是linux提供的一种虚拟网络设备;但与tap/tun不同的是,veth总是成对出现的,从一端进入的数据包将会在另一端出现,因此又常常称为veth pair。我们可以把veth pair看成一条网线两端连接的两张以太网卡。

由于network namespace隔离了网络相关的全局资源,因此从网络角度来看,一个network namespace可以看做一个独立的虚机;即使在同一个主机上创建的两个network namespace,相互之间缺省也是不能进行网络通信的。

管理网络命名空间

Network Namespace是Linux内核提供的功能,本文借助ip命令来完成各种操作。ip命令来自于iproute2安装包,一般系统默认安装,如果没有的话,可自行安装。

查看帮助信息

ip命令管理的功能很多,和Network Namespace有关的操作都在其子命令ip netns下进行的,可以通过ip netns help查询命令帮助信息:

$ ip netns help
Usage:  ip netns list
        ip netns add NAME
        ip netns attach NAME PID
        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 [target-nsid POSITIVE-INT] [nsid POSITIVE-INT]
NETNSID := auto | POSITIVE-INT

创建网络命名空间

通过ip netns add命令创建一个名为ns0的网络命名空间:

$ ip netns add ns0

查询网络命名空间

通过ip netns list命令查询网络命名空间列表:

$ ip netns list
ns0

查看网络命令空间所在的目录:

$ ls /var/run/netns/
ns0

注意:新创建的Network Namespace会出现在/var/run/netns/目录下。如果需要管理其他不是ip netns创建的network namespace,只要在这个目录下创建一个指向对应network namespace文件的链接即可。

删除网络命名空间

通过ip netns delete NAME命令删除网络命名空间:

$ ip netns delete ns0

操作网络命名空间

对于每个Network Namespace来说,它会有自己独立的网卡、路由表、ARP表、iptables等和网络相关的资源。

ip命令提供了ip netns exec子命令可以在对应的Network Namespace中执行命令。

查看网络命名空间ns0的网卡信息:

$ ip netns exec ns0 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

$ ip netns exec ns0 ping 127.0.0.1
ping: connect: Network is unreachable

每个namespace在创建时会自动创建一个回环接口lo,默认不启用。它的作用和Linux系统中默认看到的lo一样,都是为了实现loopback通信,如果希望lo口能工作,可以通过下面的命令启用它。

启用lo回环网卡:

$ ip netns exec ns0 ip link set lo up

$ ip netns exec ns0 ip addr
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

$ ip netns exec ns0 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.014 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.022 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.028 ms

--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2091ms
rtt min/avg/max/mdev = 0.014/0.021/0.028/0.005 ms

再次ping回环网卡,发现已经可以ping通了。

ns0中打开一个shell终端:

$ ip netns exec ns0 /bin/bash

$ ip addr
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

$ exit
exit

通过执行ip netns exec ns0 /bin/bash进入ns0的shell终端,后面所有的命令都在这个Network Namespace中执行,好处是不用每次执行命令时都要带上ip netns exec,缺点是我们无法清楚知道自己当前所在的shell,容易混淆。

可以采用下面的方法解决:

$ ip netns exec ns0 /bin/bash --rcfile <(echo "PS1=\"ns0> \"")
ns0>

Network Namespace之间的通信

默认情况下,network namespace是不能和主机网络,或者其他network namespace通信的。

可以使用Linux提供的veth pair来完成通信,veth pair你可以理解为使用网线连接好的两个接口,把两个端口放到两个namespace中,那么这两个namespace就能打通。

接下来我们通过实验进行验证:

创建veth pair

创建veth pair:

$ ip link add type veth

$ ip link
21: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 7e:8d:e9:81:ee:6e brd ff:ff:ff:ff:ff:ff
22: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 8e:fd:3c:5b:fd:52 brd ff:ff:ff:ff:ff:ff

可以看到,此时系统中新增了一对veth pair:veth0和veth1,需要记住的是veth pair无法单独存在,删除其中一个,另一个也会自动消失。

如果需要指定veth pair两个端点的名称,可以使用下面的命令:

$ ip link add veth001 type veth peer name veth002

$ ip link
23: veth002@veth001: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether ce:e8:7d:d3:31:07 brd ff:ff:ff:ff:ff:ff
24: veth001@veth002: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 12:bc:27:1c:64:cc brd ff:ff:ff:ff:ff:ff

$ ip link delete veth001

创建Network Namespace

前面我们已创建了一个名为ns0的Network Namespace,下面再创建一个名称为ns1的网络命名空间。

$ ip netns add ns1

$ ip netns list
ns1
ns0

把veth pair分别加入到这两个namespace中

将veth0加入到ns0:

$ ip link set veth0 netns ns0

将veth1加入到ns1:

$ ip link set veth1 netns ns1

分别为这对veth pair配置上ip地址,并启用

为veth0配置IP,并启用该虚拟网卡:

$ ip netns exec ns0 ip addr add 192.168.1.1/24 dev veth0

$ ip netns exec ns0 ip link set veth0 up

$ ip netns exec ns0 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
21: veth0@if22: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 7e:8d:e9:81:ee:6e brd ff:ff:ff:ff:ff:ff link-netns ns1
    inet 192.168.1.1/24 scope global veth0
       valid_lft forever preferred_lft forever
    inet6 fe80::7c8d:e9ff:fe81:ee6e/64 scope link
       valid_lft forever preferred_lft forever

为veth1配置IP,并启用该虚拟网卡:

$ ip netns exec ns1 ip addr add 192.168.1.2/24 dev veth1

$ ip netns exec ns1 ip link set veth1 up

$ ip netns exec ns1 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
22: veth1@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 8e:fd:3c:5b:fd:52 brd ff:ff:ff:ff:ff:ff link-netns ns0
    inet 192.168.1.2/24 scope global veth1
       valid_lft forever preferred_lft forever
    inet6 fe80::8cfd:3cff:fe5b:fd52/64 scope link
       valid_lft forever preferred_lft forever

验证两个Network Namespace之间的互通

在ns0中ping通ns1:

$ ip netns exec ns0 ping 192.168.1.2 -c 2
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=0.447 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=0.031 ms

--- 192.168.1.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1009ms
rtt min/avg/max/mdev = 0.031/0.239/0.447/0.208 ms

在ns1中ping通ns0:

$ ip netns exec ns1 ping 192.168.1.1 -c 2
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.019 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.032 ms

--- 192.168.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.019/0.025/0.032/0.006 ms

可以看到,veth pair成功实现了两个不同Network Namespace之间的网络交互。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐