- Overlay2 的实现原理
- Device Mapper 的实现原理
- Overlay2 vs Device Mapper 对比
- 实际例子
1. Overlay2 的实现原理
Overlay2 是 Linux 的联合文件系统(UnionFS)实现之一,支持将多个目录(称为“层”)叠加为一个逻辑文件系统。其原理基于将只读层(镜像)和读写层(容器)组合起来,形成统一的文件视图。
工作原理
- 镜像层(Image Layers)
- Docker 镜像由多层文件系统构成,每层只读。
- Overlay2 使用这些只读层,构建容器的基础文件系统。
- 容器层(Writable Layer)
- 容器运行时会在镜像顶部添加一个可写层(write layer)。
- 容器的所有文件修改、创建和删除都发生在这一层。
- 文件叠加机制
- Lowerdir:只读的镜像层,作为底层。
- Upperdir:可写层,存储容器运行中的变更。
- Merged:通过 OverlayFS 提供的联合挂载,将
Lowerdir 和 Upperdir 合并为统一视图。
- 操作机制
- 读操作:优先从
Upperdir 读取。如果文件不在 Upperdir,则读取 Lowerdir 中的数据。 - 写操作:任何写入都不会修改底层镜像层,而是将文件复制到
Upperdir,然后修改副本(Copy-on-Write)。 - 删除操作:在
Upperdir 中创建一个白条文件(Whiteout File),隐藏 Lowerdir 中的对应文件。
优点
- 高效的镜像层复用:通过只读层共享多个容器。
- 写时复制(Copy-on-Write)避免了不必要的数据复制。
- 文件操作速度快,尤其是对读密集型应用。
缺点
- 需要较新的 Linux 内核(≥ 4.0)。
- 对小文件频繁写入的性能优化有限。
2. Device Mapper 的实现原理
Device Mapper 是 Linux 提供的一种块设备抽象层,通过逻辑卷管理(LVM)和快照功能,虚拟化存储设备。Docker 使用 Device Mapper 提供的“块存储”功能为容器实现数据存储和快照管理。
工作原理
- 逻辑卷与镜像层
- 每个镜像层被存储为一个稀疏文件(thin provisioned block device)。
- Docker 使用
dm-thin 模块管理这些稀疏文件。
- 容器层(Writable Layer)
- 每个容器运行时会基于镜像层创建一个新的稀疏设备,作为容器的可写层。
- 容器的所有文件系统操作都会映射到该设备。
- 写时复制(Copy-on-Write)
- 写入时,只复制需要更改的块,而不是整个文件。
- 容器之间的共享层无需复制,节约了存储空间。
- 底层结构
- Pool Device:Device Mapper 的存储池,由一对主设备(Metadata Device 和 Data Device)组成,分别用于存储元数据和实际数据。
- Thin Provisioning:支持精简配置(thin provisioning),容器只使用实际需要的空间。
- 快照管理
- 每次创建容器时,Docker 会为其创建基于父镜像的快照。
- 快照本质是一个写时复制(COW)的逻辑卷,记录对原始数据的更改。
优点
- 适合块设备存储:对于需要使用直接存储设备(如 SSD 或 RAID)的场景表现优秀。
- 数据隔离性强:通过块设备提供更低级别的隔离。
缺点
- 配置复杂:需要额外设置 LVM 或专用块设备。
- 性能较差:相比文件系统驱动,I/O 性能稍逊。
- 容器启动速度慢:因为稀疏逻辑卷的管理复杂,启动容器时需要初始化较多数据。
Overlay2 vs Device Mapper 对比
总结:
- 如果系统支持,建议优先使用 Overlay2,因其性能高、易用性强。
- 对于需要直接使用块设备或低内核版本的场景,可以选择 Device Mapper。
实际例子
1. Overlay2 的工作原理示例
场景
假设我们使用一个基于 Ubuntu 镜像的容器,并在其中创建一个新的文件 /app/data.txt,然后修改该文件。
步骤
- 镜像层 (Image Layers)
Ubuntu 镜像包含多个只读层,分别表示操作系统的基础文件,例如:
这些层是只读的,容器启动时不会直接修改它们。- 第一层:核心文件
/bin/bash,/lib/libc.so - 第二层:系统配置
/etc/hosts,/etc/resolv.conf
- 容器层 (Writable Layer)
启动容器时,Docker 在镜像的顶部创建一个可写层。
- 创建文件
/app/data.txt 时,该文件会直接存储在 Upperdir(可写层)。 - 如果需要修改
/etc/hosts 文件,系统会将原始文件从只读层复制到可写层(Copy-on-Write),然后对副本进行修改。
- 读写操作
- 读操作:容器内读取
/etc/hosts 文件时,优先查看 Upperdir 是否有副本。如果没有,才从只读层中读取原始文件。 - 写操作:所有写入操作只修改 Upperdir,不会影响底层镜像。
结果
- 容器文件系统显示为完整的合并视图,包括只读镜像和可写层。
- 写入或删除的内容仅影响容器的可写层,原始镜像层不受影响。
2. Device Mapper 的工作原理示例
场景
同样以 Ubuntu 镜像为基础启动容器,并创建和修改文件 /app/data.txt。
步骤
- 存储池初始化
- Metadata Device:存储数据元信息(如快照位置)。
- Data Device:存储实际数据块。
- Docker 使用 Device Mapper 在底层块设备(如
/dev/sda)上创建一个存储池(Pool),包括:
- 镜像层与快照
Ubuntu 镜像存储为一个只读的稀疏逻辑卷(thin provisioned logical volume)。- 启动容器时,基于镜像创建一个新的快照(thin snapshot),作为容器的读写层。
- 写时复制 (Copy-on-Write)
- 创建
/app/data.txt 时,该文件的数据块直接写入到容器的逻辑卷中。 - 修改
/etc/hosts 文件时,只会复制相关的数据块到容器逻辑卷,然后对这些块进行修改。
- 读写操作
- 读操作:优先从容器快照的逻辑卷中读取数据。如果需要的数据块不存在,则从基础镜像的逻辑卷中读取。
- 写操作:直接将数据块写入容器逻辑卷,不会影响底层镜像的逻辑卷。
结果
- 容器的数据以块为单位存储在独立的逻辑卷中,提供更强的隔离性。
- 镜像层和容器层之间通过快照机制实现分离,写操作不会污染镜像。
对比
总结
- Overlay2:适用于普通应用场景,尤其是对文件操作频繁的场景(如 Web 应用)。
- Device Mapper:适合块存储设备(如 SSD),或者对隔离性要求较高的场景(如多租户)。