ZFS 存储驱动程序

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

它由 Sun Microsystems(现为 Oracle Corporation)创建,并且是开源的 在 CDDL 许可证下。由于 CDDL 和 GPL 中,ZFS 不能作为主线 Linux 内核的一部分提供。但是, ZFS On Linux (ZoL) 项目提供树外内核模块和用户空间 工具,这些工具可以单独安装。

ZFS on Linux (ZoL) 端口运行状况良好且日趋成熟。然而,在这一点上 time 不建议使用zfs用于生产的 Docker 存储驱动程序 除非您在 Linux 上具有丰富的 ZFS 使用经验,否则请使用。

注意

Linux 平台上还有一个 ZFS 的 FUSE 实现。不建议这样做。本机 ZFS 驱动程序 (ZoL) 经过了更多的测试,具有更好的性能,并且使用范围更广。本文档的其余部分涉及本机 ZoL 端口。

先决条件

  • ZFS 需要一个或多个专用块设备,最好是固态设备 驱动器 (SSD)。
  • /var/lib/docker/目录必须挂载在 ZFS 格式的 文件系统。
  • 更改存储驱动程序会生成您已经拥有的任何容器 在本地系统上创建无法访问。用docker save要保存容器, 并将现有镜像推送到 Docker Hub 或私有存储库,以便您 无需稍后重新创建它们。

注意

无需使用MountFlags=slave因为dockerdcontainerd位于不同的挂载命名空间中。

使用zfs存储驱动程序

  1. 停止 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并设置storage-driverzfs.如果文件之前为空,则它现在应该看起来 喜欢这个:

    {
      "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
    <...>
    

管理zfs

增加正在运行的设备的容量

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

$ sudo zpool add zpool-docker /dev/xvdh

限制容器的可写存储配额

如果要按镜像/数据集实施配额,则可以设置sizestorage 选项来限制单个容器可以使用的空间量 用于其可写层。

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

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

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

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

如何zfs存储驱动程序工作

ZFS 使用以下对象:

  • 文件系统:精简配置,从zpool上 需求。
  • 快照:文件系统的只读空间高效时间点副本。
  • clones:快照的读写副本。用于存储差异 从上一层。

创建克隆的过程:

ZFS snapshots and clones
  1. 从文件系统创建只读快照。
  2. 将从快照创建可写克隆。这包含任何差异 从父图层。

文件系统、快照和克隆都从底层zpool.

磁盘上的镜像和容器层

每个正在运行的容器的统一文件系统都挂载在/var/lib/docker/zfs/graph/.继续阅读以了解 统一文件系统。

镜像分层和共享

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

下图显示了如何将其与正在运行的基于 在两层镜像上。

ZFS pool for Docker container

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

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

  2. 其他镜像图层是托管镜像图层的数据集的克隆 就在它的正下方。

    在图中,通过拍摄基础的 ZFS 快照来添加 “Layer 1” 层,然后从该快照创建克隆。克隆是可写的,并且 按需占用 zpool 中的空间。快照是只读的, 将 Base Layer 维护为不可变对象。

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

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

  4. 当容器修改其可写层的内容时,space 为 分配给已更改的块。默认情况下,这些块是 128 千。

容器读取和写入的工作原理zfs

读取文件

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

ZFS block sharing

写入文件

编写新文件:按需从底层分配空间zpool并且这些块直接写入容器的可写层。

修改现有文件:仅为更改的块分配空间, 这些块使用 写入时复制 (CoW) 策略。这会最小化层的大小并增加 写入性能。

删除文件或目录

  • 删除位于较低层中的文件或目录时,ZFS driver 会掩盖容器的 writable 层,即使文件或目录仍然存在于较低的 只读层。
  • 如果您在容器的 writable 层,则块会被zpool.

ZFS 和 Docker 性能

有几个因素会影响 Docker 的性能,使用zfsstorage 驱动程序。

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

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

  • ZFS 缓存:ZFS 将磁盘块缓存在称为 自适应替换缓存 (adaptive replacement cache, ARC)。ZFS 的 Single Copy ARC 功能允许 块的单个缓存副本,将由 借助此功能,多个正在运行的容器可以共享 cached 块。此功能使 ZFS 成为 PaaS 和其他 高密度使用案例。

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

  • 使用适用于 Linux 的本机 ZFS 驱动程序:ZFS FUSE 实现不是 推荐,因为性能不佳。

性能最佳实践

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

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