1. PID Namespace(进程隔离)
sudo unshare --pid --fork --mount-proc /bin/bash
这条命令的作用是:
"以 root 身份启动一个新的、与外部隔离的 PID 命名空间,并在里面运行一个干净的 /bin/bash。"
逐段拆开看:
-
sudo
用超级用户权限执行后面的命令,否则创建新的命名空间会失败。
-
unshare
Linux 工具,用来"不共享"某些内核命名空间,即让新启动的进程拥有自己独立的命名空间副本。
-
--pid
告诉 unshare 新建一个 PID 命名空间。
在新空间内,该进程看到的 PID 从 1 重新开始,外部进程对它不可见,反之亦然。
-
--fork
unshare 本身不进入新命名空间,它只是让"接下来要执行的程序"进入。
加上 --fork 后,unshare 会先 fork 出一个子进程,子进程再进入新命名空间并执行后面的命令。
如果不加 --fork,unshare 会报错,因为 PID 命名空间要求进程一进入就 fork 一次,保证新空间里的第一个进程 PID=1。
-
--mount-proc
进入新 PID 命名空间后,把 /proc 重新挂载一份,这样 ps、top 等工具看到的是新空间里的进程列表,而不是宿主机的。
-
/bin/bash
最终在新命名空间里启动的程序,这里就是一个交互式 shell。
在这个 shell 里执行 ps -ef 只会看到寥寥几个进程(init 即 bash 本身、以及它再启动的子进程),宿主机上的其他进程完全看不见。
如图左为新空间,只有两个进程

二者看到的文件空间还是相同的

退出命名空间

