前言

本文未介绍Btrfs的去重、自我修复RAID、加密,且配置好后将Btrfs当作Ext4使用。

因曾经出现过bt软件bug将硬盘上的数据全删除的事情,并且相关数据至今都懒得恢复,所以一直渴望能使用上写时复制系统的快照功能。

根据维基百科文件系统的对比一文,选择受OMV(Debian)官方内核支持的Btrfs文件系统。单块硬盘即插即用和更少的内存消耗也是我选择Btrfs不是ZFS的原因。

Btrfs特性:

  • 写时复制
  • 透明压缩
  • 去重:重复数据删除
  • 快照:利用写时复制特性的非完整副本,类似于硬连接
  • 自我修复:数据和元数据使用校验和,自动检测静默数据损坏(不做RAID则不能还原修复)
  • RAID:支持基于软件的 RAID 0、RAID 1、RAID 10

Btrfs目前的严重缺陷(截至22.5.29):

  • RAID 5 和 RAID 6 模式存在致命缺陷,除非用来做数据丢失测试,否则不应当用于任何场景。
  • 配额使用的 Qgroup 尚且不稳定且在有(过多)快照的子卷上应用配额可能会导致性能问题。

还需注意部分功能不支持低版本的内核,其中在 5.0 之前的内核版本上搭配 Btrfs 使用交换文件可导致文件系统损坏。

对于Btrfs,可供参考的文档有:

  1. https://btrfs.wiki.kernel.org/
  2. https://btrfs.readthedocs.io/en/latest/btrfs-man5.html
  3. https://wiki.archlinux.org/title/Btrfs_(简体中文)

安装前必看:目前已知问题(Gotchas)
安装后必看:常见问题(FAQ)

新建Btrfs文件系统

擦除硬盘

存储器——磁盘中,擦除硬盘。

注:如果已有数据并且是EXT3/4文件系统,可参考wiki教程转换:https://wiki.archlinux.org/title/Btrfs_(简体中文)#从_Ext3/4_转换

创建文件系统

有分区 Btrfs 磁盘

存储器——文件系统中,创建Btrfs类型的文件系统。OMV将建立新分区表和一个Btrfs分区。

无分区 Btrfs 磁盘

建立无分区 Btrfs 磁盘,使用 Btrfs 通过子卷替代分区表模拟不同的分区。

# 替换mylabel sdX
# OMV源代码使用的配置为:-O skinny-metadata,^mixed-bg 即支持skinny-metadata,禁用mixed-bg
mkfs.btrfs -L mylabel -O skinny-metadata,^mixed-bg /dev/sdX

输出部分解读

更多信息详见 https://btrfs.readthedocs.io/en/latest/mkfs.btrfs.html

root@openmediavault:~# mkfs.btrfs -L data1 /dev/sda
btrfs-progs v5.10.1
See http://btrfs.wiki.kernel.org for more information.

Label:              data1
UUID:               bd70f070-fab3-40a8-9f45-d2c97d0f61f3
Node size:          16384
Sector size:        4096
Filesystem size:    14.55TiB
Block group profiles:
  Data:             single            8.00MiB
  Metadata:         DUP               1.00GiB
  System:           DUP               8.00MiB
SSD detected:       no
Incompat features:  extref, skinny-metadata
Runtime features:
Checksum:           crc32c
Number of devices:  1
Devices:
   ID        SIZE  PATH
    1    14.55TiB  /dev/sda
Node size

节点大小,即 Btrfs 存储元数据的树块大小。必须是扇区大小的倍数和 2 的幂,但不能大于 64KiB (65536)。
较小的节点大小会增加碎片,但会导致更高的 b 树,这反过来会导致更低的锁定争用。更高的节点大小可以在更新元数据块时以更昂贵的内存操作为代价提供更好的打包和更少的碎片。

extref

将目录中每个文件的硬链接限制增加到 65536。

skinny-metadata

更小的元数据(节省了百分之几的元数据)。

挂载文件系统

调整挂载选项

