BTRFS 存储驱动程序

重要

在大多数情况下,您应该使用 overlay2 存储驱动程序——仅仅因为您的系统使用 Btrfs 作为其根文件系统,并不需要使用 btrfs 存储驱动程序。

Btrfs 驱动程序存在已知问题。有关更多信息,请参阅 Moby 问题 #27653

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 个子卷。“子卷 2”和“子卷 3”是嵌套的,而“子卷 4”则显示了其自己的内部目录树。

Subvolume example

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

Snapshots diagram

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

Snapshot and subvolume sharing data

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

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

Btrfs container layers

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

  1. 镜像的基础层存储在 /var/lib/docker/btrfs/subvolumes 下的 Btrfs *子卷* 中。

  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

使用快速存储

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

对写入密集型工作负载使用卷

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

© . This site is unofficial and not affiliated with Kubernetes or Docker Inc.