先记住这个答案
Docker 启动容器时,会为容器进程创建一组独立的 namespace,包括 PID、Network、Mount、UTS、IPC 等(User namespace 默认不创建,需配置 userns-remap 才启用)。PID namespace 隔离进程号,使容器内进程看到独立的 PID 空间,且主进程 PID 为 1;Network namespace 提供独立的网络栈,包括网卡、IP、路由等;Mount namespace 隔离挂载点,容器有自己的文件系统视图;UTS namespace 隔离主机名和域名;IPC namespace 隔离 System V IPC 和 POSIX 消息队列;User namespace 启用后可隔离用户和组 ID,使容器内 root 映射为宿主非特权用户。这些 namespace 共同构成了容器运行的基础隔离边界。
- PID namespace 隔离进程号,容器内 PID 1 是主进程。
- NET namespace 提供独立网络栈,容器有自己的 IP 和端口。
- USER namespace 可将容器 root 映射为宿主非特权用户。
namespace 的创建与隔离对象
当 Docker 启动容器时,docker 守护进程通过 clone 系统调用(或 unshare)为新进程创建多个 namespace。每个 namespace 只隔离对应全局资源。例如 PID namespace 使容器内第一个进程 PID 为 1,容器内无法看到宿主机或其他容器的进程,只能看到同一 namespace 内的进程。这对进程管理和信号传递有直接影响。
Network namespace 隔离了整个网络协议栈,每个容器有独立的虚拟网卡、IP 地址、路由表和 netfilter 规则。容器内启动 nginx 监听 80 端口不会与宿主机或其他容器冲突。Mount namespace 隔离挂载点,容器从镜像挂载根文件系统,并拥有独立的 /tmp 等挂载,对宿主机目录的访问需显式挂载,且挂载操作只影响当前 namespace。
一个运行 nginx 容器中的隔离验证
假设宿主运行一个 Ubuntu 容器:docker run -d --name web -p 8080:80 nginx。在容器内执行 ps aux 只能看到 nginx 相关进程,PID 从 1 开始,看不到宿主 sshd 或 docker-proxy。执行 hostname 返回容器 ID,这是 UTS namespace 的效果。执行 ifconfig 看到 eth0 是 172.17.0.x 地址,而非宿主物理网卡。这些现象都是不同 namespace 的作用结果。
若试图在容器内挂载宿主机目录,比如 mount /dev/sda1 /mnt,会得到 Permission denied,因为默认容器没有 CAP_SYS_ADMIN,且 Mount namespace 隔离权限受限制。若要访问外部网络,容器通过 Docker 创建的 bridge 和 NAT 转发,这属于 Network namespace 与宿主网络的桥接逻辑,说明隔离并非完全封闭,而是通过虚拟接口通信。
namespace 隔离的失效条件
namespace 提供的是内核资源隔离,但不是安全边界。若容器以 --privileged 运行或赋予 CAP_SYS_ADMIN,则可能挂载宿主机文件系统或执行敏感操作,破坏隔离。此外,User namespace 默认未启用,容器内 root 仍是宿主 root 用户,除非配置 userns-remap。没有 User namespace 时,若进程通过漏洞逃逸,可能直接以 root 操作宿主资源。
另一个边界是共享 namespace:使用 --pid=host 会共享宿主 PID namespace,容器内能看到并可能影响宿主进程,这通常用于调试但降低隔离。--network=host 同理,共享网络栈,容器端口占用宿主端口,且失去网络隔离。这些选项应仅在可信场景使用,并需评估风险。
容易答错的地方
- 认为 namespace 等同于安全沙箱
- namespace 只隔离资源视图,不限制特权操作。缺少 user namespace 时容器内 root 映射为宿主 uid 0,但默认受 capabilities 限制,并非拥有宿主 root 全部权限;若额外授予 capabilities 则可能获得更多宿主权限,增加逃逸风险。安全隔离需要结合 capabilities、seccomp、selinux 等机制。
- 以为容器内 PID 1 是独立的 init 系统
- 容器内 PID 1 是主进程并承担 init 角色,但若进程未实现 wait 回调或信号处理,就不会自动回收孤儿进程,也难以及时响应 SIGTERM;通常需 --init 引入 tini 来接管。若主进程不处理 SIGTERM,容器停止时可能被强杀。
面试官还会怎么问?
PID namespace 中容器主进程 PID 为什么是 1?
每个 PID namespace 的初始进程 PID 都是 1,承担 init 角色。内核将孤儿进程交给它接管,但若进程未调用 wait,孤儿进程可能变成僵尸;通常需要主进程显式处理 SIGCHLD 或使用 --init 引入 tini。主进程退出时,内核会终止该 namespace 内其余进程并销毁 namespace。
User namespace 如何影响容器内 root 权限?
启用 userns-remap 后,容器内 root 映射为宿主非特权用户,容器内 root 不能直接访问宿主资源,但需要确保挂载卷的权限匹配,否则容器进程可能无权读写。
容器共享宿主 Network namespace 时还能有独立 IP 吗?
不能。--network=host 使容器直接使用宿主网络栈,无独立 IP,监听端口占用宿主端口,可能冲突。该模式适合网络调试或需要直接使用宿主网络接口的场景,但会失去网络隔离。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。