Docker用户指南(5) – AUFS存储驱动实践

AUFS是Docker使用的第一个存储驱动。因此,它与Docker有着悠久而密切的历史,AUFS非常稳定,部署在大量真实的生产环境中,并且拥有强大的社区支持。AUFS有几个特性使其成为Docker不错的选择。
这些特性是:

  • 快速启动容器
  • 高效存储使用
  • 高效内存使用
  • 尽管它功能强大和与Docker的悠久历史,一些Linux发行版本已经不支持AUFS。这个通常是因为AUFS不在Linux内核主线。
    下面开始检查AUFS的一些功能以及它们与Docker的关系。

    AUFS的镜像分层与共享

    AUFS是一个统一文件系统。意思是它管理着单台Linux主机上的多个目录,把它们互相堆叠并提供一个统一视图。AUFS使用了联合挂载(union mount)。
    AUFS堆叠多个目录并通过一个联合挂载来提供一个统一的视图。堆栈中的所有目录以及联合挂载点必须都存在于同一Linux主机上。AUFS引用作为分支堆叠的每个目录。
    在Docker中,AUFS联合挂载支持镜像分层。AUFS存储驱动使用联合挂载系统来实现Docker的镜像分层。AUFS分支对应Docker镜像数据层。下图显示基于ubuntu:latest镜像的docker容器。

    图中显示的每一个镜像数据层和容器数据层,表示为Docker主机上/var/lib/docker路径下的一个目录。联合挂载点提供了所有数据层的统一视图。从Docker 1.10开始,镜像数据层ID不再与包含它们数据的目录的名称相关。
    AUFS也支持不是所有存储驱动都支持的写时拷贝(CoW)技术。

    AUFS读和写

    Docker利用AUFS CoW技术实现镜像共享并最小化磁盘空间的使用。AUFS操作在文件级别上。意味着AUFS CoW技术将复制整个文件 – 即使是只更改一个文件的一小部分。此行为对容器性能有显着影响,特别是要复制的文件非常大,文件在层级很多的数据层下或者CoW操作必须搜索深度目录树。
    例如,运行在容器中的一个程序需要添加一个新的值到一个大的键值存储文件。如果是首次更改这个文件,它还没有在容器的顶部可写数据层。那么CoW必须从底层镜像复制出这个文件。AUFS存储驱动在每个镜像数据层搜索这个文件。搜索的顺序是从上到下。当找到后,复制整个文件到容器的顶部可写数据层。之后才能打开和修改这个文件了。
    大的文件复制需要的时间明显比小的文件要多,存在于较低数据层的文件比在较高数据层的文件所需时间多。不过,在任何一个容器中的文件只会产生一次copy-up操作。随后的这个文件的读和写都发生在容器的顶部数据层。

    AUFS删除文件

    AUFS存储驱动通过在容器数据层放置一个空白(whiteout)文件来从容器删除一个文件。空白文件有效地掩盖了下面的只读镜像层中文件的存在。下图显示了基于3个数据层的镜像的容器。

    file3已经从容器中删除。因此AUFS存储驱动放置一个空白文件到容器数据层。这个空白文件通过隐藏镜像只读数据层中的任意原始文件的存在来有效地“删除”了容器的file3文件。

    AUFS重命名文件

    在AUFS中调用rename(2)函数来重命名目录没有被完全支持。它返回EXDEV(“cross-device link not permitted”),即使源文件和目标文件在同一个AUFS数据层,除非目录没有子级。
    所以你的程序应该设计能处理EXDEV并回退到“复制再删除”策略。

    配置Docker使用AUFS

    你可以在安装有AUFS的Linux系统上只使用AUFS存储驱动。使用以下的命令为判断你的系统是否支持AUFS。

    1. $ grep aufs /proc/filesystems
    2.  
    3. nodev   aufs

    输出表明系统支持AUFS。一旦你验证系统支持AUFS后,你必须设置Docker daemon使用它。使用dockerd命令启用AUFS的方法:

    1. $ sudo dockerd --storage-driver=aufs &

    或者你可以编辑Docker配置文件在DOCKER_OPTS行添加–storage-driver=aufs:

    1. # Use DOCKER_OPTS to modify the daemon startup options.
    2. DOCKER_OPTS="--storage-driver=aufs"

    daemon运行后,使用docker info为验证存储驱动。

    1. $ sudo docker info
    2.  
    3. Containers: 1
    4. Images: 4
    5. Storage Driver: aufs
    6.  Root Dir: /var/lib/docker/aufs
    7.  Backing Filesystem: extfs
    8.  Dirs: 6
    9.  Dirperm1 Supported: false
    10. Execution Driver: native-0.2
    11. ...output truncated...

    本地存储与AUFS

    当dockerd使用AUFS驱动运行,驱动把镜像和容器存储到docker主机本地存储区域的/var/lib/docker/aufs/目录下。

    镜像

    镜像数据层及它们的数据存储在/var/lib/docker/aufs/diff/目录下。从docker 1.10起,镜像数据层ID不再与目录名称相关。
    /var/lib/docker/aufs/layers/目录包含镜像数据层如何堆叠的元数据。每一个镜像数据层或容器数据层都可以在这个目录下找到对应的一个文件(虽然文件名称不再与镜像数据层ID匹配)。每个文件包含该文件对应的数据层之下的数据层的目录名称列表。
    以下命令显示了/var/lib/docker/aufs/layers/中的元数据文件的内容,列出了在union mount中堆叠在其下面的三个目录。记住,从Docker 1.10起这些目录名没有映射到镜像层ID。

    1. $ cat /var/lib/docker/aufs/layers/91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c
    2.  
    3. d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82
    4. c22013c8472965aa5b62559f2b540cd440716ef149756e7b958a1b2aba421e87
    5. d3a1f33e8a5a513092f01bb7eb1c2abf4d711e5105390a3fe1ae2248cfde1391

    一个镜像最底层的数据层之下已经没有数据层,所以它的文件内容应该是空的。

    容器

    运行的容器挂载在/var/lib/docker/aufs/mnt/下。这个是AUFS联合挂载点所在的位置,它为容器和镜像数据层提供了一个统一的视图。如果容器没有运行,目录仍然存在但是空的。这是因为AUFS只挂载运行的容器。从Docker 1.10起,容器ID不再与/var/lib/docker/aufs/mnt/下的目录名相关。
    容器元数据和各种配置文件放置在/var/lib/docker/containers/目录。所有的容器的这个目录都存在系统上,包括已经停止的容器。运行的容器的日志文件也在这个目录。
    容器的可写数据层存储在/var/lib/docker/aufs/diff/目录。即使容器停止后,数据层目录仍然存在。意味着重启容器不会丢失容器数据。不过一旦删除容器,相应的数据层也会被删除。

    AUFS和Docker性能

    总结一些已经提到的性能相关方面:

  • AUFS存储驱动是对于容器性能很看重的PaaS和其他类似用例的理想选择。这个因为AUFS能有效的在不同运行容器共享镜像,快速启动容器以及占用最少的硬盘空间。
  • AUFS在镜像数据层和容器之间共享文件的基本机制,使得能高效利用系统页面缓存达到快速启动容器的目的。
  • AUFS可能会影响容器的写入性能。这是因为对容器文件的首次更改,必须先定位并复制文件到容器顶部可写数据层后才真正开始更新文件。特别是文件在许多数据层之下和文件非常大的情况下,性能影响会更大。
  • 最后一点,Data volumes能提供最佳和可预测的性能。因为文件的操作会绕过存储驱动,这样就不会由于写时拷贝引入潜在的性能开销。

    AUFS兼容性

    总结与其他文件系统不兼容的AUFS的方面:

  • AUFS不完全支持rename(2)系统调用。 你的应用程序需要检测其故障,并回退到“复制并删除文件”策略。
  • 标签:Docker 发布于:2019-11-20 04:25:29