Overlay 文件系统介绍

Overlayfs是一种堆叠文件系统,它依赖并建立在其它的文件系统之上(例如ext4fsxfs等等),并不直接参与磁盘空间结构的划分,仅仅将原来底层文件系统中不同的目录进行“合并”,然后向用户呈现。因此对于用户来说,它所见到的overlay文件系统根目录下的内容就来自挂载时所指定的不同目录的“合集”。如下图所示

其挂载文件的基本命令如下:

mount -t overlay overlay -o lowerdir=lower1:lower2:lower3,upperdir=upper,workdir=work merged

其中lower1:lower2:lower3表示不同的lower层目录,不同的目录使用:分隔,层次关系依次为lower1 > lower2 > lower3

多lower层功能支持在Linux-4.0合入,Linux-3.18版本只能指定一个lower dir

  • upper层是目录和文件系统挂载后用于存放临时和间接文件的工作基目录(work base dir)
  • merged目录就是最终的挂载点目录

正常执行以上命令后,overlayfs就成功挂载到merged目录下了。

挂载选项支持(即”-o”参数): 1)lowerdir=xxx:指定用户需要挂载的lower层目录(支持多lower,最大支持500层); 2)upperdir=xxx:指定用户需要挂载的upper层目录; 3)workdir=xxx:指定文件系统的工作基础目录,挂载后内容会被清空,且在使用过程中其内容用户不可见; 4)default_permissions:功能未使用; 5)redirect_dir=on/off:开启或关闭redirect directory特性,开启后可支持merged目录和纯lower层目录的rename/renameat系统调用; 6)index=on/off:开启或关闭index特性,开启后可避免hardlink copyup broken问题。

其中lowerdir、upperdir和workdir为基本的挂载选项,redirect_dir和index涉及overlayfs为功能支持选项,除非内核编译时默认启动,否则默认情况下这两个选项不启用

Overlay 文件系统挂载示例

现在以ext4文件系统作为基础文件系统挂载overlayfs。

首先创建overlayfs文件系统的基础目录5个,然后分别在两个lower目录和upper目录下创建不同文件foo1、foo2和foo3,最后在这3个目录下分别创建同名目录dir并同时在dir目录下创建同名文件aa和bb。

在挂载overlayfs文件系统之后:

  • 在merge目录下能够看到foo1、foo2和foo3,这就是overlayfs的上下层合并;
  • 在merge/dir目录下看到来自lower1的文件和aa来自upper层的文件bb,位于最底层lower2中的文件aa被lower1中的同名文件覆盖,位于lower1中的文件bb被upper中的同名文件覆盖,这就是overlayfs的“上下层同名目录合并与同名文件覆盖”特性,

对应的组织结构如下图所示:

Overlay 文件系统工作原理

用户在overlayfs的merge目录中查看文件时,会调用内核的getdents系统调用

一般情况下该系统调用会调用文件系统接口,它仅会遍历当前目录中的所有目录项并返回给用户,所以用户能够看到这个目录下的所有文件或子目录。

但在overlayfs中,如果目录不是仅来自单独的一层(当前时多层合并的或者其中可能存在曾经发生过合并的迹象),它会逐级遍历扫描所有层的同名目录,然后把各层目录中的内容返回给用户,因此用户就会感觉到上下层同名目录合并;

与此同时,如果在遍历扫描的过程中发现了同名的文件,它会判断该文件来自那一层,从而忽略来自lower层的文件而只显示来自upper层的文件,因此用户会感觉到上下层同名文件覆盖。

Overlayfs backingfs要求

从docker初始化overlay2存储驱动的Init函数可知,overlay2的存储驱动所基于的后端文件系统必须要支持d_type属性。

那么什么是d_type属性呢?

这个就需要从VFS中的目录项说起。目录项(directory dentry),简称为 dentry,用来记录文件的名字、索引节点指针以及与其他目录项的关联关系。多个关联的目录项,就构成了文件系统的目录结构。

在linux下一般用dirent结构体来表示,在dirent结构体中d_type成员表示文件类型,4表示目录,8表示普通文件,0表示未知设备。一般的文件系统直接用d_type判断,不会出现问题,但是在NFS场景中,实际的文件存放在另一台机器上,通过NFS映射到当年机器上。读文件的时候发现d_type为0,导致判断错误

The overlay2 filesystem itself is documented as requiring xfs with ftype=1 or ext4 as the backing filesystem, not NFS.

Overlay 文件系统使用约束限制

  1. 用户可以不指定upperdir和workdir,但同时必须保证lowerdir >= 2层,此时的文件系统为只读挂载(这也是只读挂载overlayfs的唯一方法);如果用户指定upperdir,则必须保证upperdir所在的文件系统是可读写的,同时还需指定workdir,并且workdir不能和upperdir是父子目录关系。

  2. 常见的文件系统中,upperdir所在的文件系统不能是nfs、cifs、gfs2、vfat、ocfs2、fuse、isofs、jfs和另一个overlayfs等文件系统,而lowerdir所在的文件系统可以是nfs、cifs这样的远程文件系统,也可以是另一个overlayfs。因为upperdir是可以写入的,所以需要避免一些特性上的不兼容(例如vfat是大小写不敏感的文件系统),而lowerdir是只读文件系统,相对要求会低一些。

  3. 用户应该尽量避免多个overlayfs使用同一个upperdir或workdir,尽管默认情况下是可以挂载成功的,但是内核还是会输出告警日志来提示用户。

  4. 用户指定的lowerdir最多可以支持500层。虽然如此,但是由于mount的挂载选项最多支持1个page的输入(默认大小为4KB),所以如果指定的lowerdir数量较多且长度较长,会有溢出而导致挂载失败的风险(目前内核的-o挂载选项不支持超过1个内存页,即4KB大小)。

  5. 指定的upperdir和workdir所在的基础文件系统的readdir接口需要支持dtype返回参数,否则将会导致本应该隐藏的whiteout文件(后文介绍)暴露,当然目前ext4和xfs等主流的文件系统都是支持的,如果不支持那内核会给出警告提示但不会直接拒绝挂载。

  6. 指定的upperdir和workdir所在的基础文件系统需要支持xattr扩展属性,否则在功能方面会受到限制,例如后面的opaque目录将无法生成,并且redirect dir特性和index特性也无法使用。

  7. 如果upperdir和各lowerdir是来自同一个基础文件系统,那在文件触发copyup前后,用户在merge层通过ls命令或stat命令看到的Device和inode值保持不变,否则会发生改变。

如何根据layer digest找到镜像本地存储信息

manifest & image config

image config 文件路径:/var/lib/docker/image/overlay2/imagedb/content/sha256/<image-id>

diff_id是layer从镜像仓库下载下来后解压成tar包格式

image config 文件路径:/var/lib/docker/image/overlay2/imagedb/content/sha256/<image-id>

diff_id是layer从镜像仓库下载下来后解压成tar包格式

参考文章