使用 OverlayFS 存储驱动

OverlayFS 是一个联合文件系统。

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

笔记

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

先决条件

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

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

  • 该驱动程序在支持文件系统overlay2上受支持,但仅在启用时才支持。xfsd_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 指的是下层目录 aslowerdir和上层目录 a upperdir。统一视图通过其自己的名为 的目录公开merged

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

磁盘上的映像和容器层

使用 下载完五层镜像后docker pull ubuntu,可以看到 下有 6 个目录/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

overlay要查看将存储驱动程序与 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挂载是可读写的。

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

Docker 如何构造映射到 OverlayFS 构造

当镜像层和容器层包含相同文件时,容器层(upperdir)优先并掩盖镜像层中相同文件的存在。

为了创建容器,overlay2驱动程序将代表图像顶层的目录与容器的新目录结合起来。图像的图层位于lowerdirs叠加层中并且是只读的。容器的新目录是 theupperdir并且是可写的。

磁盘上的映像和容器层

以下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挂载是可读写的。

容器如何与overlay2一起读写

读取文件

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

容器层不存在该文件

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

该文件只存在于容器层

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

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

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

修改文件或目录

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

第一次写入文件

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

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

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

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

删除文件和目录

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

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

重命名目录

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

OverlayFS 和 Docker 性能

overlay2可能比 表现更好btrfs。但是,请注意以下详细信息:

页面缓存

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

复制

与其他写时复制文件系统一样,每当容器首次写入文件时,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引用映像 ( ) 中的文件lowerdirfd2引用容器 ( upperdir) 中的文件。解决此问题的方法是针对touch导致发生复制操作的文件。所有后续open(2)操作,无论只读或读写访问模式,都会引用容器 ( upperdir) 中的文件。

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

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