Docker下一代写时复制文件系统BTRFS存储驱动程序

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

Docker的btrfs存储驱动程序利用了许多Btrfs特性进行镜像和容器管理。这些特性包括块级操作,按需分配,写时复制快照和易于管理。您可以轻松地将多个物理块设备组合到一个Btrfs文件系统中。

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

注意btrfs存储驱动程序仅支待Ubuntu或Debian上的Docker Engine - Community,以及SLES上的Docker EE/CS Engine。

先决条件

如果满足以下先决条件,则支持btrfs

  • Docker Engine - Community:对于Docker Engine-Community,仅在Ubuntu或Debian上建议使用btrfs

  • Docker Docker EE:对于Docker EE和CS-Engine,btrfs仅在SLES上受支持。请参阅产品兼容性列表,以获取商业支持的Docker支持的所有受配置。

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

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

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

$ sudo cat/proc/filesystems | grep btrfs

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/*
  1. 将一个或多个专用块设备格式化为Btrfs文件系统。本示例假定您使用的是两个名为/dev/xvdf/dev/xvdg的块设备。仔细检查块设备名称,因为这是破坏性操作。
$ sudo mkfs.btrfs -f /dev/xvdf /dev/xvdg

Btrfs还有更多选项,包括striping和RAID。请参阅Btrfs文档

  1. 挂载新的Btrfs文件系统到/var/lib/docker/挂载点上。您可以指定用于创建Btrfs文件系统的任何块设备。
$ sudo mount -t btrfs /dev/xvdf /var/lib/docker

不要忘记通过在/etc/fstab中添加一个条目,使更改在重启后永久生效。

  1. /var/lib/docker.bk的内容复制到/var/lib/docker/
$ sudo cp -au /var/lib/docker.bk/* /var/lib/docker/
  1. 配置Docker使用btrfs存储驱动程序。即使/var/lib/docker/现在正在使用Btrfs文件系统,这也是必需的。编辑或创建文件/etc/docker/daemon.json。如果是新文件,请添加以下内容。如果它是现有文件,则仅添加键和值,如果它不是结束大括号(}之前的最后一行,请小心以逗号结束。
{
  "storage-driver": "btrfs"
}

查看每个存储驱动程序的所有存储选项:

- [Stable](https://docs.docker.com/engine/reference/commandline/dockerd/#storage-driver-options)
- [Edge](https://docs.docker.com/edge/engine/reference/commandline/dockerd/#storage-driver-options)
  1. 启动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
<output truncated>
  1. 准备就绪后,删除/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存储驱动程序与devicemapper或其他存储驱动程序的工作方式不同,因为整个/var/lib/docker/目录存储在Btrfs卷上。

磁盘上的镜像和容器层

有关镜像层和可写容器层的信息存储在/var/lib/docker/btrfs/subvolumes/中。每个镜像或容器层在此子目录中包含一个目录,将一个层及其所有父层构建到统一的文件系统。子卷本来就是写时复制的,并从基础存储池按需分配空间。他们也支持嵌套和快照。下图显示了4个子卷。 “子卷2”和“子卷3”是嵌套的,而“子卷4”则显示其自己的内部目录树。

子卷示例

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

快照图

在磁盘上,快照的外观和感觉就像子卷一样,但实际上它们更小且更节省空间。使用写时复制最大化存储效率和最小化层大小,并且在块级别管理容器可写层中的写入。下图显示了一个子卷及其共享数据的快照。

快照和子卷共享数据

为了获得最大效率,当一个容器需要更多空间时,将以大约1 GB的“块”形式对其进行分配。

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

Btrfs容器图层

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

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

  2. 后续镜像层存储为Btrfs快照(父层子卷或快照的),但具有该层引入的更改。这些差异存储在块级别。

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

使用btrfs容器如何进行读写

读取文件

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

写入文件

  • 写入新文件:将新文件写入容器会调用按需分配操作,以将新数据块分配给容器的快照。然后将文件写入此新空间。按需分配操作是使用Btrfs本来就有的功能。结果,将新文件写入容器的快照不会降低写入速度。

  • 修改现有文件:更新容器中的现有文件是“写时复制”操作(Btrfs术语是*“写时重定向”(redirect-on-write)*)。从文件当前所在的层读取原始数据,并且仅将修改后的块写入容器的可写层。接下来,Btrfs驱动程序更新快照中的文件系统元数据以指向此新数据。此行为导致很少的开销。

  • 删除文件或目录:如果容器删除了下层中存在的文件或目录,则Btrfs将掩盖下层中文件或目录的存在。如果容器创建了一个文件然后将其删除,则此操作在Btrfs文件系统本身中执行,并回收空间。

使用Btrfs,写入和更新许多小文件可能会导致性能降低。

Btrfs和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文件系统。这些优化避免了不适用于固态介质的优化,例如seek optimizations,从而增强了SSD的写入性能。

  • 经常平衡Btrfs文件系统:在非高峰时段,使用操作系统实用程序(如cron作业)定期平衡Btrfs文件系统。这样可以回收未分配的块,并有助于防止文件系统不必要地占用。除非您向文件系统添加其他物理块设备,否则您无法重新平衡完全满的Btrfs文件系统。请参阅BTRFS Wiki

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

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

相关信息