ZFS存储驱动
ZFS 是一个支持许多高级存储技术的下一代文件系统,例如卷管理、快照、校验和、压缩和去重、复制等。
它由Sun Microsystems(现为Oracle Corporation)创建,并在CDDL许可证下开源。由于CDDL和GPL之间的许可证不兼容,ZFS不能作为主线Linux内核的一部分发布。然而,ZFS On Linux(ZoL)项目提供了一个树外内核模块和用户空间工具,可以单独安装。
Linux上的ZFS(ZoL)移植是健康且成熟的。然而,目前不建议在生产环境中使用zfs Docker存储驱动程序,除非您对Linux上的ZFS有丰富的经验。
注意
在Linux平台上也有ZFS的FUSE实现。这不推荐使用。原生的ZFS驱动(ZoL)经过了更多的测试,性能更好,使用也更广泛。本文档的其余部分指的是原生的ZoL端口。
先决条件
- ZFS 需要一个或多个专用的块设备,最好是固态硬盘(SSDs)。
/var/lib/docker/目录必须挂载在 ZFS 格式的文件系统上。- 更改存储驱动程序会使您已在本地系统上创建的任何容器无法访问。使用
docker save保存容器,并将现有镜像推送到Docker Hub或私有仓库,以便您以后无需重新创建它们。
注意
不需要使用
MountFlags=slave,因为dockerd和containerd位于不同的挂载命名空间中。
使用zfs存储驱动配置Docker
停止Docker。
将
/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/*在您的专用块设备或设备上创建一个新的
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配置 Docker 使用
zfs。编辑/etc/docker/daemon.json并将storage-driver设置为zfs。如果文件之前是空的,现在应该看起来 像这样:{ "storage-driver": "zfs" }保存并关闭文件。
启动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 add命令将其添加到zpool中:
$ sudo zpool add zpool-docker /dev/xvdh
限制容器的可写存储配额
如果你想在每个镜像/数据集的基础上实施配额,你可以设置size存储选项来限制单个容器可写层可以使用的空间量。
编辑 /etc/docker/daemon.json 并添加以下内容:
{
"storage-driver": "zfs",
"storage-opts": ["size=256M"]
}查看每个存储驱动程序的所有存储选项,请参阅 守护程序参考文档
保存并关闭文件,然后重新启动Docker。
zfs 存储驱动的工作原理
ZFS 使用以下对象:
- 文件系统: 精简配置,空间按需从
zpool分配。 - snapshots: 文件系统的只读空间高效的时间点副本。
- clones: 快照的读写副本。用于存储与前一层的差异。
创建克隆的过程:

- 从文件系统创建一个只读快照。
- 从快照创建一个可写的克隆。这包含与父层的任何差异。
文件系统、快照和克隆都从底层的zpool分配空间。
磁盘上的镜像和容器层
每个正在运行的容器的统一文件系统都挂载在/var/lib/docker/zfs/graph/中的一个挂载点上。继续阅读以了解统一文件系统的组成方式。
图像分层与共享
图像的基础层是一个ZFS文件系统。每个子层都是基于其下层ZFS快照的ZFS克隆。容器是基于其创建自图像顶层的ZFS快照的ZFS克隆。
下图展示了如何基于一个两层镜像的运行容器来组合这些内容。

当你启动一个容器时,以下步骤会按顺序发生:
镜像的基础层在Docker主机上以ZFS文件系统的形式存在。
额外的图像层是直接位于其下方的图像层数据集的克隆。
在图中,“Layer 1”是通过对基础层进行ZFS快照,然后从该快照创建克隆来添加的。克隆是可写的,并且按需从zpool中消耗空间。快照是只读的,保持基础层作为不可变对象。
当容器启动时,会在镜像之上添加一个可写层。
在图中,容器的读写层是通过对镜像的顶层(第1层)进行快照并从该快照创建克隆来创建的。
当容器修改其可写层的内容时,会为更改的块分配空间。默认情况下,这些块的大小为128k。
容器读写如何与zfs一起工作
读取文件
每个容器的可写层是一个ZFS克隆,它与创建它的数据集(其父层的快照)共享所有数据。即使读取的数据来自深层,读取操作也很快。此图说明了块共享的工作原理:

写入文件
写入新文件:空间是根据底层zpool的需求分配的,并且块直接写入容器的可写层。
修改现有文件:仅为更改的块分配空间,并且这些块使用写时复制(CoW)策略写入容器的可写层。这最小化了层的大小并提高了写入性能。
删除文件或目录:
- 当你删除存在于较低层的文件或目录时,ZFS驱动程序会掩盖该文件或目录在容器的可写层中的存在,即使该文件或目录仍然存在于较低的只读层中。
- 如果您在容器的可写层中创建然后删除文件或目录,这些块将由
zpool回收。
ZFS 和 Docker 性能
有几个因素会影响使用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实现,因为性能较差。
性能最佳实践
使用快速存储: 固态硬盘(SSDs)比旋转磁盘提供更快的读取和写入速度。
为写密集型工作负载使用卷:卷为写密集型工作负载提供了最佳和最可预测的性能。这是因为它们绕过了存储驱动程序,并且不会因精简配置和写时复制而引入任何潜在的开销。卷还有其他好处,例如允许您在容器之间共享数据,并且即使没有运行的容器使用它们,数据也会持久化。