Skip to content

命名空间 ​

Linux Namespace 是内核提供的一种隔离机制,让不同进程组看到不同的系统资源视图。 容器技术(Docker、containerd 等)的核心隔离能力即基于 Namespace 实现。

Namespace 类型 ​

类型常量隔离的资源引入版本
MountCLONE_NEWNS挂载点(文件系统)2.4.19
UTSCLONE_NEWUTS主机名(hostname)和域名2.6.19
IPCCLONE_NEWIPCSystem V IPC、POSIX 消息队列2.6.19
PIDCLONE_NEWPID进程 ID2.6.24
NetworkCLONE_NEWNET网络设备、IP、路由、端口等2.6.24
UserCLONE_NEWUSER用户 ID 和组 ID3.8
CgroupCLONE_NEWCGROUPcgroup 根目录4.6
TimeCLONE_NEWTIME系统时钟偏移5.6

查看进程的 Namespace ​

每个进程的 namespace 信息保存在 /proc/<pid>/ns/ 下:

bash
# 查看当前 shell 的所有 namespace
ls -la /proc/$$/ns/

# 输出示例
lrwxrwxrwx ... cgroup -> cgroup:[4026531835]
lrwxrwxrwx ... ipc    -> ipc:[4026531839]
lrwxrwxrwx ... mnt    -> mnt:[4026531840]
lrwxrwxrwx ... net    -> net:[4026531992]
lrwxrwxrwx ... pid    -> pid:[4026531836]
lrwxrwxrwx ... time   -> time:[4026531834]
lrwxrwxrwx ... user   -> user:[4026531837]
lrwxrwxrwx ... uts    -> uts:[4026531838]

相同 inode 号(方括号内)表示两个进程共享同一 namespace。

bash
# 比较两个进程是否共享某 namespace
ls -lai /proc/1/ns/net /proc/$$/ns/net

相关系统调用 ​

系统调用说明
clone()创建新进程,通过 flags 指定新建哪些 namespace
unshare()当前进程脱离指定 namespace,加入新建的 namespace
setns()将当前进程加入已有的 namespace

命令行工具 ​

unshare — 在新 namespace 中运行程序 ​

bash
# 在新的 UTS namespace 中修改主机名(不影响宿主机)
unshare --uts bash
hostname container-test
hostname   # 只在此 shell 中生效

# 在新的网络 namespace 中运行 bash
unshare --net bash
ip link    # 只能看到 lo

# 创建完整隔离环境(pid+mount+net+uts+ipc)
unshare --pid --mount --net --uts --ipc --fork bash

nsenter — 进入已有 namespace ​

bash
# 进入 PID 1234 的网络 namespace
nsenter --net --target 1234 bash

# 进入容器的所有 namespace(常用于调试容器)
nsenter --target <container_pid> --mount --uts --ipc --net --pid bash

# 只进入网络 namespace,执行后退出
nsenter --net=/proc/1234/ns/net ip addr

ip netns — 管理网络 namespace ​

bash
# 创建命名网络 namespace
ip netns add myns

# 列出所有命名网络 namespace
ip netns list

# 在指定 namespace 中执行命令
ip netns exec myns ip link
ip netns exec myns ping 8.8.8.8

# 删除
ip netns delete myns

PID Namespace 详解 ​

PID Namespace 允许进程在隔离环境中拥有独立的 PID 空间,容器内 PID 1 是容器的 init。

宿主机 PID   容器内 PID
  1234    →      1      (容器 init)
  1235    →      2
  1236    →      3
  • 子 namespace 中的进程对父 namespace 可见(父可看到子 ns 的进程)
  • 父 namespace 中的进程对子 namespace 不可见

Network Namespace 详解 ​

每个 Network Namespace 有独立的:网络设备、IP 地址、路由表、防火墙规则、/proc/net。

典型用法:通过 veth pair 连接两个 network namespace:

bash
# 创建两个 ns
ip netns add ns1
ip netns add ns2

# 创建 veth pair
ip link add veth1 type veth peer name veth2

# 将两端分别移入对应 ns
ip link set veth1 netns ns1
ip link set veth2 netns ns2

# 配置 IP 并启动
ip netns exec ns1 ip addr add 10.0.0.1/24 dev veth1
ip netns exec ns1 ip link set veth1 up
ip netns exec ns2 ip addr add 10.0.0.2/24 dev veth2
ip netns exec ns2 ip link set veth2 up

# 测试互通
ip netns exec ns1 ping 10.0.0.2

Mount Namespace 详解 ​

Mount Namespace 隔离挂载点视图,子 namespace 对挂载点的修改不影响父 namespace。

挂载传播模式(/proc/<pid>/mountinfo 中显示):

模式说明
shared双向传播,挂载/卸载同步到 peer group
slave单向传播,只接收 master 的变化
private不传播(默认)
unbindable不传播,也不可 bind mount
bash
# 创建私有 mount namespace 并挂载 tmpfs(不影响宿主机)
unshare --mount bash
mount -t tmpfs tmpfs /mnt
ls /mnt       # 隔离视图

User Namespace 详解 ​

User Namespace 隔离 UID/GID,允许非 root 用户在 namespace 内拥有 root 权限。

bash
# 非 root 用户创建 user namespace,mapping 自己为 ns 内 root
unshare --user --map-root-user bash
id    # 显示 uid=0(root)(只在此 ns 内有效)

UID/GID 映射文件:

  • /proc/<pid>/uid_map
  • /proc/<pid>/gid_map

格式:ns_id host_id count