使用 AUFS 存储驱动程序

已弃用

AUFS 存储驱动程序已弃用,并将在 Docker Engine v24.0 中移除。如果您正在使用 AufS,您必须在升级到 Docker Engine v24.0 之前迁移到支持的存储驱动程序。阅读 Docker 存储驱动程序 页面以了解支持的存储驱动程序。

AUFS 是一个联合文件系统aufs 存储驱动程序以前是用于管理 Docker for Ubuntu 上的映像和层的默认存储驱动程序,以及 Stretch 之前的 Debian 版本。如果您的 Linux 内核版本为 4.0 或更高,并且您使用 Docker Engine - Community,请考虑使用更新的 overlay2,它可能比 aufs 存储驱动程序具有更高的性能优势。

先决条件

  • 对于 Docker Engine - Community,AUFS 在 Ubuntu 和 Stretch 之前的 Debian 版本上受支持。
  • 对于 Docker EE,AUFS 在 Ubuntu 上受支持。
  • 如果您使用 Ubuntu,则需要将 AUFS 模块添加到内核。如果您没有安装这些软件包,则需要使用 overlay2
  • AUFS 无法使用以下作为后端的文件系统:aufsbtrfsecryptfs。这意味着包含 /var/lib/docker/aufs 的文件系统不能是这些文件系统类型之一。

使用 aufs 存储驱动程序配置 Docker

如果 AUFS 驱动程序在您启动 Docker 时加载到内核中,并且没有配置其他存储驱动程序,则 Docker 默认使用它。

  1. 使用以下命令验证您的内核是否支持 AUFS。

    $ grep aufs /proc/filesystems
    
    nodev   aufs
    
  2. 检查 Docker 使用的存储驱动程序。

    $ docker info
    
    <truncated output>
    Storage Driver: aufs
     Root Dir: /var/lib/docker/aufs
     Backing Filesystem: extfs
     Dirs: 0
     Dirperm1 Supported: true
    <truncated output>
    
  3. 如果您使用的是不同的存储驱动程序,那么 AUFS 要么未包含在内核中(在这种情况下,使用不同的默认驱动程序),要么 Docker 已明确配置为使用不同的驱动程序。检查 /etc/docker/daemon.jsonps auxw | grep dockerd 的输出,以查看 Docker 是否使用 --storage-driver 标志启动。

aufs 存储驱动程序的工作原理

AUFS 是一个联合文件系统,这意味着它将单个 Linux 主机上的多个目录叠加在一起,并将其呈现为单个目录。在 AUFS 术语中,这些目录称为分支,在 Docker 术语中称为

统一过程称为联合挂载

下图显示了一个基于 ubuntu:latest 映像的 Docker 容器。

Layers of an Ubuntu container

每个映像层和容器层在 Docker 主机上都以 /var/lib/docker/ 中的子目录形式表示。联合挂载提供了所有层的统一视图。目录名称并不直接对应于层的 ID 本身。

AUFS 使用写时复制 (CoW) 策略来最大限度地提高存储效率并最大限度地减少开销。

示例:磁盘上的映像和容器结构

以下 docker pull 命令显示了一个 Docker 主机下载包含五个层的 Docker 映像。

$ docker pull ubuntu

Using default tag: latest
latest: Pulling from library/ubuntu
b6f892c0043b: Pull complete
55010f332b04: Pull complete
2955fb827c94: Pull complete
3deef3fcbd30: Pull complete
cf9722e506aa: Pull complete
Digest: sha256:382452f82a8bbd34443b2c727650af46aced0f94a44463c62a9848133ecb1aa8
Status: Downloaded newer image for ubuntu:latest

映像层

警告:不要直接操作 /var/lib/docker/ 中的任何文件或目录。这些文件和目录由 Docker 管理。

有关映像和容器层的所有信息都存储在 /var/lib/docker/aufs/ 的子目录中。

  • diff/:每层的内容,每个都存储在单独的子目录中
  • layers/:有关映像层堆叠方式的元数据。此目录包含 Docker 主机上每个映像或容器层的一个文件。每个文件都包含其下方堆栈中所有层的 ID(其父层)。
  • mnt/:挂载点,每个映像或容器层一个,用于组装和挂载容器的统一文件系统。对于只读的映像,这些目录始终为空。

