OverlayFS 驱动


OverlayFS 是一个现代联合文件系统。 本主题将 Linux 内核驱动程序称为OverlayFS,将 Docker 存储驱动程序称为overlay2

注意:对于fuse-overlayfs驱动程序,请查看 Rootless 模式文档

OverlayFS 是推荐的存储驱动程序,如果您满足以下先决条件,则支持:

  • Linux 内核版本 4.0 或更高版本,或者使用内核版本 3.10.0-514 或更高版本的 RHEL 或 CentOS。

  • overlay2驱动程序在xfs作后备的文件系统上受支持,但仅在启用d_type=true时才支持。

    使用xfs_info验证ftype选项是否设置为1。要正确格式化xfs文件系统,请使用标志-n ftype=1

  • 更改存储驱动程序会使本地系统上的现有容器和镜像无法访问。 在更改存储驱动程序之前,使用docker save保存您构建的任何镜像或将它们推送到 Docker Hub 或私有注册表,以便您以后不需要重新创建它们。

在执行此过程之前,您必须首先满足所有 先决条件

以下步骤概述了如何配置overlay2存储驱动程序。

  1. stop Docker

    $ sudo systemctl stop docker
    

  2. /var/lib/docker/的内容复制临时位置。

    $ cp -au /var/lib/docker /var/lib/docker.bk
    

  3. 如果您想使用/var/lib/使用的单独的后备文件系统,请格式化该文件系统并将其挂载到/var/lib/docker/中。 确保将此安装添加到/etc/fstab以使其永久。

  4. 编辑/etc/docker/daemon.json。如果它尚不存在,请创建它。 假设该文件为空,添加以下内容。

    {
      "storage-driver": "overlay2"
    }
    
    如果 daemon.json 文件包含格式错误的 JSON,则 Docker 不会启动。

  5. 启动 Docker。

    $ sudo systemctl start docker
    

  6. 验证守护程序是否正在使用overlay2存储驱动程序。 使用docker info命令并查找存储驱动程序和支持的文件系统。

    $ docker info
    
    Containers: 0
    Images: 0
    Storage Driver: overlay2
     Backing Filesystem: xfs
     Supports d_type: true
     Native Overlay Diff: true
    ...
    

Docker 现在使用overlay2存储驱动程序, 并自动创建具有所需的lowerdirupperdirmergedworkdir结构的 overlay 挂载。

继续阅读有关 OverlayFS 如何在 Docker 容器中工作的详细信息,以及性能建议和有关其与不同后备文件系统的兼容性限制的信息。

如果您仍在使用overlay驱动程序而不是overlay2,请参阅 overlay 驱动程序的工作原理

OverlayFS 在单个 Linux 主机上分层为两个目录,并将它们呈现为单个目录。 这些目录称为层,统一过程称为联合挂载。 OverlayFS 将下层目录称为lowerdir,将上层目录称为upperdir。 统一视图通过其自己的名为merged的目录公开。

overlay2驱动程序本身支持最多 128 个较低的 OverlayFS 层。 此功能为与层相关的 Docker 命令(例如docker builddocker commit)提供了更好的性能,并在后备文件系统上消耗更少的 inode。

使用docker pull ubuntu下载五层镜像后,可以看到/var/lib/docker/overlay2下有6个目录。

小心:不要直接操作/var/lib/docker/中的任何文件或目录。 这些文件和目录由 Docker 管理。
$ ls -l /var/lib/docker/overlay2
total 24
drwx------ 5 root root 4096 Jun 20 07:36 223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7
drwx------ 3 root root 4096 Jun 20 07:36 3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b
drwx------ 5 root root 4096 Jun 20 07:36 4e9fa83caff3e8f4cc83693fa407a4a9fac9573deaf481506c102d484dd1e6a1
drwx------ 5 root root 4096 Jun 20 07:36 e8876a226237217ec61c4baf238a32992291d059fdac95ed6303bdff3f59cff5
drwx------ 5 root root 4096 Jun 20 07:36 eca1e4e1694283e001f200a667bb3cb40853cf2d1b12c29feda7422fed78afed
drwx------ 2 root root 4096 Jun 20 07:36 l

