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 主机上分层两个目录,并将它们呈现为一个单一目录。这些目录称为层,统一过程称为联合挂载(union mount)。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
目录,其中包含其父层和自身的统一内容,以及一个由 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
要查看将 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
驱动程序将代表镜像顶层的目录与容器的新目录组合。镜像层在 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
目录是 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
一起工作
读取文件
考虑容器使用 overlay 打开文件进行读取访问的三种场景。
文件在容器层中不存在
如果容器打开文件进行读取访问,并且该文件在容器 (upperdir
) 中尚不存在,则从镜像 (lowerdir
) 读取。这会带来非常小的性能开销。
文件仅存在于容器层中
如果容器打开文件进行读取访问,并且该文件存在于容器 (upperdir
) 中而不在镜像 (lowerdir
) 中,则直接从容器读取。
文件在容器层和镜像层中都存在
如果容器打开文件进行读取访问,并且该文件存在于镜像层和容器层中,则读取容器层中的文件版本。容器层 (upperdir
) 中的文件会隐藏镜像层 (lowerdir
) 中同名的文件。
修改文件或目录
考虑容器中文件被修改的一些场景。
首次写入文件
容器首次写入现有文件时,该文件在容器 (upperdir
) 中不存在。overlay2
驱动程序会执行 copy_up
操作,将文件从镜像 (lowerdir
) 复制到容器 (upperdir
)。然后,容器将更改写入容器层中该文件的新副本。
然而,OverlayFS 工作在文件级别而不是块级别。这意味着所有 OverlayFS copy_up
操作都会复制整个文件,即使文件很大且只修改了其中一小部分。这可能对容器的写入性能产生显著影响。但是,有两点值得注意:
copy_up
操作仅在首次写入给定文件时发生。对同一文件的后续写入将针对已复制到容器的该文件副本进行操作。OverlayFS 使用多个层。这意味着在具有许多层的镜像中搜索文件时,性能可能会受到影响。
删除文件和目录
当在容器中删除一个 文件 时,会在容器 (
upperdir
) 中创建一个 whiteout 文件。镜像层 (lowerdir
) 中的文件版本不会被删除(因为lowerdir
是只读的)。但是,whiteout 文件会阻止该文件对容器可用。在容器内删除目录时,会在容器内创建一个不透明目录 (
upperdir
)。这与白out文件的工作方式相同,并有效阻止对该目录的访问,即使它仍然存在于镜像中 (lowerdir
)。
重命名目录
对目录调用 rename(2)
仅当源路径和目标路径都在顶层时才允许。否则,它会返回 EXDEV
错误("不允许跨设备链接")。您的应用程序需要设计来处理 EXDEV
并回退到"复制然后删除"的策略。
OverlayFS 和 Docker 性能
overlay2
的性能可能优于 btrfs
。然而,请注意以下详细信息
页缓存
OverlayFS 支持页面缓存共享。多个容器访问同一文件时,会共享该文件的一个页面缓存条目。这使得 overlay2
驱动程序内存效率高,并且是高密度使用场景(例如 PaaS)的良好选择。
Copyup
与其他写时复制文件系统一样,OverlayFS 会在容器首次写入文件时执行 copy-up 操作。这会增加写入操作的延迟,特别是对于大文件。然而,一旦文件被 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
包。如果在您的发行版中(例如 RHEL/CentOS 6.8 或 7.2 之前)此包不可用,您可能需要在运行yum install
之前运行touch /var/lib/rpm/*
。此包为yum
实现了上面提到的touch
解决方法。 rename(2)
- OverlayFS 不完全支持
rename(2)
系统调用。您的应用程序需要检测其失败并回退到"复制然后删除"的策略。