OverlayFS 存储驱动程序

OverlayFS 是一个联合文件系统。

此页面将 Linux 内核驱动程序称为OverlayFS,将 Docker 存储驱动程序称为overlay2

注意

对于fuse-overlayfs驱动程序,请查看无根模式文档

先决条件

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存储驱动程序配置 Docker

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

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

  1. 停止 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命令并查找Storage DriverBacking filesystem

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

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

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

overlay2驱动程序的工作原理

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

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

磁盘上的镜像和容器层

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

警告

不要直接操作/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

新的l(小写L)目录包含作为符号链接的缩短的层标识符。这些标识符用于避免遇到对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目录,其中包含其父层和自身的内容的统一视图,以及一个work目录,OverlayFS内部使用该目录。

$ 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 中使用overlay存储驱动程序时存在的挂载点,请使用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挂载点是读写的。

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

How Docker constructs map to OverlayFS constructs

如果镜像层和容器层包含相同的文件,则容器层 (upperdir) 优先,并隐藏镜像层中相同文件的存在。

要创建容器,overlay2驱动程序将表示镜像顶层的目录与容器的新目录组合在一起。镜像的层是 overlay 中的lowerdirs,并且是只读的。容器的新目录是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 的内部目录。

要查看在 Docker 中使用overlay2存储驱动程序时存在的挂载点,请使用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挂载点是读写的。

overlay2如何处理容器的读写操作

读取文件

考虑三个场景,其中容器使用 overlay 打开文件进行读取访问。

容器层中不存在该文件

如果容器打开文件进行读取访问,并且该文件尚不存在于容器 (upperdir) 中,则从镜像 (lowerdir) 读取该文件。这几乎不会产生性能开销。

该文件仅存在于容器层

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

该文件存在于容器层和镜像层中

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

修改文件或目录

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

首次写入文件

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

但是,OverlayFS 在文件级别而不是块级别工作。这意味着所有 OverlayFS 的copy_up操作都会复制整个文件,即使文件很大,并且仅修改了文件的一小部分。这可能会对容器写入性能产生明显的影响。但是,有两点需要注意

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

  • OverlayFS 可与多个层一起工作。这意味着在搜索包含许多层的镜像中的文件时,可能会影响性能。

删除文件和目录

  • 当在容器中删除文件时,会在容器 (upperdir) 中创建一个白洞文件。镜像层 (lowerdir) 中的文件版本不会被删除(因为lowerdir是只读的)。但是,白洞文件会阻止容器访问该文件。

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

重命名目录

仅当源路径和目标路径都位于顶层时,才允许调用目录的rename(2)。否则,它将返回EXDEV错误(“不允许跨设备链接”)。您的应用程序需要设计为处理EXDEV并回退到“复制和取消链接”策略。

OverlayFS 和 Docker 性能

overlay2的性能可能比btrfs更好。但是,请注意以下细节

页面缓存

OverlayFS 支持页面缓存共享。访问同一文件的多个容器共享该文件的单个页面缓存条目。这使得overlay2驱动程序在内存方面效率很高,并且是 PaaS 等高密度用例的理想选择。

Copyup

与其他写时复制文件系统一样,每当容器第一次写入文件时,OverlayFS 都会执行复制操作。这可能会增加写入操作的延迟,尤其对于大型文件而言。但是,一旦文件被复制,对该文件的所有后续写入都发生在上层,无需进一步的复制操作。

性能最佳实践

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

使用快速存储

固态驱动器 (SSD) 比旋转磁盘提供更快的读写速度。

对写入密集型工作负载使用卷

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

OverlayFS 兼容性限制

总结 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会受到影响。如果在您的发行版(例如 RHEL/CentOS 6.8 或 7.2 之前的版本)中没有yum-plugin-ovl包,则可能需要在运行yum install之前运行touch /var/lib/rpm/*。此包实现了上面提到的yumtouch解决方法。

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