Docker底层-Namespaces

1. PID Namespace(进程隔离)

sudo unshare --pid --fork --mount-proc /bin/bash

这条命令的作用是:

"以 root 身份启动一个新的、与外部隔离的 PID 命名空间,并在里面运行一个干净的 /bin/bash。"

逐段拆开看:

  1. sudo

    用超级用户权限执行后面的命令,否则创建新的命名空间会失败。

  2. unshare

    Linux 工具,用来"不共享"某些内核命名空间,即让新启动的进程拥有自己独立的命名空间副本。

  3. --pid

    告诉 unshare 新建一个 PID 命名空间。

    在新空间内,该进程看到的 PID 从 1 重新开始,外部进程对它不可见,反之亦然。

  4. --fork

    unshare 本身不进入新命名空间,它只是让"接下来要执行的程序"进入。

    加上 --fork 后,unshare 会先 fork 出一个子进程,子进程再进入新命名空间并执行后面的命令。

    如果不加 --fork,unshare 会报错,因为 PID 命名空间要求进程一进入就 fork 一次,保证新空间里的第一个进程 PID=1。

  5. --mount-proc

    进入新 PID 命名空间后,把 /proc 重新挂载一份,这样 ps、top 等工具看到的是新空间里的进程列表,而不是宿主机的。

  6. /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 自动创建
相关推荐
做一个码农都是奢望2 小时前
高算linux平台如何安装gprmax
linux·运维·服务器
Ancelin安心2 小时前
Windows搭建和使用vulhub的一些常用命令
linux·运维·服务器·网络安全·docker·容器·vulhub
木卫二号Coding2 小时前
Linux-删除一级目录下子目录-github例子
linux·运维·github
虾..3 小时前
Linux 信号的处理
linux·运维·服务器
Gofarlic_oms13 小时前
Cadence许可证全生命周期数据治理方案
java·大数据·运维·开发语言·人工智能·安全·自动化
仰望星空的打工人3 小时前
雨云云应用部署frp
服务器·docker·容器
布史3 小时前
Linux软链接应用详解:从原理到实战案例
linux·运维·服务器
顶点多余3 小时前
linux的基本指令
linux·运维·服务器
Peterrrr09113 小时前
深入理解 Shell 编程:正则表达式与 sed 文本处理器
linux·运维·正则表达式·sed·linux命令