BTRFS 存储驱动

Btrfs 是一种支持许多高级存储技术的写时复制文件系统,使其非常适合 Docker。Btrfs 包含在主线 Linux 内核中。

Docker的btrfs存储驱动程序利用了许多Btrfs特性来进行镜像和容器管理。这些特性包括块级操作、精简配置、写时复制快照以及易于管理。您可以将多个物理块设备组合成一个Btrfs文件系统。

本页面将Docker的Btrfs存储驱动称为btrfs,并将整个Btrfs文件系统称为Btrfs。

注意

btrfs 存储驱动仅在 SLES、Ubuntu 和 Debian 系统上的 Docker Engine CE 中受支持。

先决条件

btrfs 在满足以下先决条件时受支持:

  • btrfs 仅在 Ubuntu 或 Debian 系统上推荐与 Docker CE 一起使用。

  • 更改存储驱动程序会使您已经创建的容器在本地系统上无法访问。使用docker save保存容器,并将现有镜像推送到Docker Hub或私有仓库,这样您以后就不需要重新创建它们。

  • btrfs 需要一个专用的块存储设备,例如物理磁盘。这个块设备必须格式化为 Btrfs 并挂载到 /var/lib/docker/。下面的配置说明将引导您完成此过程。默认情况下,SLES 的 / 文件系统是使用 Btrfs 格式化的,因此对于 SLES,您不需要使用单独的块设备,但出于性能原因,您可以选择这样做。

  • btrfs 支持必须存在于你的内核中。要检查这一点,请运行以下命令:

    $ grep btrfs /proc/filesystems
    
    btrfs
    
  • 要在操作系统级别管理Btrfs文件系统,您需要btrfs命令。如果您没有这个命令,请安装btrfsprogs包(SLES)或btrfs-tools包(Ubuntu)。

配置 Docker 使用 btrfs 存储驱动