新的lL小写)目录包含缩短的层标识符作为符号链接。 这些标识符用于避免达到mount命令参数的页面大小限制。

$ ls -l /var/lib/docker/overlay2/l
total 20
lrwxrwxrwx 1 root root 72 Jun 20 07:36 6Y5IM2XC7TSNIJZZFLJCS6I4I4 -> ../3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 B3WWEFKBG3PLLV737KZFIASSW7 -> ../4e9fa83caff3e8f4cc83693fa407a4a9fac9573deaf481506c102d484dd1e6a1/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 JEYMODZYFCZFYSDABYXD5MF6YO -> ../eca1e4e1694283e001f200a667bb3cb40853cf2d1b12c29feda7422fed78afed/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 NFYKDW6APBCCUCTOUSYDH4DXAT -> ../223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 UL2MW33MSE3Q5VYIKBRN4ZAGQP -> ../e8876a226237217ec61c4baf238a32992291d059fdac95ed6303bdff3f59cff5/diff

最底层包含一个名为link的文件,其中包含缩短的标识符的名称,以及一个名为diff的目录,其中包含该层的内容。

$ ls /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/
diff  link

$ cat /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/link
6Y5IM2XC7TSNIJZZFLJCS6I4I4

$ ls  /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/diff
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

第二低层和每个较高层包含一个名为lower的文件(表示其父级)和一个名为diff的目录(包含其内容)。 它还包含一个merged目录,其中包含其父层和自身的统一内容,以及一个由 OverlayFS 内部使用的work目录。

$ ls /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7
diff  link  lower  merged  work

$ cat /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/lower
l/6Y5IM2XC7TSNIJZZFLJCS6I4I4

$ ls /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/diff/
etc  sbin  usr  var

要查看将覆盖存储驱动程序与 Docker 结合使用时存在的挂载,请使用mount命令。 为了便于阅读,下面的输出被截断。

$ mount | grep overlay
overlay on /var/lib/docker/overlay2/9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/merged
type overlay (rw,relatime,
lowerdir=l/DJA75GUWHWG7EWICFYX54FIOVT:l/B3WWEFKBG3PLLV737KZFIASSW7:l/JEYMODZYFCZFYSDABYXD5MF6YO:l/UL2MW33MSE3Q5VYIKBRN4ZAGQP:l/NFYKDW6APBCCUCTOUSYDH4DXAT:l/6Y5IM2XC7TSNIJZZFLJCS6I4I4,
upperdir=9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/diff,
workdir=9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/work)
...

第二行的rw表明overlay挂载是可读写的。

OverlayFS 在单个 Linux 主机上分层为两个目录,并将它们呈现为单个目录。 这些目录称为层,统一过程称为联合挂载。 OverlayFS 将下层目录称为lowerdir,将上层目录称为upperdir。 统一视图通过其自己的名为merged的目录公开。

下图显示了 Docker 镜像和 Docker 容器是如何分层的。 镜像层是lowerdir,容器层是upperdir。 如果镜像有多个图层,则使用多个lowerdir目录。 统一视图通过名为merged的目录公开,该目录实际上是容器挂载点。 该图显示了 Docker 构造 如何映射到 OverlayFS 构造。

overlay-constructs

当镜像层和容器层包含相同的文件时,容器层 "获胜" 并掩盖镜像层中相同文件的存在。

为了创建容器,overlay2驱动程序将表示镜像顶层的目录与容器的新目录结合起来。 镜像的图层是覆盖层中的lowerdir目录,并且是只读的。 容器的新目录是upperdir并且是可写的。

以下 docker pull 命令显示 Docker 主机下载包含五层的 Docker 镜像。

$docker pull ubuntu

Using default tag: latest
latest: Pulling from library/ubuntu

5ba4f30e5bea: Pull complete
9d7d19c9dc56: Pull complete
ac6ad7efd0f9: Pull complete
e7491a747824: Pull complete
a3ed95caeb02: Pull complete
Digest: sha256:46fb5d001b88ad904c5c732b086b596b92cfb4a4840a3abd0e35dbb6870585e4
Status: Downloaded newer image for ubuntu:latest