2. Network Namespace(网络隔离)
Docker底层-Namespaces(网络隔离)-CSDN博客
用例:创建独立的网络栈,容器有自己的网络接口。
# 创建新的 Network Namespace
sudo ip netns add test-ns
# 进入 Network Namespace
sudo ip netns exec test-ns /bin/bash
# 查看网络接口(只有 lo)
ip addr show
# 添加虚拟网络接口
sudo ip netns exec test-ns ip link add veth0 type veth peer name veth1
# 配置 IP 地址
sudo ip netns exec test-ns ip addr add 10.0.0.1/24 dev veth0
sudo ip netns exec test-ns ip link set veth0 up
# 删除 Network Namespace
sudo ip netns delete test-ns
完整示例:创建两个容器并连接它们
# 创建两个 Network Namespace
sudo ip netns add container1
sudo ip netns add container2
# 创建 veth pair
sudo ip link add veth1 type veth peer name veth2
# 将 veth 接口放入各自的 Namespace
sudo ip link set veth1 netns container1
sudo ip link set veth2 netns container2
# 配置 IP 地址
sudo ip netns exec container1 ip addr add 10.0.0.1/24 dev veth1
sudo ip netns exec container2 ip addr add 10.0.0.2/24 dev veth2
# 启动接口
sudo ip netns exec container1 ip link set veth1 up
sudo ip netns exec container2 ip link set veth2 up
# 测试连通性
sudo ip netns exec container1 ping 10.0.0.2
# 清理
sudo ip netns delete container1
sudo ip netns delete container2
3. Mount Namespace(文件系统隔离)
用例:创建独立的挂载点视图,容器有自己的根文件系统。
# 创建新的 Mount Namespace
sudo unshare --mount /bin/bash
# 创建临时挂载点
mkdir /tmp/myroot
sudo mount --bind /tmp/myroot /tmp/myroot
sudo mount --make-private /tmp/myroot
# 创建 chroot 环境
sudo mount --bind /tmp/myroot /tmp/myroot
sudo chroot /tmp/myroot /bin/bash
# 退出
exit
实际用例:创建类似容器的根文件系统
# 创建目录结构
mkdir -p /tmp/container-root/{bin,lib,etc,home}
# 创建新的 Mount Namespace
sudo unshare --mount --pid --fork /bin/bash
# 使用 pivot_root 切换根文件系统(类似 Docker)
cd /tmp/container-root
sudo mount --bind /tmp/container-root /tmp/container-root
sudo pivot_root /tmp/container-root /tmp/container-root/oldroot
Docker底层-Mount Namespace-CSDN博客
4. UTS Namespace(主机名隔离)
用例:容器可以有自己的主机名
# 创建新的 UTS Namespace
sudo unshare --uts /bin/bash
# 修改主机名(只影响当前 Namespace)
hostname my-container
hostname
# 输出: my-container
# 退出后,主机名恢复
exit
hostname
# 主机名恢复为原来的值
5. IPC Namespace(进程间通信隔离)
用例:隔离 System V IPC 和 POSIX 消息队列。
# 创建新的 IPC Namespace
sudo unshare --ipc /bin/bash
# 创建共享内存
ipcmk -M 1024
# 输出: shmid: 0
# 查看共享内存(只看到当前 Namespace 的)
ipcs -m
# 退出 Namespace
exit
# 在主机上查看,看不到容器内的共享内存
ipcs -m
Docker底层-IPC Namespace(进程间通信隔离)-CSDN博客
6. User Namespace(用户隔离)
用例:在容器内以 root 运行,但在主机上是普通用户。
# 创建新的 User Namespace(不需要 root)
unshare --user --map-root-user /bin/bash
# 查看用户 ID
id
# 输出: uid=0(root) gid=0(root) groups=0(root)
# 但在主机上,你还是普通用户
# 在另一个终端查看
ps aux | grep bash
# 可以看到进程属于普通用户
Docker底层-User Namespace -CSDN博客
7. 组合使用多个 Namespaces
用例:创建类似容器的环境。
# 创建多个 Namespaces 的组合
sudo unshare \
--pid --fork \
--mount --mount-proc \
--net \
--uts \
--ipc \
--user --map-root-user \
/bin/bash
# 现在你有了:
# - 独立的进程树(PID Namespace)
# - 独立的网络(Network Namespace)
# - 独立的文件系统视图(Mount Namespace)
# - 独立的主机名(UTS Namespace)
# - 独立的 IPC(IPC Namespace)
# - 独立的用户空间(User Namespace)
8. 使用 nsenter 进入已存在的 Namespace
用例:调试运行中的容器。查找容器进程进入容器
# 查看容器的 PID
CONTAINER_ID=8809a15ac3b5
PID=$(docker inspect -f '{{.State.Pid}}' $CONTAINER_ID)
# 进入容器的 Network Namespace
sudo nsenter -t $PID -n /bin/bash
# 现在你可以使用容器的网络
# 进入容器的所有 Namespaces
sudo nsenter -t $PID -a /bin/bash
# 现在你完全在容器的环境中
# 只进入 PID Namespace
sudo nsenter -t $PID -p /bin/bash
# 只进入 Mount Namespace
sudo nsenter -t $PID -m /bin/bash
nsenter 工作原理
nsenter 通过进程 PID 进入该进程所在的 Namespace。
┌─────────────────────────────────────────────────────────┐
│ nsenter 工作原理 │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 任何进程都有自己的 Namespace │ │
│ │ │ │
│ │ 进程 A (PID: 1234) │ │
│ │ ├─ PID Namespace: [4026531836] │ │
│ │ ├─ Network Namespace: [4026532000] │ │
│ │ ├─ Mount Namespace: [4026531840] │ │
│ │ ├─ IPC Namespace: [4026531839] │ │
│ │ ├─ UTS Namespace: [4026531838] │ │
│ │ ├─ User Namespace: [4026531837] │ │
│ │ └─ Cgroup Namespace: [4026531835] │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ │ nsenter -t 1234 -n /bin/bash │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 新进程进入进程 A 的 Network Namespace │ │
│ │ │ │
│ │ 新进程 (PID: 5678) │ │
│ │ ├─ PID Namespace: [4026531836] ← 共享 │ │
│ │ ├─ Network Namespace: [4026532000] ← 共享 │ │
│ │ ├─ Mount Namespace: [4026531840] ← 共享 │ │
│ │ └─ ... (其他 Namespace 保持不变) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ 关键:只需要进程的 PID,不需要是 Docker 容器! │
└─────────────────────────────────────────────────────────┘
重要点
1. exit 只终止当前进程
┌─────────────────────────────────────────────────────────┐
│ exit 的影响范围 │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 目标进程 (PID: 1234) │ │
│ │ │ │
│ │ 状态:✅ 仍然运行 │ │
│ │ Namespace:✅ 仍然存在 │ │
│ │ 网络:✅ 仍然可用 │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ │ 不受影响 │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ nsenter 创建的进程 (PID: 5678) │ │
│ │ │ │
│ │ 状态:❌ 已终止(exit 后) │ │
│ │ Namespace:❌ 不再共享 │ │
│ │ 网络:❌ 无法访问 │ │
│ └──────────────────────────────────────────
# 第一次进入
$ nsenter -t 1234 -n /bin/bash
$ ip addr
# 可以看到目标进程的网络
$ exit
# 第二次进入(目标进程还在运行)
$ nsenter -t 1234 -n /bin/bash
$ ip addr
# 仍然可以看到相同的网络(目标进程的网络没变)
$ exit
9. 实际案例:手动创建"容器"
#!/bin/bash
# 创建一个类似 Docker 容器的环境
# 1. 创建目录结构
CONTAINER_ROOT=/tmp/my-container
mkdir -p $CONTAINER_ROOT/{bin,lib,etc,home,proc,sys}
# 2. 复制必要的文件(简化版)
cp /bin/bash $CONTAINER_ROOT/bin/
cp /bin/ls $CONTAINER_ROOT/bin/
# 3. 创建新的 Namespaces
sudo unshare \
--pid --fork \
--mount --mount-proc \
--net \
--uts \
--ipc \
--user --map-root-user \
bash << 'EOF'
# 4. 在新的 Namespace 中设置主机名
hostname my-container
# 5. 挂载 proc(让 ps 等命令工作)
mount -t proc proc /proc
# 6. 查看进程(只有当前 Namespace 的)
ps aux
# 7. 查看网络(独立的网络栈)
ip addr show
# 8. 查看主机名
hostname
EOF
10. Docker 如何使用 Namespaces
Docker 在创建容器时自动创建这些 Namespaces:
# Docker 创建容器时相当于执行:
# 1. 创建所有 Namespaces
# 2. 配置网络(Network Namespace)
# 3. 挂载文件系统(Mount Namespace)
# 4. 设置主机名(UTS Namespace)
# 5. 启动进程(PID Namespace)
# 查看容器的 Namespaces
CONTAINER_ID=8809a15ac3b5
PID=$(docker inspect -f '{{.State.Pid}}' $CONTAINER_ID)
ls -la /proc/$PID/ns/
# 输出:
# cgroup -> cgroup:[4026532296]
# ipc -> ipc:[4026532294]
# mnt -> mnt:[4026532292]
# net -> net:[4026532297]
# pid -> pid:[4026532295]
# pid_for_children -> pid:[4026532295]
# time -> time:[4026531834]
# user -> user:[4026531837]
# uts -> uts:[4026532293]
总结对比
| Namespace | 作用 | 手动创建命令 | Docker 对应 |
|---|---|---|---|
| PID | 进程隔离 | unshare --pid | docker run --pid=host |
| Network | 网络隔离 | ip netns add | docker run --network=host |
| Mount | 文件系统隔离 | unshare --mount | Docker 自动创建 |
| UTS | 主机名隔离 | unshare --uts | docker run --hostname |
| IPC | IPC 隔离 | unshare --ipc | docker run --ipc=host |
| User | 用户隔离 | unshare --user | docker run --userns=host |
| Cgroup | Cgroup 隔离 | unshare --cgroup | Docker 自动创建 |