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 存储驱动程序。
停止 Docker。
$ sudo systemctl stop docker将
/var/lib/docker的内容复制到临时位置。$ cp -au /var/lib/docker /var/lib/docker.bk如果您想使用与
/var/lib/不同的独立后备文件系统,请格式化该文件系统并将其挂载到/var/lib/docker。 确保将此挂载添加到/etc/fstab以使其永久生效。编辑
/etc/docker/daemon.json。如果它尚不存在,则创建它。假设 该文件为空,添加以下内容。{ "storage-driver": "overlay2" }如果
daemon.json文件包含无效的 JSON,Docker 将无法启动。启动 Docker。
$ sudo systemctl start docker验证守护进程是否正在使用
overlay2存储驱动。 使用docker info命令并查找Storage Driver和Backing filesystem。$ docker info Containers: 0 Images: 0 Storage Driver: overlay2 Backing Filesystem: xfs Supports d_type: true Native Overlay Diff: true <...>
Docker 现在正在使用 overlay2 存储驱动程序,并已自动创建了包含所需的 lowerdir、upperdir、merged
和 workdir 构造的 overlay 挂载。
继续阅读以了解 OverlayFS 在您的 Docker 容器中如何工作的详细信息,以及性能建议和关于其与不同后备文件系统兼容性限制的信息。
overlay2 驱动程序如何工作
OverlayFS 在单个 Linux 主机上将两个目录分层,并将它们呈现为
单个目录。这些目录被称为层,统一过程被称为联合挂载。OverlayFS 将下层目录
称为 lowerdir,上层目录称为 upperdir。统一视图通过
其自己的目录 merged 暴露出来。
overlay2 驱动原生支持最多 128 个底层 OverlayFS 层。此功能为与层相关的 Docker 命令(如 docker build 和 docker 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 的目录暴露,该目录实际上是容器的挂载点。

当镜像层和容器层包含相同的文件时,容器层(upperdir)优先,并掩盖镜像层中相同文件的存在。
要创建容器,overlay2 驱动程序将代表镜像顶层目录的目录与为容器新建的目录组合在一起。镜像的层是覆盖中的 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 目录是 lowerdir 和 upperdirs 的联合挂载,它构成了从运行容器内部看到的文件系统视图。
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
与其他写时复制(copy-on-write)文件系统一样,每当容器首次写入文件时,OverlayFS 会执行向上复制(copy-up)操作。这可能会增加写入操作的延迟,特别是对于大文件。然而,一旦文件被向上复制,对该文件的所有后续写入操作都将在上层(upper layer)进行,无需进一步执行向上复制操作。
性能最佳实践
以下通用的性能最佳实践适用于 OverlayFS。
使用快速存储
固态硬盘(SSD)比旋转磁盘提供更快的读写速度。
使用卷处理高写入工作负载
卷为写密集型工作负载提供最佳且最可预测的性能。这是因为它们绕过了存储驱动程序,不会引入精简配置和写时复制可能带来的任何潜在开销。卷还有其他优势,例如允许您在容器之间共享数据,即使没有正在运行的容器使用它们,也能持久化您的数据。
OverlayFS 兼容性的限制
总结OverlayFS与其他文件系统不兼容的方面:
open(2)- OverlayFS 仅实现了 POSIX 标准的一个子集。这可能导致某些 OverlayFS 操作违反 POSIX 标准。一种这样的操作是复制操作。假设您的应用程序调用了
fd1=open("foo", O_RDONLY),然后调用了fd2=open("foo", O_RDWR)。在这种情况下, 您的应用程序期望fd1和fd2指向同一个文件。然而,由于在第二次调用open(2)之后发生了复制向上操作, 这些描述符指向不同的文件。fd1继续引用镜像中的文件 (lowerdir),而fd2引用容器中的文件 (upperdir)。一个解决方法是touch这些文件,这将触发复制操作。所有后续的open(2)操作,无论 是只读还是读写访问模式,都引用容器中的文件 (upperdir)。yum已知受影响,除非安装了yum-plugin-ovl软件包。 如果您的发行版(例如 6.8 或 7.2 之前的 RHEL/CentOS)中没有yum-plugin-ovl软件包, 您可能需要在运行yum install之前运行touch /var/lib/rpm/*。 该软件包实现了上述针对yum的touch变通方法。 rename(2)- OverlayFS 不完全支持
rename(2)系统调用。您的应用程序需要检测其失败情况,并回退到“复制并取消链接”策略。