docs.dockoro.cn/docs/storage/drivers/overlayfs-driver.md

286 lines
16 KiB
Markdown
Raw Normal View History

---
outline: [2,5]
---
# OverlayFS 存储驱动程序
OverlayFS 是一个联合文件系统。
本页将 Linux 内核驱动程序称为` OverlayFS`,将 Docker 存储驱动程序称为 `overlay2`
:::info 注意
对于 `fuse-overlayfs` 驱动程序,请检查 [无根模式文档][rootless-doc]。
:::
## 先决条件
OverlayFS 是推荐的存储驱动程序,如果您满足以下先决条件,则支持 OverlayFS
- Linux 内核版本 4.0 或更高版本,或者使用内核版本 3.10.0-514 或更高版本的 RHEL 或 CentOS。
- `xfs` 后备文件系统支持 `overlay2` 驱动程序,但仅在启用 `d_type=true` 的情况下受支持。
使用 `xfs_info` 验证 `ftype` 选项是否设置为 `1`。要格式化 `xfs` 文件系统中,请使用标志 `-n ftype=1`
- 更改存储驱动程序将使本地系统上的现有容器和映像无法访问。在更改存储驱动程序之前,使用 `docker save` 保存您构建的任何映像或将其推送到 Docker Hub 或私有注册表,这样您以后就不需要重新创建它们。
## 使用 `overlay2` 存储驱动程序配置 Docker
在执行此过程之前,您必须首先满足所有 [先决条件](#先决条件)。
以下步骤概述了如何配置 `overlay2` 存储驱动程序。
1. 停止 Docker。
```bash
sudo systemctl stop docker
```
2.`/var/lib/docker` 的内容复制到临时位置。
```bash
cp -au /var/lib/docker /var/lib/docker.bk
```
3. 如果要使用与 `/var/lib/` 中,格式化文件系统并将其挂载到 `/var/lib/docker` 中。确保将此挂载添加到 `/etc/fstab` 以使其成为永久挂载。
4. 编辑 `/etc/docker/daemon.json`。如果尚不存在,请创建它。假设文件为空,请添加以下内容。
```json
{
"storage-driver": "overlay2"
}
```
如果 `daemon.json` 文件包含无效的 JSON则 Docker 不会启动。
5. 启动 Docker。
```bash
sudo systemctl start docker
```
6. 验证守护程序是否正在使用 `overlay2` 存储驱动程序。使用 `docker info` 命令并查找 `Storage Driver``Backing filesystem` (后备文件系统)。
```bash
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` 下看到六个目录。
:::warning 警告
不要直接操作 `/var/lib/docker/` 中。这些文件和目录由 Docker 管理。
:::
```bash
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` 命令参数的页面大小限制。
```bash
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` 的目录,其中包含该层的内容。
```bash
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` 的目录,其中包含其内容。它还包含一个合并目录,该目录包含其父层和自身的统一内容,以及一个由 OverlayFS 内部使用的`work`(工作)目录。
```bash
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` 命令。为了提高可读性,下面的输出被截断。
```bash
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 容器的分层方式。image 层是 `lowerdir`,容器层是 `upperdir` 的 URL 中。如果图像具有多个图层,则使用多个 `lowerdir` 目录。统一视图通过一个名为 `merged` 的目录公开,该目录实际上是容器挂载点。
![overlay_constructs](./overlay_constructs.webp)
如果图像层和容器层包含相同的文件,则容器层 `upperdir` 优先并掩盖图像层中存在相同文件。
要创建容器,`overlay2` 驱动程序将表示映像顶层的目录与容器的新目录组合在一起。图像的图层是叠加中的 `lowerdirs`,并且是只读的。容器的新目录是 `upperdir` 并且是可写的。
### 磁盘上的映像和容器层
以下 `docker pull` 命令显示了一个 Docker 主机正在下载一个包含五个层的 Docker 镜像。
```bash
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 不对应。
:::warning 注意
不要直接操作 `/var/lib/docker/` 中。这些文件和目录由 Docker 管理。
:::
```bash
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
```
图像图层目录包含该图层独有的文件以及与较低图层共享的数据的硬链接。这允许有效地使用磁盘空间。
```bash
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` 命令列出正在运行的容器的子目录,则存在三个目录和一个文件:
```bash
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`
```bash
cat /var/lib/docker/overlay2/ec444863a55a9f1ca2df72223d459c5d940a721b2288ff86a3f27be28b53be6c/lower-id
55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358
```
`upper` 目录包含容器的读写层的内容,对应于 OverlayFS `upperdir`
`merged` (合并)的目录是 `lowerdir``upperdirs` 的联合挂载,它包含正在运行的容器中的文件系统视图。
`work` (工作)目录是 OverlayFS 的内部目录。
要查看将 `overlay2` 存储驱动程序与 Docker 一起使用时存在的挂载,请使用 `mount` 命令。为了提高可读性,以下输出被截断。
```bash
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 适用于多个图层。这意味着在具有多个图层的图像中搜索文件时,性能可能会受到影响。
#### 删除文件和目录
- 在容器中删除文件时,将在容器中创建一个 *whiteout* 文件 `upperdir`)。图像层中的文件版本 `lowerdir` 不会被删除(因为 `lowerdir` 是只读的。但是whiteout 文件会阻止它对容器可用。
- 在容器中删除*目录*时,将在容器内创建一个不透明目录 `upperdir`)。这与 whiteout 文件的工作方式相同,可以有效地防止访问该目录,即使它仍然存在于映像中 `lowerdir`)。
#### 重命名目录
只有当源路径和目标路径都位于顶层时,才允许为目录调用 `rename2`。否则,它将返回 `EXDEV` 错误“cross-device link not permitted”。您的应用程序需要设计为处理 `EXDEV` 并回退到“复制和取消链接”策略。
## OverlayFS 和 Docker 性能
`overlay2` 的性能可能比 `Btrfs` 好。但是,请注意以下详细信息:
### 页面缓存
OverlayFS 支持页面缓存共享。访问同一文件的多个容器共享该文件的单个页面缓存条目。这会使 `overlay2` 驱动程序对内存高效,是高密度用例的不错选择,例如 作为 PaaS。
### 复制
与其他写入时复制文件系统一样每当容器首次写入文件时OverlayFS 都会执行复制操作。这可能会增加写入操作的延迟,尤其是对于大型文件。但是,一旦文件被复制,对该文件的所有后续写入都将在上层进行,而无需进一步的复制操作。
### 性能最佳实践
以下通用性能最佳实践适用于 OverlayFS。
#### 使用快速存储
固态驱动器 SSD 的读取和写入速度比旋转磁盘更快。
#### 将卷用于写入密集型工作负载
卷为写入密集型工作负载提供最佳且最可预测的性能。这是因为它们绕过了存储驱动程序,并且不会产生精简配置和写入时复制引入的任何潜在开销。卷还有其他好处,例如允许您在容器之间共享数据,以及即使没有正在运行的容器正在使用数据也可以保留数据。
## OverlayFS 兼容性限制
总结 OverlayFS 与其他文件系统不兼容的方面:
### [`open(2)`](https://linux.die.net/man/2/open)
OverlayFS 仅实现 POSIX 标准的子集。 这可能会导致某些 OverlayFS 操作违反 POSIX 标准。一 此类操作称为复制操作。假设您的应用程序调用 `fd1=open“foo” O_RDONLY`,然后是 `fd2=open“foo” O_RDWR`。在这种情况下,您的应用程序希望 `fd1``fd2` 引用同一个文件。但是,由于在第二次调用 `open2` 之后发生了复制操作,因此 Descriptors 引用不同的文件。`fd1` 继续引用映像中的文件 `lowerdir`),而 fd2 引用容器中的文件 `upperdir`)。解决方法是触摸文件,这会导致发生复制操作。所有后续的 `open2` 操作,无论只读还是读写访问模式,都会引用容器中的文件 `upperdir`)。
除非安装了 `yum-plugin-ovl` 软件包,否则已知 `yum` 会受到影响。如果您的发行版(例如 6.8 或 7.2 之前的 RHEL/CentOS中没有 `yum-plugin-ovl` 软件包,您可能需要执行 `touch /var/lib/rpm/*` 在运行 `yum install` 之前。此软件包实现了上面提到的 `yum` 的`touch`(触摸)解决方法。
### [`rename(2)`](https://linux.die.net/man/2/rename)
OverlayFS 不完全支持 `rename2` 系统调用。您的应用程序需要检测其故障并回退到“复制和取消链接”策略。
[rootless-doc]:https://docs.docker.com/engine/security/rootless/