容器原理
Linux 容器是一组受隔离和资源约束的进程。本文负责 Namespace、cgroup 和分层文件系统。
1. 面试核心
先记住这几条,再读正文- 容器与虚拟机。 Linux 容器共享宿主内核,虚拟机运行独立内核。卡 · 容器与虚拟机
- 隔离与资源约束。 Namespace 隔离资源视图,cgroup 统计并约束资源用量。卡 · Namespace
- 资源配额。 cgroup v2 的
cpu.max限制 CPU 配额,memory.max限制内存。 - 文件系统。 OverlayFS 用可写上层和只读下层组成文件视图。
- 权限控制。 capabilities 拆分特权,seccomp 过滤系统调用。卡 · 容器安全边界
2. 容器与虚拟机
| 维度 | Linux 容器 | 虚拟机 |
|---|---|---|
| 运行对象 | 宿主内核调度的进程 | 虚拟硬件上的客户操作系统 |
| 内核 | 与宿主共享 | 客户系统独立运行 |
| 启动工作 | 创建隔离环境并启动程序 | 启动客户内核和系统服务 |
| 资源开销 | 进程和隔离机制的开销 | 另有客户操作系统的开销 |
| 安全边界 | 依赖共享内核和权限配置 | 依赖虚拟化边界及其实现 |
| 系统兼容 | 程序须兼容所用内核与架构 | 客户系统须适配虚拟硬件 |
容器启动时间还包括镜像拉取和应用初始化。虚拟机也能通过快照缩短启动时间。因此,启动耗时应在相同工作负载下比较。
Linux 容器在 macOS 上运行时,需要 Linux 虚拟机。容器共享的是该虚拟机的内核。
3. Namespace
Namespace 为进程提供独立的资源视图。
| 类型 | 隔离对象 |
|---|---|
| PID | 进程 ID 与可见进程范围 |
| Network | 网络设备、路由、协议栈和端口 |
| Mount | 挂载点 |
| UTS | 主机名和 NIS 域名 |
| IPC | System V IPC 与 POSIX 消息队列 |
| User | 用户、组 ID 和对应特权范围 |
| Cgroup | 可见的 cgroup 根路径 |
| Time | 单调时钟与启动时钟的偏移 |
Mount Namespace 隔离挂载视图。运行时还需设置根目录及具体挂载。Network Namespace 本身只提供网络隔离。veth、网桥或路由由网络配置建立。
ls -l /proc/<pid>/ns 可查看 Namespace 标识。nsenter -t <pid> -n ip addr 在目标网络视图内执行命令。docker exec 则通过运行时创建新的容器内进程。Linux 运行时可用 setns() 加入现有 Namespace。PID Namespace 的加入对后续创建的子进程生效。
容器可以显式共享部分 Namespace。例如 --network=host 使用宿主网络视图。
4. Cgroups
cgroup 把进程组织成资源管理层级。本文的文件名以 cgroup v2 为准。
| 文件 | 含义 |
|---|---|
cpu.max | 每个周期内可用的 CPU 时间 |
cpu.weight | 竞争 CPU 时的相对权重 |
cpuset.cpus | 允许使用的 CPU 集合 |
memory.max | 内存硬上限 |
memory.current | 当前内存用量 |
memory.events | 内存压力、OOM 与 OOM kill 事件计数 |
io.max | 指定块设备的带宽与 IOPS 上限 |
pids.max | 任务数量上限,线程也计入 |
cpu.max=50000 100000 表示每 100 ms 可用 50 ms。配额由组内任务共享,相当于半个 CPU 的时间。用完配额后,任务等待后续周期。宿主 CPU 空闲时也会发生这种限流。cpu.stat 的 nr_throttled 可用于确认。
内存达到 memory.max 后,内核尝试回收。回收无法满足分配时,会触发组内 OOM。确认 OOM 应结合 memory.events 与运行时状态。137 是 128+SIGKILL 的常见退出编码。手工强杀和停止超时也能产生该值。
v1 允许控制器使用不同层级。v2 使用统一层级,祖先的约束也作用于后代。v2 默认按进程分组,threaded 模式支持部分线程级控制。实际使用版本由宿主系统配置决定。
Docker 参数包括 --cpus=1.5、--memory=512m。任务数可通过 --pids-limit=100 约束。
5. 联合文件系统
OverlayFS 将多层目录合并成一个视图。
lowerdir提供只读下层。upperdir保存新增和修改后的文件。merged是应用看到的挂载视图。
读取时,上层同名对象覆盖下层对象。修改下层文件内容会触发 copy-up。文件复制到上层后,再接受修改。仅修改元数据时,行为还取决于 metacopy 配置。
删除下层文件会创建 whiteout,遮住原文件。下层镜像中的内容仍被保留。持久数据应使用卷,具体方式见 Docker 镜像与 Dockerfile。
6. 容器安全边界
容器安全还依赖特权和系统调用约束。Namespace 只隔离各类资源视图。
capabilities 将 root 权限拆成独立能力。seccomp 按策略过滤系统调用。AppArmor、SELinux 进一步约束文件和资源访问。
非 root 用户可减少应用本身的权限。User Namespace 可将容器 UID 映射到宿主普通 UID。Docker rootless 还让守护进程以非 root 身份运行。两者的范围不同。
--privileged 会显著扩大容器权限。挂载 Docker socket 也会暴露守护进程控制能力。运行不可信代码时,还需评估独立虚拟机等隔离方案。
7 · 面试题卡片
本页题目与参考回答
7.1 容器和虚拟机有什么本质区别?
考察重点
- 是否共享内核:容器是隔离的进程,虚拟机虚拟了硬件。
- 由此推出启动速度、开销、隔离强度和可运行系统的差异。
相关内容:容器与虚拟机、Namespace、Cgroups。
参考回答
Linux 容器共享宿主内核,虚拟机有独立的客户内核。
容器通过 Namespace 隔离资源视图。cgroup 负责资源约束,运行时设置根文件系统。
容器省去客户内核及系统服务的开销。实际启动速度还取决于镜像和应用初始化。虚拟机增加了一层虚拟化安全边界。容器则依赖共享内核和权限策略。两者都需要结合具体实现评估隔离能力。
7.2 容器是怎样实现隔离和资源限制的?Namespace 和 Cgroups 分别解决什么问题?
考察重点
- 各类 Namespace 隔离的内容;Cgroups 的主要控制器。
- 内存超限与 CPU 限流的表现;v1 与 v2 的区别;
docker exec的原理。
相关内容:Namespace、Cgroups、联合文件系统。
参考回答
Namespace 隔离资源视图,cgroup 约束资源用量。
PID、Network、Mount 分别管理进程、网络与挂载视图。docker exec 让运行时启动新的容器内进程。运行时可通过 setns() 加入已有 Namespace。
cgroup v2 的 cpu.max 按周期限制 CPU 时间。配额用尽会引发限流。memory.max 触发回收,回收失败后可触发 OOM。退出码 137 还需结合 OOM 事件确认。pids.max 约束任务数,包含线程。
v1 可以有多个控制器层级,v2 使用统一层级。父级的资源上限同样约束子级。
7.3 容器内使用非 root 用户后,还需要哪些安全限制?
考察重点:用户映射、权限与系统调用边界。
相关内容:容器安全边界。
参考回答
还需要限制 capabilities、系统调用和宿主资源访问。
非 root 用户只降低进程的用户权限。seccomp 过滤系统调用,安全模块控制资源访问。User Namespace 提供 UID 映射。rootless 还降低守护进程权限。容器共享内核,因此仍需评估内核攻击面。