Namespace与容器嵌套通过多层隔离协同实现资源视图逐级收敛:各Namespace类型独立生效且可继承或隔离父视图,嵌套容器由运行时按序创建挂载并配合cgroups限界。

Namespace 和容器嵌套不是简单叠加,而是通过多层隔离机制协同实现资源视图的逐级收敛。核心在于:每个 Namespace 类型(如 PID、NET、MNT)独立生效,且子命名空间可继承或隔离父命名空间的视图;而嵌套容器则依赖运行时(如 containerd 或 runc)按层级顺序依次创建并挂载这些 Namespace,同时配合 cgroups 限制资源边界。
Namespace 的分层隔离原理
Linux Namespace 并非“一创建就完全隔绝”,而是以“是否在当前上下文中可见”为判断标准。例如:
- PID Namespace 中,进程在子空间看到的 PID 从 1 开始编号,但其在父空间仍有真实 PID,内核通过 task_struct 中的 pid_link 维护跨空间映射关系;
- NET Namespace 默认不继承网络设备,需显式挂载或拷贝(如用 ip link set dev eth0 netns),否则子空间无网络能力;
- UTS 和 IPC Namespace 支持 clone() 时指定 CLONE_NEWUTS/CLONE_NEWIPC,但不自动传播主机名或信号量,需用户态配合初始化。
容器嵌套的关键约束与触发条件
并非所有容器运行时都支持嵌套,能否嵌套取决于三点:
- 内核配置:需开启 CONFIG_USER_NS=y(用户命名空间是嵌套前提),部分发行版默认关闭;
- 权限模型:外层容器必须以 privileged 模式运行,或显式授权 CAP_SYS_ADMIN + CAP_NET_ADMIN 等能力,否则内层无法调用 unshare() 或 setns();
- 运行时策略:Docker 默认禁用嵌套(因安全风险),需启动时加 --userns-remap 或改用 Podman(原生支持 rootless 嵌套)。
嵌套时的 Namespace 生命周期管理
子容器退出不会自动销毁其 Namespace,父容器仍可访问该命名空间(如用 nsenter -t
- mount Namespace 在嵌套中极易引发泄漏:子容器挂载的 tmpfs 若未显式 umount,父容器重启后可能残留不可见挂载点;
- user Namespace 嵌套需 UID/GID 映射对齐,否则子容器内进程无法正确解析 /proc/*/status 中的 uid 字段;
- time Namespace(5.6+)目前不支持嵌套,尝试在已有 time ns 内创建新 time ns 会返回 EINVAL。
典型嵌套场景的操作逻辑链
以 Podman 启动嵌套容器为例,实际执行流程是:
- 宿主机调用 clone(CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWNET) 创建第一层隔离环境;
- Podman 在该环境中调用 unshare(CLONE_NEWUSER) 创建 user ns,并通过 /proc/self/setgroups 写入 deny,再映射 0→1000;
- 子容器启动时再次调用 clone(),复用父容器已有的 net/pid ns,但新建 mnt+uts,形成“共享网络 PID、独占挂载和主机名”的混合视图;
- cgroups v2 下,每层容器对应一个子 cgroup 路径(如 /sys/fs/cgroup/podman/containerA/containerB),资源限制逐级生效。