OMV6 下 Btrfs 的挂载变量 OMV_FSTAB_MNTOPS_BTRFS,默认值为"defaults,nofail",详见OMV源码,需要按需更改以开启透明压缩等功能。

# 按需添加 commit=0、ssd_spread、subvol=
omv-env set OMV_FSTAB_MNTOPS_BTRFS "defaults,nofail,compress=zstd,autodefrag,noatime,space_cache=v2"
# 使得环境变量更改生效
omv-salt stage run all

完整选项见Wiki

compress=zstd

启动透明压缩,实测若是以媒体(影视大文件(以GB计)+音乐、图片、图书等小文件(以MB计))为主,zstd的压缩比更优秀。总共1.8T | 1.0T,可压缩部分为1.8G | 5.1G,lzo压缩比50% | 56%,zstd压缩比35 | 49%。lzo\zstd均为默认压缩等级,均未使用过碎片整理。
zlib、 lzo、zstd的对比可看相关测试

autodefrag

自动碎片整理,懒人必备,不适合大量小文件的写入(如大型数据库)。

noatime

要求系统不必在每次访问文件的时候都修改最后一次的访问时间,可以明显提升服务器性能,尤其是在读取密集型工作负载下。这是因为btrfs是写时复制文件系统,每次修改访问时间都需要先复制元数据块,如果一次性访问了很多文件,就会导致大量和分散的写入操作。
除非是安全审计和应用程序需要,否在完全没必要使用文件访问时间。

space_cache=v2

在非常大的文件系统(many TB)和某些工作负载上,v1空间缓存的性能可能会急剧下降。自4.5起,v2通过实现添加了一个称为空闲空间树的 新 b 树解决了这个问题。启用后,将始终使用v2空间缓存,除非将其清除,否则无法禁用。

commit=0

立即提交数据,即立即将数据写入硬盘,默认为30秒,适当增加时间可以提升磁盘性能,但会有断电丢数据的风险。

ssd_spread

  • OMV 挂载 ssd 时默认添加 ssd 参数。
  • 除非你通过OMV WEB自动挂载的盘全是SSD,才需要写入/etc/default/openmediavault,否则请在SSD挂载后修改/etc/openmediavault/config.xml,并执行omv-salt deploy run fstab

尝试分配到更大且对齐的未使用空间块,在低端 SSD 上可能表现更好,根据报告,非常大容量的 SSD 也应该启用。

subvol=<路径>

从路径而不是顶级子卷挂载子卷。
该路径为相对于顶级子卷的相对路径

挂载文件系统

存储器——文件系统中,像平常挂载硬盘一样进行。

挂载子卷

正如前说述,Btrfs 可通过子卷替代分区表模拟不同的分区。但是这属于 Btrfs 的高级用法, OMV6 的 WEB 并不支持,需要手动添加配置。

新建Btrfs子卷

存储器——文件系统中,右上角显示\隐藏栏目中,将挂载点勾上显示,并复制 Btrfs 的挂载点。这里以/srv/dev-disk-by-uuid-140146e1-31da-404d-bf40-491a8e5bc42a 为例,让 Docker 使用挂载到/var/lib/docker/的 Btrfs 子卷docker

创建名为docker的子卷

cd /srv/dev-disk-by-uuid-140146e1-31da-404d-bf40-491a8e5bc42a
btrfs subvolume create docker

修改配置文件

备份配置文件

cp /etc/openmediavault/config.xml /etc/openmediavault/config.xml.bk

方法一

# 获取btrfs配置
omv-confdbadm read conf.system.filesystem.mountpoint  | jq -r '.[]|select(.type=="btrfs")'

# 添加文件系统
## 将获取到的 json 中去除 uuid,改 dir 为挂载目录,opts 添加",subvol=/docker"
## 压缩至一行后执行 omv-confdbadm create conf.system.filesystem.mountpoint '<json>'

omv-confdbadm update conf.system.filesystem.mountpoint '{"fsname":"/dev/disk/by-uuid/140146e1-31da-404d-bf40-491a8e5bc42a","dir":"/var/lib/docker/","type":"btrfs","opts":"defaults,nofail,compress=lzo,autodefrag,noatime,space_cache=v2,subvol=/docker,ssd","freq":0,"passno":2,"hidden":true,"usagewarnthreshold":85,"comment":""}'

