使用ZFS存储驱动程序

预计阅读时间:9分钟

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

它由Sun Microsystems(现为Oracle Corporation)创建,并以CDDL许可证开源。由于CDDL和GPL之间的许可不兼容,因此ZFS不能作为主线Linux内核的一部分提供。但是,Linux上的ZFS(ZoL)项目提供了树外内核模块和用户空间工具,可以分别安装它们。

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

注意:Linux平台上还有ZFS的FUSE实现。不建议这样做。本地ZFS驱动程序(ZoL)经过了更多测试,性能更高且得到了更广泛的使用。本文档的其余部分指的是本机ZoL端口。

先决条件

  • ZFS需要一个或多个专用块设备,最好是固态驱动器(SSD)。
  • ZFS仅支持泊坞窗引擎-社区与Ubuntu 14.04或更高版本,与zfs 包(16.04及更高版本)或zfs-nativeubuntu-zfs已安装的软件包(14.04)。
  • Docker EE或CS-Engine或任何其他Linux平台不支持ZFS。
  • /var/lib/docker/目录必须安装在ZFS格式的文件系统上。
  • 更改存储驱动程序将使您已经创建的所有容器在本地系统上均不可访问。使用docker save保存的容器,并推动现有图像多克尔集线器或私人仓库,这样就不需要再后来创建它们。

笔记

无需MountFlags=slave与Docker Engine 18.09或更高版本一起使用,因为dockerdcontainerd处于不同的安装命名空间中。

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

  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

限制容器的可写存储配额

如果要基于每个图像/数据集实施配额,则可以设置 size存储选项以限制单个容器可用于其可写层的空间量。

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

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

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

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

zfs存储驱动程序如何工作

ZFS使用以下对象:

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

创建克隆的过程:

zfs快照和克隆

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

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

磁盘上的图像和容器层

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

图像分层和共享

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

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

Docker容器的zfs池

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

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

  2. 附加图像层是数据集的克隆,这些数据集承载着位于其正下方的图像层。

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

  3. 启动容器后,可写层将添加到图像上方。

    在该图中,通过对映像顶层(第1层)进行快照并从该快照创建克隆来创建容器的读写层。

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

容器如何读写 zfs

读取文件

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

zfs块共享

写文件

编写一个新文件:根据需要从底层分配空间,zpool 并将这些块直接写入容器的可写层。

修改现有文件:仅为更改的块分配空间,然后使用写时复制(CoW)策略将这些块写入容器的可写层。这样可以最小化层的大小并提高写入性能。

删除文件或目录

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

ZFS和Docker性能

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

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

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

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

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

  • 对Linux使用本地ZFS驱动程序:由于性能不佳,不建议使用ZFS FUSE实现。

绩效最佳实践

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

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

容器存储驱动程序ZFS