此过程在SLES和Ubuntu上基本相同。

  1. 停止Docker。

  2. /var/lib/docker/的内容复制到备份位置,然后清空/var/lib/docker/的内容:

    $ sudo cp -au /var/lib/docker /var/lib/docker.bk
    $ sudo rm -rf /var/lib/docker/*
    
  3. 将您的专用块设备格式化为Btrfs文件系统。此示例假设您正在使用两个名为/dev/xvdf/dev/xvdg的块设备。请仔细检查块设备名称,因为这是一个破坏性操作。

    $ sudo mkfs.btrfs -f /dev/xvdf /dev/xvdg
    

    Btrfs 还有许多其他选项,包括条带化和 RAID。请参阅 Btrfs 文档

  4. 将新的Btrfs文件系统挂载到/var/lib/docker/挂载点。您可以指定用于创建Btrfs文件系统的任何块设备。

    $ sudo mount -t btrfs /dev/xvdf /var/lib/docker
    

    注意

    通过向/etc/fstab添加条目,使更改在重启后永久生效。

  5. /var/lib/docker.bk的内容复制到/var/lib/docker/

    $ sudo cp -au /var/lib/docker.bk/* /var/lib/docker/
    
  6. 配置Docker以使用btrfs存储驱动。即使/var/lib/docker/现在使用的是Btrfs文件系统,这也是必需的。 编辑或创建文件/etc/docker/daemon.json。如果是一个新文件,添加以下内容。如果是一个现有文件,仅添加键和值,注意如果不是在结束大括号(})之前的最后一行,则要以逗号结束该行。

    {
      "storage-driver": "btrfs"
    }

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

  7. 启动Docker。当它运行时,验证btrfs是否被用作存储驱动程序。

    $ docker info
    
    Containers: 0
     Running: 0
     Paused: 0
     Stopped: 0
    Images: 0
    Server Version: 17.03.1-ce
    Storage Driver: btrfs
     Build Version: Btrfs v4.4
     Library Version: 101
    <...>
    
  8. 当你准备好时,删除 /var/lib/docker.bk 目录。

管理Btrfs卷

Btrfs 的一个好处是可以轻松管理 Btrfs 文件系统,而无需卸载文件系统或重新启动 Docker。

当空间不足时,Btrfs 会自动以大约 1 GB 的块扩展卷。

要将块设备添加到Btrfs卷,请使用btrfs device addbtrfs filesystem balance命令。

$ sudo btrfs device add /dev/svdh /var/lib/docker

$ sudo btrfs filesystem balance /var/lib/docker

注意

虽然你可以在Docker运行时执行这些操作,但性能会受到影响。 最好计划一个停机窗口来平衡Btrfs文件系统。

btrfs 存储驱动的工作原理

btrfs 存储驱动程序的工作方式与其他存储驱动程序不同,因为整个 /var/lib/docker/ 目录都存储在 Btrfs 卷上。

磁盘上的镜像和容器层

关于镜像层和可写容器层的信息存储在 /var/lib/docker/btrfs/subvolumes/。这个子目录包含每个镜像或容器层的一个目录, 其中包含由该层及其所有父层构建的统一文件系统。子卷本身是写时复制的,并且从底层存储池中按需分配空间。 它们也可以嵌套和快照。下图显示了4个子卷。'Subvolume 2'和 'Subvolume 3'是嵌套的,而'Subvolume 4'显示其自己的内部目录树。

Subvolume example

只有图像的基础层被存储为真正的子卷。所有其他层都存储为快照,这些快照仅包含该层引入的差异。您可以创建快照的快照,如下图所示。

Snapshots diagram

在磁盘上,快照看起来和子卷一样,但实际上它们更小且空间效率更高。使用写时复制技术来最大化存储效率并最小化层大小,容器可写层中的写入在块级别进行管理。下图显示了一个子卷及其共享数据的快照。

Snapshot and subvolume sharing data

为了达到最高效率,当容器需要更多空间时,会以大约1 GB的大小进行分配。

Docker的btrfs存储驱动程序将每个镜像层和容器存储在其自己的Btrfs子卷或快照中。镜像的基础层存储为子卷,而子镜像层和容器则存储为快照。如下图所示。

Btrfs container layers

在运行btrfs驱动程序的Docker主机上创建镜像和容器的高级流程如下:

  1. 图像的基础层存储在Btrfs 子卷下,位于/var/lib/docker/btrfs/subvolumes

  2. 后续的图像层作为父层子卷或快照的Btrfs 快照存储,但包含此层引入的更改。这些差异在块级别存储。

  3. 容器的可写层是最终镜像层的Btrfs快照,其中包含了运行容器引入的差异。这些差异在块级别上存储。

容器读写如何与btrfs一起工作

读取文件

容器是镜像的空间高效快照。快照中的元数据指向存储池中的实际数据块。这与子卷相同。因此,对快照执行的读取操作基本上与对子卷执行的读取操作相同。

写入文件

作为一般警告,使用Btrfs写入和更新大量小文件可能会导致性能变慢。

考虑三种场景,其中容器使用Btrfs打开文件进行写访问。

写入新文件

向容器写入新文件会触发按需分配操作,为容器的快照分配新的数据块。然后文件被写入这个新空间。按需分配操作是所有使用Btrfs写入的原生操作,与向子卷写入新数据相同。因此,向容器的快照写入新文件以原生Btrfs速度运行。

修改现有文件

在容器中更新现有文件是一种写时复制操作 (在Btrfs术语中称为写时重定向)。原始数据从 文件当前所在的层读取,只有修改的块被 写入容器的可写层。接着,Btrfs驱动程序更新 快照中的文件系统元数据以指向这些新数据。这种行为 会产生轻微的开销。

删除文件或目录

如果容器删除了存在于较低层中的文件或目录,Btrfs 会屏蔽较低层中该文件或目录的存在。如果容器创建了一个文件然后删除它,这个操作会在 Btrfs 文件系统本身中执行,并且空间会被回收。

Btrfs 和 Docker 性能

有几个因素会影响Docker在btrfs存储驱动下的性能。

注意

通过使用Docker卷来处理写密集型工作负载,而不是依赖在容器的可写层中存储数据,许多这些因素得到了缓解。然而,在Btrfs的情况下,除非/var/lib/docker/volumes/不是由Btrfs支持的,否则Docker卷仍然会遭受这些缺点。

页面缓存

Btrfs 不支持页面缓存共享。这意味着每个访问同一文件的进程都会将文件复制到 Docker 主机的内存中。因此,btrfs 驱动程序可能不是高密度使用场景(如 PaaS)的最佳选择。

小写

执行大量小写操作的容器(这种使用模式与在短时间内启动和停止许多容器时发生的情况相匹配)可能导致Btrfs块的使用效率低下。这可能会过早地填满Btrfs文件系统,并导致Docker主机上的空间不足情况。使用btrfs filesys show来密切监控Btrfs设备上的可用空间量。

顺序写入

Btrfs 在写入磁盘时使用日志技术。这可能会影响顺序写入的性能,使性能降低多达50%。

碎片化

碎片化是像Btrfs这样的写时复制文件系统的自然副产品。许多小的随机写入可能会加剧这个问题。在使用SSD时,碎片化可能表现为CPU峰值,而在使用旋转磁盘时,则可能导致磁头抖动。这些问题中的任何一个都可能损害性能。

如果您的Linux内核版本是3.9或更高,您可以在挂载Btrfs卷时启用autodefrag功能。在将其部署到生产环境之前,请在自己的工作负载上测试此功能,因为一些测试显示对性能有负面影响。

SSD性能

Btrfs 包含对 SSD 介质的原生优化。要启用这些功能,请使用 -o ssd 挂载选项挂载 Btrfs 文件系统。这些优化包括通过避免不适用于固态介质的优化(如寻道优化)来增强 SSD 的写入性能。

经常平衡Btrfs文件系统

使用操作系统工具,如cron作业,在非高峰时段定期平衡Btrfs文件系统。这可以回收未分配的块,并有助于防止文件系统不必要地填满。除非向文件系统添加额外的物理块设备,否则无法完全重新平衡一个完全满的Btrfs文件系统。

参见 Btrfs Wiki.

使用快速存储

固态硬盘(SSDs)比旋转磁盘提供更快的读写速度。

使用卷来处理写密集型工作负载

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