ZFS 驱动


ZFS 是下一代文件系统,支持许多先进的存储技术,例如卷管理、快照、校验和、压缩和重复数据删除、复制等。

由 Sun Microsystems(现为 Oracle Corporation)创建,并在 CDDL 许可下开源。 由于 CDDL 和 GPL 之间的许可不兼容,ZFS 无法作为主线 Linux 内核的一部分提供。 但是,ZFS On Linux (ZoL) 项目提供了可以单独安装的 out-of-tree 内核模块和用户空间工具。

Linux 上的 ZFS (ZoL) 端口健康且成熟。 但是,目前不建议将zfs Docker 存储驱动程序用于生产用途,除非您对 Linux 上的 ZFS 有丰富的经验。

注意:Linux 平台上还有 ZFS 的 FUSE 实现。不建议这样做。本机 ZFS 驱动程序 (ZoL) 经过更多测试,性能更高,并且使用更广泛。本文档的其余部分涉及本机 ZoL 端口。
  • ZFS 需要一个或多个专用块设备,最好是固态驱动器 (SSD)。
  • /var/lib/docker/目录必须安装在 ZFS 格式的文件系统上。
  • 更改存储驱动程序会使您已创建的任何容器在本地系统上无法访问。 使用docker save保存容器,并将现有镜像推送到 Docker Hub 或私有存储库,这样您以后就不需要重新创建它们。