每个镜像层在/var/lib/docker/overlay/中都有自己的目录,其中包含其内容,如下所示。 镜像层ID与目录ID不对应。

小心:不要直接操作/var/lib/docker/中的任何文件或目录。 这些文件和目录由 Docker 管理。
$ ls -l /var/lib/docker/overlay/
total 20
drwx------ 3 root root 4096 Jun 20 16:11 38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8
drwx------ 3 root root 4096 Jun 20 16:11 55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358
drwx------ 3 root root 4096 Jun 20 16:11 824c8a961a4f5e8fe4f4243dab57c5be798e7fd195f6d88ab06aea92ba931654
drwx------ 3 root root 4096 Jun 20 16:11 ad0fe55125ebf599da124da175174a4b8c1878afe6907bf7c78570341f308461
drwx------ 3 root root 4096 Jun 20 16:11 edab9b5e5bf73f2997524eebeac1de4cf9c8b904fa8ad3ec43b3504196aa3801

镜像层目录包含该层特有的文件以及与较低层共享的数据的硬链接。 这可以有效地利用磁盘空间。

$ ls -i /var/lib/docker/overlay2/38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8/root/bin/ls
19793696 /var/lib/docker/overlay2/38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8/root/bin/ls

$ ls -i /var/lib/docker/overlay2/55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358/root/bin/ls
19793696 /var/lib/docker/overlay2/55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358/root/bin/ls

容器还存在于 Docker 主机文件系统的磁盘上/var/lib/docker/overlay/下。 如果使用ls -l命令列出正在运行的容器的子目录,则存在三个目录和一个文件:

$ ls -l /var/lib/docker/overlay2/<directory-of-running-container>
total 16
-rw-r--r-- 1 root root   64 Jun 20 16:39 lower-id
drwxr-xr-x 1 root root 4096 Jun 20 16:39 merged
drwxr-xr-x 4 root root 4096 Jun 20 16:39 upper
drwx------ 3 root root 4096 Jun 20 16:39 work

lower-id文件包含容器所基于的镜像顶层的 ID,即 OverlayFS lowerdir

$ cat /var/lib/docker/overlay2/ec444863a55a9f1ca2df72223d459c5d940a721b2288ff86a3f27be28b53be6c/lower-id
55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358

upper目录包含容器读写层的内容,对应于OverlayFS upperdir

merged的目录是lowerdirupperdirs的联合挂载,其中包含正在运行的容器内的文件系统视图。

work目录位于 OverlayFS 内部。

要查看将overlay2存储驱动程序与 Docker 结合使用时存在的挂载,请使用mount命令。 为了便于阅读,下面的输出被截断。

$ mount | grep overlay
overlay on /var/lib/docker/overlay2/l/ec444863a55a.../merged
type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/55f1e14c361b.../root,
upperdir=/var/lib/docker/overlay2/l/ec444863a55a.../upper,
workdir=/var/lib/docker/overlay2/l/ec444863a55a.../work)

第二行的rw表明overlay挂载是可读写的。

考虑容器打开文件以通过覆盖进行读取访问的三种场景。

  • 该文件不存在于容器层中:如果容器打开一个文件进行读取访问,并且该文件尚不存在于容器(upperdir)中,则会从镜像(lowerdir)中读取该文件。 这会产生非常小的性能开销。

  • 文件只存在于容器层:如果容器打开一个文件进行读访问,并且该文件存在于容器(upperdir)中而不存在于镜像(lowerdir)中,则直接从容器中读取。

  • 文件同时存在于容器层和镜像层:如果容器打开一个文件进行读访问,并且该文件存在于镜像层和容器层,则读取该文件在容器层的版本。 容器层(upperdir)中的文件会掩盖镜像层(lowerdir)中同名的文件。