容器层

如果容器正在运行,则 /var/lib/docker/aufs/ 的内容会以下列方式更改

  • diff/:可写容器层中引入的差异,例如新文件或修改后的文件。
  • layers/:有关可写容器层父层的元数据。
  • mnt/:每个正在运行的容器的统一文件系统的挂载点,与容器内部看到的一模一样。

aufs 如何处理容器读写操作

读取文件

考虑三个场景,其中容器使用 aufs 以读访问方式打开文件。

  • 文件不存在于容器层:如果容器以读访问方式打开一个文件,并且该文件尚不存在于容器层中,则存储驱动程序会在映像层中搜索该文件,从容器层正下方的层开始搜索。它会从找到该文件的层中读取。

  • 文件仅存在于容器层:如果容器以读访问方式打开一个文件,并且该文件存在于容器层中,则会从那里读取该文件。

  • 文件同时存在于容器层和映像层:如果容器以读访问方式打开一个文件,并且该文件存在于容器层和一个或多个映像层中,则会从容器层读取该文件。容器层中的文件会遮蔽映像层中具有相同名称的文件。

修改文件或目录

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

  • 首次写入文件:容器首次写入现有文件时,该文件不存在于容器中 (upperdir)。aufs 驱动程序执行copy_up 操作,将文件从它所在的映像层复制到可写容器层。然后,容器将更改写入容器层中该文件的新副本。

    但是,AUFS 针对的是文件级别,而不是块级别。这意味着所有 copy_up 操作都会复制整个文件,即使文件非常大,并且只有一小部分被修改。这会对容器写入性能产生明显的影响。当在包含多个层的映像中搜索文件时,AUFS 会遇到明显的延迟。但是,值得注意的是,copy_up 操作仅在首次写入给定文件时才会发生。后续写入同一文件会针对已复制到容器的该文件副本进行操作。

  • 删除文件和目录:

    • 当在容器中删除一个文件时,会在容器层中创建一个白出文件。映像层中的文件版本不会被删除(因为映像层是只读的)。但是,白出文件会阻止容器访问它。

    • 当在容器中删除一个目录时,会在容器层中创建一个不透明文件。这与白出文件的操作方式相同,并且有效地阻止了对该目录的访问,即使它仍然存在于映像层中。

  • 重命名目录:在 AUFS 上,调用 rename(2) 来重命名目录不受完全支持。它会返回 EXDEV(“不允许跨设备链接”),即使源路径和目标路径都位于同一 AUFS 层上,除非该目录没有任何子项。您的应用程序需要设计为处理 EXDEV 并回退到“复制和取消链接”策略。

AUFS 和 Docker 性能

总结上面提到的某些与性能相关的方面

  • AUFS 存储驱动程序的性能不如 overlay2 驱动程序,但对于容器密度很重要的 PaaS 和其他类似用例来说,它是一个不错的选择。这是因为 AUFS 可以有效地在多个正在运行的容器之间共享映像,从而实现快速容器启动时间和最少的磁盘空间使用。

  • AUFS 利用页缓存高效地实现镜像层和容器之间文件共享的底层机制。

  • AUFS 存储驱动可能会导致容器写入性能出现显著延迟。这是因为容器第一次写入任何文件时,需要先定位该文件并将其复制到容器的顶层可写层。当这些文件位于多个镜像层之下,且文件本身很大时,延迟会增加并累积。

性能最佳实践

以下通用性能最佳实践也适用于 AUFS。

  • **固态硬盘 (SSD)** 比旋转磁盘提供更快的读写速度。

  • **对于写密集型工作负载,使用卷:** 卷为写密集型工作负载提供最佳且最可预测的性能。这是因为它们绕过了存储驱动程序,不会产生由稀疏配置和写时复制引入的任何潜在开销。卷还有其他好处,例如允许您在容器之间共享数据,即使没有运行的容器使用它们也能持久存在。