# 修改 /etc/openmediavault/config.xml,若新增内容缩在一行,按已挂载的文件系统修改缩进格式,否则更改可能不生效

# 使更改生效
omv-salt deploy run fstab

方法二

# 直接修改 /etc/openmediavault/config.xml
## 复制修改 uuid、usagewarnthreshold,改 dir 为挂载目录,opts 添加",subvol=/docker"
      <mntent>
        ...
      </mntent>

# 使更改生效
omv-salt deploy run fstab

方法三

# 创建文件夹
mkdir /var/lib/docker

# 修改/etc/fstab
nano /etc/fstab
## 复制 /dev/disk/by-uuid/140146e1-31da-404d-bf40-491a8e5bc42a        /srv/dev-disk-by-uuid-140146e1-31da-404d-bf40-491a8e5bc42a  btrfs   defaults,nofail,compress=lzo,autodefrag,noatime,space_cache=v2,ssd  0 2
## 修改/srv/dev-disk-by-uuid-*为/var/lib/docker
## 在,ssd前添加,subvol=/docker

# 使更改生效
mount -a

查看使用空间

btrfs filesystem usage /
# 可查看实际压缩比的工具 compsize
apt install -y btrfs-compsize
compsize /

设备统计信息

必须先挂载文件系统,Btrfs 默认开启 checksum 校验,可使用btrfs device stats命令。

btrfs device stats /dev/sda
btrfs device stats /srv/dev-disk-by-uuid-e0ca4578-293c-468e-ba3a-3c89938c125c

快照

创建快照

# source为要创建快照的对象,[dest/]name为快照安放路径。
btrfs subvolume snapshot source [dest/]name
# 创建只读快照
btrfs subvolume snapshot -r source [dest/]name

定时快照

将以下命令加入至OMV 6 系统——计划任务服务——Anacron

# ./data为目标子卷
# ./snapshots为放快照的目录
# 为子卷 data 创建名为 data-年-月-日 的只读快照
btrfs subvolume snapshot -r ./data ./snapshots/data-$(date +"%Y-%m-%d")
# 删除 7天前的只读快照
btrfs subvolume delete ./snapshots/*-$(date +"%Y-%m-%d" -d'-7 day')

将快照发送到备份用的硬盘

热备盘挂载至/srv/backups
上一小节 定时快照 运行了一天之后,将以下命令加入至OMV 6 系统——计划任务服务——Anacron,定时增量备份快照。

# 先发送一次快照
# btrfs send ./snapshots/data-$(date +"%Y-%m-%d") | btrfs receive /srv/backups
# 按前一天的快照增量发送,增量发送只能发送只读快照
btrfs send -p ./snapshots/data-$(date +"%Y-%m-%d" -d'-1 day') ./snapshots/data-$(date +"%Y-%m-%d") | btrfs receive /srv/backups
# 删除热备盘里7天前的快照
btrfs subvolume delete /srv/backups/*-$(date +"%Y-%m-%d" -d'-7 day')

情景假设

让我们情景假设一下是如何发送:
假设我有一个用于热备的硬盘,挂载至/srv/backups。
并且已经运行了如下命令创建快照:

btrfs subvolume snapshot -r ./data ./snapshots/data-1

那么,在第一次发送时使用如下命令:

btrfs send ./snapshots/data-1 | btrfs receive /srv/backups
# 此时/srv/backups里创建了名为data-1的快照

第二天同一时刻,又创建了一个快照:

btrfs subvolume snapshot -r ./data ./snapshots/data-2

那么,可以使用如下命令进行增量发送:

btrfs send -p ./snapshots/data-1 ./snapshots/data-2 | btrfs receive /srv/backups
# 此时/srv/backups里根据data-1增量创建了名为data-2的快照。
# 此时/srv/backups里同时存在data-1、data-2。
最后修改:2022 年 07 月 21 日 07 : 45 PM