使用 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
结构自动创建了覆盖挂载。
继续阅读有关 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
要查看在将 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
的目录公开,该目录实际上是容器的挂载点。


如果镜像层和容器层包含相同的文件,则容器层 (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,该 ID 是 OverlayFS 的 lowerdir
。
$ cat /var/lib/docker/overlay2/ec444863a55a9f1ca2df72223d459c5d940a721b2288ff86a3f27be28b53be6c/lower-id
55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358
upper
目录包含容器的读写层的目录,该目录对应于 OverlayFS 的 upperdir
。
merged
目录是 lowerdir
和 upperdirs
的联合挂载,它包含从运行的容器内看到的文件系统的视图。
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 在文件级别而不是块级别工作。这意味着所有 OverlayFS copy_up
操作都会复制整个文件,即使该文件很大并且只有一小部分被修改。这可能会对容器写入性能产生明显的影响。但是,值得注意的是两点:
copy_up
操作只发生在第一次写入给定文件时。随后对同一文件的写入操作会针对已复制到容器的该文件副本进行操作。OverlayFS 与多个层一起工作。这意味着当在具有多个层的镜像中搜索文件时,性能可能会受到影响。
删除文件和目录
当在容器内删除文件时,会在容器 (
upperdir
) 中创建一个白洞文件。镜像层 (lowerdir
) 中的该文件版本不会被删除(因为lowerdir
是只读的)。但是,白洞文件会阻止该文件对容器可用。当在容器内删除目录时,会在容器 (
upperdir
) 中创建一个不透明目录。它的工作原理与白洞文件相同,并且有效地阻止了对该目录的访问,即使该目录仍然存在于镜像 (lowerdir
) 中。
重命名目录
只有当源路径和目标路径都在顶层时,才允许调用 rename(2)
来重命名目录。否则,它会返回 EXDEV
错误(“不允许跨设备链接”)。您的应用程序需要设计为处理 EXDEV
错误并回退到“复制和取消链接”策略。
OverlayFS 和 Docker 性能
overlay2
的性能可能比 btrfs
好。但是,请注意以下细节
页面缓存
OverlayFS 支持页面缓存共享。多个访问同一文件的容器共享该文件的单个页面缓存条目。这使得 overlay2
驱动程序在内存方面高效,并且成为 PaaS 等高密度用例的理想选择。
Copyup
与其他写时复制文件系统一样,每当容器首次写入文件时,OverlayFS 都会执行 copy-up 操作。这可能会在写入操作中增加延迟,尤其是对于大型文件。但是,一旦文件被复制,所有后续对该文件的写入都会发生在上层,而无需进一步的 copy-up 操作。
性能最佳实践
以下通用性能最佳实践适用于 OverlayFS。
使用快速存储
固态硬盘 (SSD) 提供比旋转磁盘更快的读写速度。
对写入密集型工作负载使用卷
卷为写入密集型工作负载提供了最佳和最可预测的性能。这是因为它们绕过存储驱动程序,并且不会产生由精简配置和写时复制引入的任何潜在开销。卷还有其他优点,例如允许您在容器之间共享数据,并且即使没有运行的容器使用它们,也能持久化您的数据。
OverlayFS 兼容性限制
概括 OverlayFS 与其他文件系统不兼容的方面
open(2)
- OverlayFS 只实现了 POSIX 标准的子集。这会导致某些 OverlayFS 操作违反 POSIX 标准。其中一项操作是 copy-up 操作。假设您的应用程序调用
fd1=open("foo", O_RDONLY)
,然后调用fd2=open("foo", O_RDWR)
。在这种情况下,您的应用程序希望fd1
和fd2
引用同一个文件。但是,由于在第二次调用open(2)
之后发生的 copy-up 操作,描述符会引用不同的文件。fd1
继续引用镜像 (lowerdir
) 中的文件,而fd2
引用容器 (upperdir
) 中的文件。解决此问题的办法是使用touch
命令触碰文件,这会导致 copy-up 操作执行。所有随后的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)
系统调用。您的应用程序需要检测其失败并回退到“复制和取消链接”策略。