前置回顾
第 01 篇讲过 mount 挂载机制--文件系统必须 mount 到某个目录才能使用.OverlayFS 就是一种特殊的文件系统类型,通过 mount -t overlay 将多个目录叠加成一个统一视图.
第 02 篇的 Mount Namespace 则保证每个容器看到自己独立的挂载--两者配合,就是容器文件系统隔离的完整实现.
为什么需要分层存储
假设你有 100 个容器都基于 Ubuntu 镜像运行.如果每个容器都完整复制一份 Ubuntu 文件系统(约 70MB),光镜像层就要占 7GB.
分层存储的解决方案:把镜像拆成多个只读层,多个容器共享相同的层,每个容器只额外维护自己的可写层:
|
类比透明胶片投影:每一层是一张透明片,叠在一起看就是完整的画面.底层(基础镜像)是背景,上层(应用代码)画在新的透明片上.修改时只需要换掉某一张片,不用重绘整幅画.
OverlayFS 原理
OverlayFS 是 Linux 内核内置的联合文件系统(Union File System),也是 Docker 当前默认的存储驱动(overlay2).
三层结构
|
| 层 | 说明 | 读写权限 | 对应概念 |
|---|---|---|---|
| lowerdir | 镜像层,可多个,按顺序叠加(后面的优先级高) | 只读 | Docker 镜像的各层 |
| upperdir | 容器层,记录所有修改 | 读写 | 容器的可写层 |
| workdir | OverlayFS 内部使用的临时工作目录 | 内部 | - |
| merged | 最终合并后的统一视图 | 合并 | 容器看到的 rootfs |
写时复制(Copy-on-Write)
容器运行时需要修改文件怎么办?镜像层是只读的.OverlayFS 的策略是写时复制--只在真正修改时才产生拷贝:
| 操作 | OverlayFS 行为 |
|---|---|
| 读取文件 | 直接从 lowerdir 读取,不做任何拷贝(零开销) |
| 修改文件 | 先将文件从 lowerdir 复制 到 upperdir,然后在 upperdir 中修改副本 |
| 删除文件 | 在 upperdir 中创建一个 whiteout 文件(特殊标记),遮挡 lowerdir 中的原文件 |
| 新建文件 | 直接在 upperdir 中创建,不涉及 lowerdir |
类比图书馆的参考书规则:参考书(lowerdir)不能带走,不能涂改.你需要修改时,先去复印一份(copy-up),然后在你的复印件(upperdir)上随便写.删除一本书?在目录卡上贴个"已撤架"标签(whiteout),让人查不到它.
实操:手动体验 OverlayFS
挂载 OverlayFS
|
lowerdir 的顺序
lowerdir=A:B:C 中,A 优先级最高,C 最低.同名文件存在于多层时,取优先级最高的那个.对应 Docker:最上面的镜像层优先级最高.
修改与删除:观察 upperdir 的变化
|
动手验证
上面的命令可以直接在任何 Linux 机器上运行(内核 3.18+ 内置 OverlayFS).通过观察 upper/ 目录的变化,你可以直观看到写时复制和 whiteout 的行为--这就是 docker diff 命令背后的原理.
Docker 镜像分层
Dockerfile 与层的关系
Dockerfile 中每条会修改文件系统的指令(RUN,COPY,ADD)都会产生一个新的层:
|
运行容器时,Docker 在这些只读层之上加一个可写层(upperdir),形成完整的 OverlayFS:
|
最佳实践:合并 RUN 指令
每个 RUN 产生一层,层越多镜像越大(删除文件只是加 whiteout,下层的数据仍占空间).应合并相关操作并清理缓存:
|
查看镜像层信息
|
小结
容器三件套完成
- Namespace(第 02 篇) = 看到什么(视图隔离)
- Cgroups(第 03 篇) = 用多少(资源限制)
- OverlayFS(本篇) = 文件从哪来(分层文件系统 + 写时复制)
至此已经理解了容器的全部底层原理.从第 05 篇开始,我们进入 Docker 工程实践--看它如何把这三件套封装成好用的产品.
本篇要点回顾
| 要点 | 一句话概括 |
|---|---|
| 分层存储的意义 | 多个容器共享相同的只读镜像层,节省磁盘空间(100 个容器只存一份基础镜像) |
| OverlayFS 结构 | lowerdir(只读镜像层)+ upperdir(容器可写层)→ merged(统一视图) |
| 写时复制 | 读取零开销;修改时才复制到 upper;删除用 whiteout 遮挡 |
| Dockerfile 与层 | RUN / COPY / ADD 每条产生一层;应合并指令减少层数 |
| Docker 验证 | docker inspect 看 GraphDriver.Data 即可见 OverlayFS 四个目录 |
动手练习
- 按照实操部分手动挂载 OverlayFS,修改文件后观察 upperdir 的变化
- 删除 merged 中的文件,用
ls -la upper/找到 whiteout 文件,确认它是字符设备 - 运行一个 Docker 容器,用
docker inspect找到其 LowerDir/UpperDir/MergedDir 路径,进去看看真实的文件 - 对比
docker diff输出与 upperdir 中的文件变化,验证一致性 - 思考题:为什么在 Dockerfile 中
RUN rm -rf /big-file并不能让最终镜像变小?(即使文件"删了")