Docker 容器隔离机制是什么?边界在哪里?
Docker 的容器隔离主要靠 Linux 内核能力完成,不是靠 Docker 自己“虚拟出一台机器”。一句话说:Namespace 负责把容器“看见的世界”隔开,Cgroup 负责限制它能“用多少资源”,再叠加联合文件系统、Capabilities、seccomp、AppArmor 或 SELinux 等机制,形成一套相对完整的运行边界。
Namespace 隔离了容器能看到什么
Namespace 可以理解成 Linux 给进程准备的“视角隔离”。同一台宿主机上的进程,放进不同 Namespace 后,看到的进程、网络、挂载点、主机名可能完全不同。
PID Namespace:隔离进程编号
PID Namespace 让容器拥有自己的进程树。容器内的第一个进程通常看到自己是 PID 1,但在宿主机上它仍然只是一个普通进程,有宿主机上的真实 PID。
这也是为什么在宿主机执行 ps 能看到容器进程,而容器内默认看不到宿主机其他进程。
NET Namespace:隔离网络栈
NET Namespace 隔离网卡、IP、路由表、端口和防火墙规则。每个容器可以有自己的虚拟网卡、独立 IP 和端口空间。
Docker 默认会通过 veth pair 和 bridge 网络把容器接到宿主机网络上。容器觉得自己有一张独立网卡,实际流量仍然经过宿主机内核转发。
MNT Namespace:隔离文件系统挂载点
MNT Namespace 让容器看到自己的根目录和挂载结构。容器里看到的 / 不是宿主机真正的 /,而是 Docker 准备好的镜像层和可写层组合后的文件系统视图。
但如果把宿主机目录挂进去,例如 -v /:/host,隔离边界就会被主动削弱。所以挂载权限是容器安全里很关键的一环。
UTS、IPC 和 USER Namespace
UTS Namespace 隔离 hostname 和 domain name。IPC Namespace 隔离 System V IPC、POSIX message queue 等进程间通信资源。USER Namespace 用来把容器内的用户 ID 映射到宿主机上的另一个用户 ID,例如容器里看起来是 root,映射到宿主机后可能只是普通用户。
这对安全很重要,因为容器内 root 如果直接等于宿主机 root,一旦逃逸风险会更高。Rootless Docker 和 userns-remap 都是在利用这个思路降低权限面。
Cgroup 限制了容器能用多少资源
Namespace 解决“看见什么”,Cgroup 解决“能用多少”。Docker 可以通过 Cgroup 限制 CPU、内存、磁盘 I/O、进程数量等资源,避免一个容器拖垮整台宿主机。
| 资源 | 作用 |
|---|---|
| CPU | 限制 CPU 使用比例、权重或可用核心 |
| Memory | 限制内存上限,超出后可能触发 OOM |
| Block I/O | 限制磁盘读写权重或吞吐 |
| PIDs | 限制进程数量,防止 fork bomb |
| Devices | 控制容器能访问哪些设备 |
Cgroup v1 按资源类型拆成多个独立控制器,配置灵活但层级容易混乱。Cgroup v2 统一了层级模型,资源控制更一致,现代 Linux 发行版和新版本容器运行时越来越多地使用 v2。
需要注意,Cgroup 是资源控制,不是完整的安全沙箱。它能限制资源滥用,但不能替代权限隔离和系统调用过滤。
联合文件系统提供镜像层隔离
Docker 镜像通常由多层只读层组成,容器启动时再叠加一个可写层。常见实现包括 overlay2。
这样做有两个好处:多个容器可以共享相同镜像层,节省磁盘空间;容器写入文件时只写自己的可写层,不会直接修改镜像原始层。
不过,联合文件系统不是安全边界的全部。真正决定容器能不能访问宿主机敏感路径的,还是挂载配置、权限、Capabilities 和安全策略。
Capabilities、seccomp 和 LSM 继续收紧权限
Linux root 权限被拆成很多 Capabilities。Docker 默认会去掉一部分高危能力,比如直接加载内核模块通常不应该出现在普通容器里。
seccomp 用来过滤系统调用。比如某些危险 syscall 可以被默认策略拦截,降低容器利用内核攻击面的机会。
AppArmor 和 SELinux 属于 Linux Security Module,可以进一步限制进程能访问哪些文件、执行哪些操作。它们更像一层强制访问控制,防止“进程有权限但不该做”的行为。
生产环境常见做法是:不要使用 --privileged,按需添加 Capability,启用默认 seccomp 配置,并配合 AppArmor 或 SELinux 策略。
Docker 隔离的边界在哪里
Docker 容器和虚拟机最大的区别是:容器共享宿主机内核,虚拟机通常有独立内核。
这意味着容器隔离的边界主要在 Linux 内核能力上。如果内核存在可利用漏洞,或者容器被授予了过高权限,例如 --privileged、挂载 Docker socket、挂载宿主机根目录,容器就可能影响宿主机。
Rootless 模式和 USER Namespace 可以降低风险,但也不是万能的。它们能减少容器进程在宿主机上的实际权限,却不能消除所有内核攻击面,也可能受到功能兼容性限制。
实际使用时怎么判断是否安全
判断一个容器是否隔离得足够好,不能只看它是不是 Docker 跑起来的,还要看这些配置:
- 是否避免
--privileged; - 是否限制了不必要的 Capabilities;
- 是否启用了 seccomp、AppArmor 或 SELinux;
- 是否给内存、CPU、PIDs 设置了合理 Cgroup 限制;
- 是否避免挂载宿主机敏感目录和 Docker socket;
- 是否使用 USER Namespace 或 Rootless 模式降低 root 风险;
- 镜像和宿主机内核是否及时更新。
Docker 的隔离机制不是单点能力,而是一组内核机制的组合。Namespace 让容器看起来像独立系统,Cgroup 控制资源使用,联合文件系统隔离文件变更,安全模块收紧权限。它足够适合大多数应用隔离场景,但不能把它误认为和虚拟机一样强的硬隔离。