Docker镜像挂载调研与思考

OverlayFS(overlay2) 背景-多层叠加挂载

早在10年前研究Debian Live的时候,接触到了Aufs (advanced multi-layered unification filesystem), 就像Photoshop的图层,文件系统一层层叠加后,形成了最终可读写的文件层。

**Aufs 格式参考**

mount -t aufs -o br:可读写路径=权限+属性:只读路径1=权限+属性:只读路径2=权限+属性...:只读路径n=权限+属性 挂载标签 挂载点

#例子
mount -t aufs -o br:/rw:/ro:/rr=rr aufs /u

实现的思路是很好的,但由于代码质量问题,并没有被Linux 内核接受

Aufs consists of about 20,000 lines of dense, unreadable, uncommented code, as opposed to around 10,000 for unionfs and 3,000 for union mounts and 60,000 for all of the VFS. The aufs code is generally something that one does not want to look at.
REF: https://lwn.net/Articles/327738/

取而代之的是 2014年合并进入Linux内核 (Linux 3.18后 )的 OverlayFS

Overlayfs allows one, usually read-write, directory tree to be overlaid onto another, read-only directory tree. All modifications go to the upper, writable layer.
REF: https://github.com/torvalds/linux/commit/e9be9d5e76e34872f0c37d72e25bc27fe9e2c54c

改进后的 OverlayFS 4.0 也成为了Docker容器的 overlay2 存储引擎

OverlayFS挂载方法以及覆盖读写表现

Usage: 

mount -t overlay overlay -o lowerdir=/lower,upperdir=/upper,\
workdir=/work /merged


mount -t overlay overlay -olowerdir=/lower1:/lower2:/lower3,upperdir=/upper,\
workdir=/work /merged
  • work 目录需要为空,且与upperdir在同一个文件系统
  • lowerdir 允许多个,使用:分离, 排在前面的目录越上层,优先级越高
  • lowerdir如果保护冒号,可以使用 \ 进行转义,例如 -olowerdir=/a\:lower\:\:dir (当然如果可以,没必要在文件目录用特殊符号)

文件读取要简单一些

  • 如果文件存在 upperdir,直接读取文件;
  • 如果文件存在 lowerdir,如果多个,则便利查询读取;
  • 如果文件都不存在,则返回不存在

目录读取则需要取并集模式

  • 同时查找upperdir、lowerdir(N) 中的目录信息,合并覆盖后返回

  • 首次写入: 如果在upperdir中不存在,执行copy_up操作,把文件从lowdir拷贝到upperdir,由于overlayfs是文件级别的(即使文件只有很少的一点修改,也会产生的copy_up的行为),后续对同一文件的在此写入操作将对已经复制到upperdir的文件的副本进行操作。这也就是常常说的写时复制(copy-on-write)

  • 删除文件和目录: 当文件在容器被删除时,在upperdir 创建whiteout文件,lowerdir 的文件是不会被删除的,因为他们是只读的,但without文件会阻止他们显示,当目录在在upperdir 层被删除时,在upperdir维护一个不透明的目录,这个和上面whiteout原理一样,阻止用户继续访问,即便 lowerdir 层仍然存在。

  • 文件移动,直接在upperdir创建移动后的文件,inode保持不变

技术应用-2个案例

由于软件版本的存在,使用多层存储来存放文件diff,在一定程度上可以优化软件分发的效率,例如

游戏代码分发效率优化 (简案)

只需下载镜像后即刻加载,无需考虑解压、小文件写入性能等问题

代码打包

# 使用压缩算法 Lz4 打包镜像
mksquashfs UnrealEngine-5.5.0-release UnrealEngine-5.5.0-release.squashfs -comp lz4

代码挂载

mkdir -p /home/game
mkdir -p /home/game/{src,data,work,server}

sudo mount -t squashfs UnrealEngine-5.5.0-release.squashfs /home/game/src

sudo mount -t overlay -o lowerdir=/home/game/src,upperdir=/home/game/data,workdir=/home/game/work overlay /home/game/server

代码卸载

sudo umount /home/game/src
sudo umount /home/game/server

扩展

由于OverlayFS的loverdir支持多层挂载,因此针对软件开发的版本,可以挂载N个版本的squashfs 来实现高效分发,例如

- UnrealEngine-5.5.0-base.squashfs /home/game/src/5.5.0
- UnrealEngine-5.5.1-patch.squashfs /home/game/src/5.5.1
- UnrealEngine-5.5.2-patch.squashfs /home/game/src/5.5.2
- UnrealEngine-5.5.3-patch.squashfs /home/game/src/5.5.3

挂载时,按照 lowerdir=5.5.3:5.5.5.2:5.5.1:5.5.0 即刻实现超快更新 (需要重新挂载,验证无法重新remount, 不如aufs)