1. 容器镜像基础:定义、分层文件系统与 UnionFS/OverlayFS
容器镜像是容器化技术的核心交付物,它本质上是一个只读的静态文件系统快照,包含了运行一个应用所需的所有依赖、库、配置文件、环境变量和元数据。与传统的虚拟机镜像不同,容器镜像并非一个完整的操作系统磁盘映像,而是一个分层(Layered) 的、基于联合挂载(Union Mount)技术的轻量级文件系统集合。
1.1 容器镜像的定义与核心特征
从 DevOps 实践的角度看,容器镜像是一个不可变(Immutable) 的交付单元。一旦构建完成,其内容在运行时不应被修改。任何变更都应通过构建新镜像来实现,这保证了环境的一致性和可追溯性。
| 特征 | 说明 | 对发布流水线的意义 |
|---|---|---|
| 不可变性 | 镜像构建后内容只读,运行时修改发生在可写层。 | 确保测试、预发、生产环境使用完全相同的文件系统。 |
| 分层结构 | 由多个只读层(Layer)堆叠而成,每层代表一个 Dockerfile 指令。 | 支持缓存复用,加速构建;减少网络传输量。 |
| 轻量级 | 共享宿主内核,仅包含应用及其运行时依赖。 | 秒级启动,磁盘占用小,适合微服务架构。 |
| 标准化 | 遵循 OCI(Open Container Initiative)镜像规范。 | 跨平台、跨运行时(Docker、containerd、Podman)可移植。 |
1.2 分层文件系统原理
容器镜像的每一层(Layer)都是一个增量文件系统差异。例如,一个基于 Ubuntu 的 Python 应用镜像可能包含以下层:
- 基础层(Base Layer): Ubuntu 22.04 的最小文件系统。
- 系统更新层: 安装 ca-certificates、tzdata 等系统包。
- 运行时层: 安装 Python 3.11 解释器及其依赖。
- 应用依赖层: 通过 pip install 安装 requirements.txt 中的库。
- 应用代码层: 复制编译后的应用二进制或脚本。
这些层在构建时被缓存。当修改应用代码时,只有第 5 层需要重新构建,前 4 层可直接从缓存中复用,极大提升了 CI 流水线的效率。
.dockerignore 减少不必要的文件进入镜像。
1.3 UnionFS 与 OverlayFS 简介
分层文件系统的实现依赖于联合文件系统(Union Filesystem, UnionFS) 技术。它允许将多个目录(层)挂载到同一个挂载点,呈现为一个统一的文件系统视图。Docker 早期使用 AUFS(Another UnionFS),但因其未进入 Linux 内核主线,现代容器运行时(如 Docker 19.03+、containerd)默认使用 OverlayFS。
OverlayFS 是一种更轻量、性能更优的联合文件系统,它仅使用两个主要层:
- LowerDir(下层): 只读层,对应容器镜像的各个只读层。
- UpperDir(上层): 可写层,对应容器运行时的读写层。
- MergedDir(合并层): 最终呈现给容器进程的统一视图。
- WorkDir(工作目录): 用于实现 copy-up 操作的临时目录。
1.4 分层与 OverlayFS 的 I/O 性能影响
在发布流水线中,OverlayFS 的 copy-up 机制 是影响运行时 I/O 性能的关键。当容器进程尝试修改一个位于 LowerDir(只读层)的文件时,OverlayFS 会先将整个文件从 LowerDir 复制到 UpperDir,然后再进行修改。对于大文件(如日志文件、数据库文件),这种操作会带来显著的 I/O 延迟。
| 操作场景 | OverlayFS 行为 | I/O 性能影响 | 优化建议 |
|---|---|---|---|
| 读取未修改的文件 | 直接从 LowerDir 读取 | 接近原生文件系统性能 | 无需优化 |
| 修改小文件(< 1MB) | copy-up 整个文件到 UpperDir | 轻微延迟(毫秒级) | 将频繁修改的文件挂载为卷(Volume) |
| 修改大文件(> 100MB) | copy-up 整个文件到 UpperDir | 显著延迟(秒级),可能阻塞应用 | 使用 tmpfs 或绑定挂载替代 |
| 删除 LowerDir 中的文件 | 在 UpperDir 创建白色out文件(白障) | 几乎无性能影响 | 无需优化 |
1.5 关键概念对比:UnionFS vs OverlayFS
| 特性 | AUFS(传统 UnionFS) | OverlayFS(现代默认) |
|---|---|---|
| 内核支持 | 未进入 Linux 主线内核 | 自 Linux 3.18 起进入主线 |
| 层数限制 | 最多 127 层 | 最多 500 层(实际建议 < 100) |
| 性能 | copy-up 性能较差,大文件场景明显 | copy-up 性能更优,页缓存共享更好 |
| 存储驱动 | aufs(Docker 早期默认) | overlay2(Docker 当前推荐) |
| 适用场景 | 历史遗留系统 | 所有现代 Linux 发行版 |
/etc/passwd 或日志文件)。这会触发 copy-up 操作,导致 UpperDir 膨胀,并增加 I/O 延迟。正确的做法是:1. 将需要持久化的数据挂载为 Volume。
2. 将临时数据挂载为 tmpfs。
3. 通过环境变量或配置文件注入运行时配置。