命名空间
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之间的网络交互。