命名空间
Linux Namespace 是内核提供的一种隔离机制,让不同进程组看到不同的系统资源视图。 容器技术(Docker、containerd 等)的核心隔离能力即基于 Namespace 实现。
Namespace 类型
| 类型 | 常量 | 隔离的资源 | 引入版本 |
|---|---|---|---|
| Mount | CLONE_NEWNS | 挂载点(文件系统) | 2.4.19 |
| UTS | CLONE_NEWUTS | 主机名(hostname)和域名 | 2.6.19 |
| IPC | CLONE_NEWIPC | System V IPC、POSIX 消息队列 | 2.6.19 |
| PID | CLONE_NEWPID | 进程 ID | 2.6.24 |
| Network | CLONE_NEWNET | 网络设备、IP、路由、端口等 | 2.6.24 |
| User | CLONE_NEWUSER | 用户 ID 和组 ID | 3.8 |
| Cgroup | CLONE_NEWCGROUP | cgroup 根目录 | 4.6 |
| Time | CLONE_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 bashnsenter — 进入已有 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 addrip 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 mynsPID 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.2Mount 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