Docker —— 隔离的基本操作(2)
- unshare
- `unshare` 命令详解
- 基本语法
- 常用选项
- 常用示例
- 实际应用场景
- 注意事项
- 与 Docker 的关系
- 1. 执行命令
- 2. 修改主机名
- 3. 退出命名空间
- 4. 验证宿主机主机名
- 关键原理
- 类比 Docker 容器
- 总结
- 实战操作一(PID 隔离)
- 关于 `unshare --pid --fork --mount-proc` 的深入解释
- 1. `--fork` 的作用
- 2. `--pid` 的隔离特性
- 3. `--mount-proc` 的必要性
- 完整命令的意义
- 常见问题
- Q1:不加 `--fork` 会怎样?
- Q2:不加 `--mount-proc` 会怎样?
- Q3:为什么需要 `sudo`?
- 总结
- 实战操作二(Mount 隔离)
我们上次简单介绍了一些关于隔离的一些基本操作,如果不熟悉的小伙伴可以点击这里:
https://blog.csdn.net/qq_67693066/article/details/147712428
我们今天主要来操作一下隔离,如何实现:
unshare
unshare
命令详解
unshare
是 Linux 系统中用于创建新命名空间(namespace)并运行程序的命令,它是 Linux 容器技术(如 Docker)的基础工具之一。通过 unshare
可以创建隔离的运行环境。
基本语法
unshare [选项] [--] [程序 [参数...]]
常用选项
短参数 | 长参数 | 含义 |
---|---|---|
-i | --ipc | 不共享 IPC 空间(隔离进程间通信:消息队列、共享内存等) |
-m | --mount | 不共享 Mount 空间(隔离文件系统挂载点) |
-n | --net | 不共享 Net 空间(隔离网络栈:IP、端口、路由等) |
-p | --pid | 不共享 PID 空间(隔离进程 ID,需配合 --fork 和 --mount-proc 使用) |
-u | --uts | 不共享 UTS 空间(隔离主机名和域名) |
-U | --user | 不共享用户命名空间(隔离用户和组 ID,普通用户可用) |
-V | --version | 查看版本信息 |
--fork | 创建子进程并在子进程中执行 unshare (通常与 --pid 共用,使子进程成为 PID 1) | |
--mount-proc | 在新 PID 命名空间中挂载 /proc 文件系统(确保 ps 、top 等命令正常工作) |
常用示例
-
创建新的 mount 命名空间:
unshare --mount
在这个新命名空间中的挂载操作不会影响主系统
-
创建 PID 命名空间(类似容器环境):
sudo unshare --pid --fork --mount-proc bash
在这个新环境中,bash 将成为 PID 1 的进程
-
创建网络命名空间:
sudo unshare --net
这个新环境将有独立的网络栈
-
创建完整的隔离环境:
sudo unshare --pid --fork --mount-proc --net --mount --uts bash
-
创建用户命名空间(不需要 root 权限):
unshare --user --map-root-user
-
在新命名空间中运行特定程序:
unshare --pid --fork --mount-proc -- sleep 1000
实际应用场景
-
测试软件安装:
unshare --mount bash mkdir /tmp/testroot mount --bind /tmp/testroot /tmp/testroot mount --make-private /tmp/testroot mount --bind /path/to/package /tmp/testroot chroot /tmp/testroot /bin/bash
-
创建临时网络环境:
sudo unshare --net ip link set lo up ip addr add 192.168.1.100/24 dev lo
-
安全测试:
unshare --user --map-root-user --pid --fork --mount-proc bash
注意事项
- 大多数命名空间需要 root 权限,除了用户命名空间
- 使用
--pid
时通常需要同时使用--fork
和--mount-proc
- 命名空间中的修改不会影响主系统
- 可以使用
nsenter
命令进入已存在的命名空间
与 Docker 的关系
Docker 等容器技术底层就是使用 unshare
类似的机制创建隔离环境。例如:
# 类似于 Docker 容器的基础隔离
sudo unshare --pid --fork --mount-proc --net --mount --uts --ipc bash
unshare
是理解 Linux 容器技术的重要工具,通过它可以深入了解 Linux 的命名空间隔离机制。
我们来简单使用一下:
这段命令演示了使用 unshare
创建 UTS 命名空间(主机名隔离)的过程。以下是逐步解释:
1. 执行命令
sudo unshare -u /bin/bash
-u
(--uts
):创建新的 UTS 命名空间,隔离主机名和域名。/bin/bash
:在新命名空间中启动 Bash shell。- 需要
sudo
因为 UTS 命名空间默认需要 root 权限。
2. 修改主机名
hostname test1
hostname
- 在新 UTS 命名空间中,将主机名修改为
test1
。 hostname
命令验证修改成功,此时 仅影响新命名空间。
3. 退出命名空间
exit
- 退出 Bash shell 后,回到原来的 UTS 命名空间(宿主机环境)。
4. 验证宿主机主机名
hostname
- 显示
localhost.localdomain
,证明宿主机的主机名 未被修改。 - 因为之前的
hostname test1
只在新创建的 UTS 命名空间中生效。
关键原理
-
UTS 命名空间隔离:
- 每个 UTS 命名空间维护独立的主机名和域名。
- 子命名空间的修改不会影响父命名空间(宿主机)。
-
作用域:
unshare -u
创建的子进程及其后代进程会继承新的 UTS 命名空间。- 退出子进程后,命名空间自动销毁(除非使用
--persistent
等选项)。
类比 Docker 容器
Docker 容器启动时也会创建独立的 UTS 命名空间:
# 在容器内修改主机名不影响宿主机
docker run -it --hostname mycontainer alpine sh
总结
- 发生了什么:
通过unshare -u
创建了一个临时的隔离环境,在其中修改的主机名仅在该环境内有效,退出后宿主机环境保持不变。 - 为什么需要:
这种隔离机制是容器技术的基础,允许不同环境拥有独立的主机名配置,避免冲突。
实战操作一(PID 隔离)
在主机上执行 ps -ef,可以看到进程列表如下,其中启动进程 PID 1 为 init 进程:
我们打开另外一个 shell ,执行下面命令创建一个 bash 进程,并且新建一个 PID
Namespace:
unshare --fork --pid --mount-proc /bin/bash
执行 exit 退出进程
关于 unshare --pid --fork --mount-proc
的深入解释
1. --fork
的作用
-
核心问题:
当使用--pid
创建新的 PID 命名空间时,新命名空间需要有一个独立的进程树,且第一个进程(PID 1)必须是该命名空间内的进程。- 如果不加
--fork
,unshare
自身进程(父进程)会作为新命名空间的初始进程,但该父进程 并不属于新命名空间,导致内核无法正确初始化 PID 命名空间,报错Cannot allocate memory
。
- 如果不加
-
解决方案:
--fork
会让unshare
先创建一个子进程(fork
),再在该子进程中调用unshare
创建新命名空间。此时子进程成为新命名空间的 PID 1(类似init
进程),符合内核要求。
2. --pid
的隔离特性
-
仅隔离 PID:
--pid
仅隔离进程 ID 视图,其他资源(如网络、挂载点等)仍与宿主机共享。- 新命名空间内的进程无法看到宿主机其他进程,但宿主机仍能看到新命名空间内的进程(只是 PID 不同)。
-
对比完整容器:
若需完全隔离,需组合其他命名空间(如--net --mount --uts
)。
3. --mount-proc
的必要性
-
/proc
文件系统的特殊性:
Linux 的/proc
是一个虚拟文件系统,动态反映当前命名空间的进程信息。- 默认情况下,
/proc
显示 宿主机全局进程信息,与新 PID 命名空间隔离的视图冲突。
- 默认情况下,
-
挂载新
/proc
的作用:
--mount-proc
会自动在新命名空间中重新挂载/proc
,使其仅包含 当前命名空间及其子命名空间的进程信息。- 这样,
ps
、top
等命令才能正确显示当前命名空间内的进程。
- 这样,
-
与
--mount
的关系:
--mount-proc
隐含了挂载点隔离(类似--mount
),但专门针对/proc
优化,避免手动挂载的复杂性。
完整命令的意义
sudo unshare --pid --fork --mount-proc bash
- 流程:
--fork
创建子进程作为 PID 1。--pid
在新子进程中初始化独立的 PID 命名空间。--mount-proc
挂载隔离后的/proc
,确保进程工具正常工作。- 启动
bash
,成为新命名空间的主进程。
常见问题
Q1:不加 --fork
会怎样?
- 内核拒绝初始化 PID 命名空间,报错
Cannot allocate memory
,因为缺少合法的 PID 1 进程。
Q2:不加 --mount-proc
会怎样?
ps
/top
仍显示宿主机所有进程,破坏 PID 隔离的语义。
Q3:为什么需要 sudo
?
- 创建 PID 命名空间默认需要
root
权限(涉及系统级进程管理)。
总结
参数 | 作用 | 必要性 |
---|---|---|
--pid | 创建独立的 PID 命名空间 | 核心功能 |
--fork | 确保新命名空间有合法的 PID 1 进程 | 必须配合 --pid 使用 |
--mount-proc | 挂载隔离后的 /proc ,使进程工具正常工作 | 强烈推荐(否则隔离不完整) |
这种机制是 Linux 容器(如 Docker)的基础:通过组合多个命名空间(--pid --net --mount
等)和 --mount-proc
,实现进程、网络、文件系统等资源的隔离。
实战操作二(Mount 隔离)
打开第一个 shell 窗口 A,执行命令, df -h ,查看主机默认命名空间的磁盘挂载情
况:
打开一个新的 shell 窗口 B,执行 Mount 隔离命令
unshare --mount --fork /bin/bash
在窗口 B 中添加新的磁盘挂载
dd if=/dev/zero of=fdimage.img bs=8k count=10240
mkfs -t ext4 ./fdimage.img
mount ./fdimage.img 文件路径
在窗口 B 挂载的磁盘中添加文件
echo "Hello world!" > ./tmpmount/hello.txt
查看窗口 A 中的磁盘挂载信息
查看窗口 B 中的文件信息
查看窗口 A 中的文件信息,可以看到窗口 B 中新建的文件和磁盘挂载在主机的窗
口中并没有,说明我们实现了文件系统隔离。
窗口 B 执行 exit,退出