:::info Linux 命名空间(Namespaces)是 Linux 内核中的一种机制,它能够为进程提供一个独立的命名空间,使得这个进程看到的系统资源与其他进程看到的资源隔离开来。 ::: Linux 系统中目前有 6 种命名空间:

  • Mount namespace(挂载命名空间):每个进程都有自己的挂载点,所以每个进程看到的文件系统都是独立的。
  • UTS namespace(主机名命名空间):每个进程都有自己的主机名和域名,所以每个进程看到的主机名都是独立的。
  • IPC namespace(进程间通信命名空间):每个进程都有自己的 System V IPC 对象和 POSIX 消息队列,所以每个进程间的 IPC 通信都是独立的。
  • Network namespace(网络命名空间):每个进程都有自己的网络设备、IP 地址、路由表和 iptables 规则,所以每个进程看到的网络环境都是独立的。
  • PID namespace(进程 ID 命名空间):每个进程都有自己的进程 ID 编号,所以每个进程看到的进程树都是独立的。
  • User namespace(用户命名空间):每个进程都有自己的用户 ID 和组 ID,所以每个进程看到的用户和组信息都是独立的。

命名空间机制可以让我们在一个系统上运行多个隔离的环境,每个环境中的进程都有自己独立的命名空间。这个机制在容器技术中非常重要,例如 Docker 就是使用命名空间机制来实现容器隔离的。

Linux 网络命名空间

(Network namespace)是 Linux 内核中的一种机制,它能够为进程提供一个独立的网络命名空间,使得这个进程看到的网络设备、IP 地址、路由表和 iptables 规则与其他进程看到的网络信息隔离开来。
在 Linux 系统中,每个进程都有自己的网络命名空间。如果一个进程创建了一个新的网络命名空间,那么在这个新的网络命名空间中,进程看到的网络信息就与原来的命名空间隔离开来。
Linux 网络命名空间提供了如下功能:

  • 多个进程可以在同一台机器上使用同一个端口,因为每个进程看到的端口都是独立的。
  • 多个进程可以在同一台机器上使用同一个 IP 地址,因为每个进程看到的 IP 地址都是独立的。
  • 多个进程可以在同一台机器上运行同一个服务,因为每个进程看到的服务都是独立的。

Linux 网络命名空间是容器技术中非常重要的一部分,例如 Docker 就是使用网络命名空间来实现容器隔离的。

示例1

下面是一个使用 Linux 网络命名空间的示例,这个示例演示了如何在同一台机器上运行两个 webserver,两个 webserver 看到的网络环境是隔离的:

  1. # 创建一个新的网络命名空间
  2. ip netns add webserver1
  3. # 在新的网络命名空间中创建一个虚拟网络接口
  4. ip link add veth0 type veth peer name veth1
  5. # 将 veth0 接口放到 webserver1 命名空间中
  6. ip link set veth0 netns webserver1
  7. # 在 webserver1 命名空间中配置 IP 地址
  8. ip netns exec webserver1 ip addr add 192.168.0.1/24 dev veth0
  9. # 在 webserver1 命名空间中启动 webserver
  10. ip netns exec webserver1 python3 -m http.server 8080
  11. # 创建另一个新的网络命名空间
  12. ip netns add webserver2
  13. # 将 veth1 接口放到 webserver2 命名空间中
  14. ip link set veth1 netns webserver2
  15. # 在 webserver2 命名空间中配置 IP 地址
  16. ip netns exec webserver2 ip addr add 192.168.0.2/24 dev veth1
  17. # 在 webserver2 命名空间中启动 webserver
  18. ip netns exec webserver2 python3 -m http.server 8080

这个示例中,我们在同一台机器上运行了两个 webserver,两个 webserver 看到的网络环境是隔离的。你可以通过访问 http://192.168.0.1:8080http://192.168.0.2:8080 来访问这两个 webserver。

示例2

