Linux命名空间
Linux命名空间是一种资源隔离机制,Docker for Linux就是主要利用Linux命名空间实现资源隔离的。
目前有八种Linux命名空间。下表列出了这些命名空间及其隔离的资源类型,Flag是用于一些系统调用的标志,Page指示如何找到相关命名空间的详细文档。
Namespace | Flag | Page | Isolates |
---|---|---|---|
Cgroup | CLONE_NEWCGROUP | cgroup_namespaces(7) | Cgroup root directory |
IPC | CLONE_NEWIPC | ipc_namespaces(7) | System V IPC, |
POSIX message queues | |||
Network | CLONE_NEWNET | network_namespaces(7) | Network devices, |
stacks, ports, etc. | |||
Mount | CLONE_NEWNS | mount_namespaces(7) | Mount points |
PID | CLONE_NEWPID | pid_namespaces(7) | Process IDs |
Time | CLONE_NEWTIME | time_namespaces(7) | Boot and monotonic clocks |
User | CLONE_NEWUSER | user_namespaces(7) | User and group IDs |
UTS | CLONE_NEWUTS | uts_namespaces(7) | Hostname and NIS domain name |
网络命名空间与veth
本文重点探究Linux网络命名空间,Linux网络命名空间可以隔离网络设备、套接字(端口号)、网络协议栈、路由表、防火墙规则等网络资源。也可以为每个网络命名空间提供不同的DNS配置,比如假如存在一个名为 nstest
的网络命名空间,则对于nstest
, DNS解析将会首先参照 /etc/netns/nstest/resolv.conf
中的DNS配置,其次才是全局DNS配置 /etc/resolv.conf
。
虚拟网络设备 veth
可用于连接两个网络命名空间,可以为隔离于网络命名空间中的进程提供访问外部网络的能力。
虚拟网络设备 veth
总是成对出现的,两个 veth
一端互相连接在一起,另一端与网络协议栈相连,一个 veth
收到的数据包会立即被转发到与之相连的 veth
。
1 | +----------------------------------------------------------------+ |
网络命名空间的介绍参考network_namespaces(7),网络命名空间的的创建、删除与操纵,参考ip-netns(8),veth
的介绍参考veth(4)和veth of Linux Virtual Network Device。
网络命名空间相关操作
可以用 ip netns
进行网络命名空间的创建、删除、在特定网络命名空间执行进程等操作,用 ip link
可以进行在特定命名空间创建接口,为接口变更命名空间等操作。下面给出几个示例:
- 创建网络命名空间
nstest
1
ip netns add nstest
- 删除命名空间
nstest
1
ip netns del nstest
- 在网络命名空间
nstest
中执行命令1
ip netns exec nstest COMMAND
- 创建虚拟网络接口对
veth0s0
和veth0s1
1
ip link add veth0s0 type veth peer veth0s1
- 将虚拟网络接口
veth0s1
移动至网络命名空间nstest
1
2
3# 一旦将网络接口移动至 nstest ,就无法在默认网络命名空间中看到该网络接口
# 因为一个网络接口只能属于一个网络命名空间,物理网络接口也是如此!!!
ip link set veth0s1 netns nstest - 将虚拟网络接口
veth0s1
移动至默认网络命名空间1
2
3
4# 由于网络接口 veth0s1 属于 nstest ,所以需要在该网络命名空间中操作
# 因为各个网络命名空间中的接口时相互隔离的
# 其中 1 指的是默认网络命名空间的 id
ip netns exec nstest ip link set veth0s1 netns 1
实验测试
实验一:利用veth
连通两个网络命名空间
大致流程为:
- 创建新的网络命名空间
nstest
- 利用
veth
虚拟网络接口对连接默认网络命名空间和nstest
- 为
veth
配置IP进行测试
配置命令如下:
1 | ip netns add nstest |
配置好之后,可以使用 ip addr
确认一下网络情况:
下图是我电脑上的情况,可以看到 nstest
只存在 lo
和 veth0s1
,它与默认网络命名空间的接口是互相隔离的。
然后就可以实现默认网络命名空间与 nstest
之间的通信。
可以通过ping测试:1
2ping 192.168.11.101
ip netns exec nstest ping 192.168.11.100
实验一拓展配置
首先介绍一下如何捕获经过网络接口的数据包,在Linux上可以使用 tcpdump
捕获数据包,如果只是想要捕获特定接口的数据包,可以使用 tcpdump -lni interface_name
。
下面是实验一进行ping测试的抓包截图:
现在nstest
和默认网络命名空间可以互相ping通了,那么nstest
能否访问外部网络呢?结果是不可以。
首先,如果想要访问 baidu.com
,需要配置好网关,并且,由于我们希望访问域名,也就需要配置好DNS解析服务。
那么,新建的网络命名空间中的网关和DNS配置又是怎么的呢?可以通过 ip route
查看网关。而DNS配置仍是通过 /etc/resolv.conf
文件控制,如果仅仅使用 /etc/resolv.conf
控制DNS,那么理论上 nstest
中的DNS配置是与默认网络命名空间相同的,不过如今大部分Linux发行版默认情况下都默认安装了网络服务相关守护进程,Ubuntu Server 20.04中使用的是netplan服务,所以说了这么多,我也不太清楚nstest
中默认的DNS配置是怎么的,可能与网络接口的IP地址配置有关系,如果采用DHCP,应该会根据DHCP服务器返回的结果设置DNS,具体可以查看netplan配置文件。
首先查看 nstest
的网关配置,下图顺便展示了默认网络命名空间的路由信息,用于突出网络命名空间之间路由表的隔离性:
可以看到, nstest
中未配置默认路由,所以自然无法访问 baidu.com
了,下面为它配置默认路由,这里我们将 veth0s0
的IP地址 192.168.11.100
设为 nstest
中的默认网关。
1 | ip netns exec nstest ip route add default via 192.168.11.100 |
然后检查 nstest
的路由表就会看到默认路由,那么我们现在ping 114.114.114.114
和 baidu.com
,看能否通过 tcpdump
抓到包。
可以看到发往 114.114.114.114
的 ICMP echo request
,但是没有回应,也有发往 127.0.0.53
的DNS查询,同样没有回应。(DNS查询应该和DNS配置有关系,这里不深究了)
这里要关注的一点是,veth0s0
收到了来自 nstest
中 veth0s1
的数据包,如果进一步配置NAT,veth0s1
就可以访问外部网络了。其实使用桥接网络模式的docker容器就是利用类似的技术访问外部网络的,只不过docekr容器并非直接配置NAT,而是将对端veth接入了docker网桥,再进一步通过网桥访问外部网络。
接下来,使用iptables为配置NAT:
1 | IFACE=ens33 # 可以访问外网的接口名称 |
然后 nstest
就可以访问外部网络了。
参考资源
namespaces(7) — Linux manual page
network_namespaces(7) — Linux manual page
ip-netns(8) — Linux manual page
veth(4) — Linux manual page
veth of Linux Virtual Network Device
Setup a network namespace with Internet access
A tcpdump Tutorial with Examples — 50 Ways to Isolate Traffic