考虑一些修改容器中的文件的场景。

  • 第一次写入文件:容器第一次写入现有文件时,该文件在容器(upperdir)中不存在。 overlay驱动程序执行copy_up操作,将文件从镜像 (lowerdir) 复制到容器 (upperdir)。 然后,容器将更改写入容器层中文件的新副本。

    然而,OverlayFS 在文件级别而不是块级别工作。这意味着所有 OverlayFS 复制操作都会复制整个文件,即使文件非常大并且只修改了其中的一小部分。 这会对容器写入性能产生显着影响。 然而,有两件事值得注意:

    • 复制操作仅在第一次写入给定文件时发生。 对同一文件的后续写入将针对已复制到容器的文件副本进行操作。

    • OverlayFS 适用于多个层。 这意味着在具有多层的镜像中搜索文件时,性能可能会受到影响。

  • 删除文件和目录

    • 当删除容器内的文件时,会在容器(upperdir)中创建一个 whiteout 文件。 镜像层(lowerdir)中的文件版本不会被删除(因为lowerdir是只读的)。 但是,whiteout 文件会阻止容器使用它。

    • 当删除容器内的目录时,会在容器内创建一个不透明目录(upperdir)。这与 whiteout 文件的工作方式相同,并且有效地防止目录被访问,即使它仍然存在于镜像中(upperdir)。

  • 重命名目录:只有当源路径和目标路径都位于顶层时,才允许对目录调用rename(2)。 否则,它返回EXDEV错误 (“cross-device link not permitted” “不允许跨设备链接”)。 您的应用程序需要设计为处理EXDEV并回退到 复制和取消链接 策略。

overlay2驱动程序比devicemapper性能更高。在某些情况下,overlay2的性能也可能比btrfs更好。 但是,请注意以下详细信息。

  • 页面缓存: OverlayFS 支持页面缓存共享。 访问同一文件的多个容器共享该文件的单个页面缓存条目。 这使得overlay2驱动程序能够高效利用内存,并且成为 PaaS 等高密度用例的良好选择。

  • copy_up: 与其他写时复制文件系统一样,每当容器首次写入文件时,OverlayFS 都会执行复制操作。 这会增加写入操作的延迟,尤其是对于大文件。 然而,一旦文件被复制,所有后续对该文件的写入都发生在上层,而不需要进一步的复制操作。

以下通用性能最佳实践适用于 OverlayFS。

  • 使用快速存储:固态驱动器 (SSD) 提供比旋转磁盘更快的读取和写入速度。

  • 将卷用于写入密集型工作负载:卷为写入密集型工作负载提供最佳且最可预测的性能。 这是因为它们绕过存储驱动程序,并且不会产生精简配置和写时复制带来的任何潜在开销。 卷还有其他好处,例如允许您在容器之间共享数据并保留数据,即使没有正在运行的容器正在使用它们。

总结一下 OverlayFS 与其他文件系统不兼容的方面:

  • open(2):OverlayFS 仅实现 POSIX 标准的子集。 这可能会导致某些 OverlayFS 操作违反 POSIX 标准。 一种这样的操作是复制操作。 假设您的应用程序调用fd1=open("foo", O_RDONLY),然后调用fd2=open("foo", O_RDWR)。 在这种情况下,您的应用程序期望fd1fd2引用同一文件。 但是,由于第二次调用open(2)之后发生了复制操作,描述符引用了不同的文件。 fd1继续引用镜像中的文件(lowerdir),fd2引用容器中的文件(upperdir)。 解决此问题的方法是touch导致发生复制操作的文件。 所有后续的open(2)操作,无论是只读还是读写访问模式,都会引用容器 (upperdir) 中的文件。

    除非安装yum-plugin-ovl软件包,否则yum会受到已知的影响。 如果yum-plugin-ovl软件包在您的发行版(例如 6.8 或 7.2 之前的 RHEL/CentOS)中不可用,则可能需要在运行yum install之前运行touch /var/lib/rpm/*。 该包实现了上面提到的针对yumtouch解决方法。

  • rename(2):OverlayFS 不完全支持rename(2)系统调用。您的应用程序需要检测其故障并回退到 复制并取消链接 策略。