下面是另一个使用 Linux 网络命名空间的示例,这个示例演示了如何在同一台机器上运行两个隔离的 web 服务器,并且演示了如何使用 iptables 规则来限制进出流量:

  1. # 创建一个新的网络命名空间
  2. ip netns add webserver1
  3. # 在新的网络命名空间中创建一个虚拟网络接口
  4. ip link add veth0 type veth peer name veth1
  5. # 将 veth0 接口放到 webserver1 命名空间中
  6. ip link set veth0 netns webserver1
  7. # 在 webserver1 命名空间中配置 IP 地址
  8. ip netns exec webserver1 ip addr add 192.168.0.1/24 dev veth0
  9. # 在 webserver1 命名空间中启动 web 服务器
  10. ip netns exec webserver1 python3 -m http.server 8080
  11. # 在 webserver1 命名空间中设置 iptables 规则,只允许来自 192.168.0.2 的流量进入
  12. ip netns exec webserver1 iptables -A INPUT -s 192.168.0.2 -j ACCEPT
  13. ip netns exec webserver1 iptables -A INPUT -j DROP
  14. # 创建另一个新的网络命名空间
  15. ip netns add webserver2
  16. # 将 veth1 接口放到 webserver2 命名空间中
  17. ip link set veth1 netns webserver2
  18. # 在 webserver2 命名空间中配置 IP 地址
  19. ip netns exec webserver2 ip addr add 192.168.0.2/24 dev veth1
  20. # 在 webserver2 命名空间中启动 web 服务器
  21. ip netns exec webserver2 python3 -m http.server 8080
  22. # 在 webserver2 命名空间中设置 iptables 规则,只允许流量出发到 192.168.0.1
  23. ip netns exec webserver2 iptables -A OUTPUT -d 192.168.0.1 -j ACCEPT
  24. ip netns exec webserver2 iptables -A OUTPUT -j DROP

这个示例中,我们在同一台机器上运行了两个 web 服务器,两个 web 服务器看到的网络环境是隔离的。我们还使用 iptables 规则来限制了进出流量。
你可以通过访问 http://192.168.0.1:8080http://192.168.0.2:8080 来访问这两个 web 服务器。此时,你会发现只能从 webserver2 访问 webserver1,而不能从 webserver1 访问 webserver2。

Linux 挂载命名空间

示例1

下面是一个使用 Linux 挂载命名空间的示例,这个示例演示了如何在同一台机器上运行两个隔离的进程,两个进程看到的文件系统是隔离的:

  1. # 创建一个新的挂载命名空间
  2. unshare -m
  3. # 在新的挂载命名空间中挂载一个文件系统
  4. mount -t tmpfs tmpfs /mnt
  5. # 在新的挂载命名空间中运行进程
  6. chroot /mnt bash
  7. # 在原来的挂载命名空间中再次运行进程
  8. bash

这个示例中,我们在同一台机器上运行了两个进程,两个进程看到的文件系统是隔离的。你可以在新的挂载命名空间中挂载一个新的文件系统,并在新的文件系统中运行进程。

示例2

下面是另一个使用 Linux 挂载命名空间的示例,这个示例演示了如何在同一台机器上运行两个隔离的进程,两个进程看到的文件系统是隔离的,并且演示了如何使用 pivot_root 命令来更改根文件系统:

  1. # 创建一个新的挂载命名空间
  2. unshare -m
  3. # 在新的挂载命名空间中挂载一个文件系统
  4. mount -t tmpfs tmpfs /mnt
  5. # 在 /mnt 中创建一个新的目录作为根文件系统
  6. mkdir /mnt/rootfs
  7. # 将根文件系统挂载到 /mnt/rootfs 中
  8. mount -o bind / /mnt/rootfs
  9. # 使用 pivot_root 命令将根文件系统更改为 /mnt/rootfs
  10. pivot_root /mnt/rootfs /mnt/rootfs/mnt
  11. # 删除原来的根文件系统
  12. umount /mnt
  13. # 在新的根文件系统中运行进程
  14. chroot / bash
  15. # 在原来的挂载命名空间中再次运行进程
  16. bash

这个示例中,我们在同一台机器上运行了两个进程,两个进程看到的文件系统是隔离的。我们还使用了 pivot_root 命令来更改根文件系统。