注意:不需要使用MountFlags=slave,因为 dockerd 和 containerd 位于不同的挂载命名空间中。
  1. stop Docker

    $ sudo systemctl stop docker
    

  2. /var/lib/docker/的内容复制到/var/lib/docker.bk并删除/var/lib/docker/的内容。

    $ sudo cp -au /var/lib/docker /var/lib/docker.bk
    
    $ sudo rm -rf /var/lib/docker/*
    

  3. 在一个或多个专用块设备上创建一个新的zpool,并将其挂载到/var/lib/docker/中。 确保您指定了正确的设备,因为这是破坏性操作。 此示例将两个设备添加到池中。

    $ sudo zpool create -f zpool-docker -m /var/lib/docker /dev/xvdf /dev/xvdg
    
    该命令创建zpool并将其命名为zpool-docker。 该名称仅用于显示目的,您可以使用其他名称。 使用zfs list检查池是否已正确创建和安装。
    $ sudo zfs list
    
    NAME           USED  AVAIL  REFER  MOUNTPOINT
    zpool-docker    55K  96.4G    19K  /var/lib/docker
    

  4. 配置 Docker 以使用zfs。 编辑/etc/docker/daemon.json并将存储驱动程序设置为zfs。 如果文件之前是空的,现在应该如下所示:

    {
      "storage-driver": "zfs"
    }
    
    保存并关闭文件。

  5. 启动 Docker。 使用docker info验证存储驱动程序是否为zfs

    $ sudo docker info
      Containers: 0
       Running: 0
       Paused: 0
       Stopped: 0
      Images: 0
      Server Version: 17.03.1-ce
      Storage Driver: zfs
       Zpool: zpool-docker
       Zpool Health: ONLINE
       Parent Dataset: zpool-docker
       Space Used By Parent: 249856
       Space Available: 103498395648
       Parent Quota: no
       Compression: off
      ...
    

要增加zpool的大小,需要向 Docker 主机添加专用的块设备,然后使用zpool add命令将其添加到zpool

$ sudo zpool add zpool-docker /dev/xvdh

如果您想在每个镜像/数据集的基础上实施配额,您可以设置大小存储选项来限制单个容器可用于其可写层的空间量。

编辑/etc/docker/daemon.json并添加以下内容:

{
  "storage-driver": "zfs",
  "storage-opts": ["size=256M"]
}

请参阅 守护程序参考文档 中每个存储驱动程序的所有存储选项

保存并关闭文件,然后重新启动 Docker。

ZFS 使用以下对象:

  • 文件系统:精简配置,按需从zpool分配空间。
  • 快照:文件系统的只读、节省空间的时间点副本。
  • 克隆:快照的读写副本。 用于存储与上一层的差异。

创建克隆的过程:

zfs-clone

  1. 从文件系统创建只读快照。
  2. 从快照创建可写克隆。 这包含与父层的任何差异。

文件系统、快照和克隆都从底层zpool分配空间。

每个正在运行的容器的统一文件系统都安装在/var/lib/docker/zfs/graph/中的安装点上。 继续阅读有关统一文件系统如何组成的说明。

镜像的基础层是 ZFS 文件系统。 每个子层都是基于其下层的 ZFS 快照的 ZFS 克隆。容器是基于其所创建的镜像顶层的 ZFS 快照的 ZFS 克隆。

下图显示了如何将其与基于两层镜像的正在运行的容器组合在一起。

zfs-zpool

当您启动容器时,将按顺序发生以下步骤:

  1. 镜像的基础层作为 ZFS 文件系统存在于 Docker 主机上。

  2. 附加镜像层是直接在其下方托管镜像层的数据集的克隆。

  3. 在图中,通过获取基础层的 ZFS 快照然后从该快照创建克隆来添加 第 1 层。克隆是可写的,并按需消耗zpool的空间。快照是只读的,将基础层维护为不可变对象。

  4. 当容器启动时,会在镜像上方添加一个可写层。

    在图中,容器的读写层是通过制作镜像顶层(第 1 层)的快照并从该快照创建克隆来创建的。

  5. 当容器修改其可写层的内容时,将为更改的块分配空间。 默认情况下,这些块的大小为 128k。

每个容器的可写层都是一个 ZFS 克隆,它与创建它的数据集(其父层的快照)共享其所有数据。 即使读取的数据来自深层,读取操作也很快。 下图说明了块共享的工作原理:

zpool-blocks

  • 写入新文件:从底层zpool按需分配空间,并将块直接写入容器的可写层。

  • 修改现有文件:仅为更改的块分配空间,并使用写时复制(CoW)策略将这些块写入容器的可写层。 这最大限度地减少了层的大小并提高了写入性能。

  • 删除文件或目录

    • 当您删除较低层中存在的文件或目录时,ZFS 驱动程序会屏蔽该文件或目录在容器的可写层中的存在,即使该文件或目录仍然存在于较低的只读层中。
    • 如果您在容器的可写层中创建然后删除文件或目录,则zpool会回收这些块。

有几个因素会影响使用zfs存储驱动程序的 Docker 的性能。

  • 内存:内存对 ZFS 性能有重大影响。 ZFS 最初是为具有大量内存的大型企业级服务器设计的。

  • ZFS 功能:ZFS 包括重复数据删除功能。使用此功能可能会节省磁盘空间,但会占用大量内存。建议您为与 Docker 一起使用的zpool禁用此功能,除非您使用 SAN、NAS 或其他硬件 RAID 技术。

  • ZFS 缓存:ZFS 在称为自适应替换缓存 (ARC) 的内存结构中缓存磁盘块。 ZFS 的单副本 ARC 功能允许块的单个缓存副本由块的多个克隆共享。 通过此功能,多个正在运行的容器可以共享缓存块的单个副本。 此功能使 ZFS 成为 PaaS 和其他高密度用例的良好选择。

  • 碎片:碎片是 ZFS 等写时复制文件系统的自然副产品。 ZFS 通过使用 128k 的小块大小来缓解这一问题。 ZFS 意图日志 (ZIL) 和写入合并(延迟写入)也有助于减少碎片。 您可以使用zpool status监控碎片。 但是,如果不重新格式化和恢复文件系统,就无法对 ZFS 进行碎片整理。

  • 使用适用于 Linux 的本机 ZFS 驱动程序:由于性能较差,不建议使用 ZFS FUSE 实现。

  • 使用快速存储:固态驱动器 (SSD) 提供比旋转磁盘更快的读取和写入速度。

  • 将卷用于写入密集型工作负载:卷为写入密集型工作负载提供最佳且最可预测的性能。 这是因为它们绕过存储驱动程序,并且不会产生精简配置和写时复制带来的任何潜在开销。 卷还有其他好处,例如允许您在容器之间共享数据,甚至在没有正在运行的容器使用它们时也能保留数据。