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 的一个优势是无需卸载文件系统或重启 Docker 即可轻松管理 Btrfs 文件系统。

当空间不足时,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 个子卷。 “子卷 2”和“子卷 3”是嵌套的,而“子卷 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 性能

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

注意

对于写入密集型工作负载,使用 Docker 卷可以缓解许多这些因素,而不是依赖于将数据存储在容器的可写层中。然而,在 Btrfs 的情况下,Docker 卷仍然存在这些缺点,除非 /var/lib/docker/volumes/ 不由 Btrfs 支持。

页面缓存

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

小写入

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

顺序写入

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

碎片化

碎片化是像 Btrfs 这样的写时复制(copy-on-write)文件系统的自然副产品。 许多小的随机写入可能会加剧这个问题。碎片化在使用 SSD 时表现为 CPU 尖峰, 在使用旋转磁盘时表现为磁头频繁移动。这些问题中的任何一个都会损害性能。

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

SSD 性能

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

经常平衡 Btrfs 文件系统

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

查看 Btrfs Wiki

使用快速存储

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

使用卷处理高写入工作负载

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