Btrfs RAID
本页提供在 MikroTik RouterOS 中使用两块磁盘设置 Btrfs RAID1 阵列的分步说明,涵盖磁盘准备、格式化、添加设备、数据 balance 以及确保 RAID profile 一致。
Btrfs RAID
信息
此功能需要 Storage 软件包。
双磁盘 Btrfs-RAID(RAID1)
如果你想仅用两块磁盘(例如 NAS)创建一个可靠的数据存储方案,可以按照以下步骤成功创建 Btrfs 的 RAID1 阵列:
-
找到你想用于设置 Btrfs-RAID 的磁盘名称。
/disk/print:::warning 在本示例中,使用的磁盘将被称为
<disk-name-1>和<disk-name-2>,请确保将这些占位符替换为你实际的磁盘名称! ::: -
将其中一块磁盘格式化为 Btrfs,本例中为
<disk-name-1>。/disk/format <disk-name-1> file-system=btrfs -
你可以使用以下命令查看 Btrfs 磁盘的当前状态。
/disk/btrfs/filesystem/print:::info 如果你的磁盘上已存在 RAID,而你想为简化起见移除任何过时的配置,可以使用
/disk/format <disk-name-x> file-system=wipe-quick擦除磁盘。当/disk/btrfs/filesystem/print下存在不需要的条目时,这会很有用 ::: -
为便于识别,为 Btrfs 磁盘添加一个标签。
/disk/btrfs/filesystem/set [find where present-devs=<disk-name-1>] label=BtrfsRAID -
将一块磁盘添加到现有的 Btrfs 磁盘。
/disk/btrfs/filesystem/add-device [find where present-devs=<disk-name-1>] device=<disk-name-2> -
启动 Btrfs balance 过程,以创建 RAID1 配置的 Btrfs-RAID。
/disk/btrfs/filesystem/balance-start [find where label=BtrfsRAID] data-profile=raid1 metadata-profile=raid1 system-profile=raid1 -
重要: 请再次确认所有磁盘的 Btrfs profile 一致。在本例中,你需要确保
data、meta和systemprofile 均为raid1。/disk/btrfs/filesystem/print:::danger 如果你注意到输出中例如出现
data,single:1,那么你需要再次执行/disk/btrfs/filesystem/balance-start命令。 ::::::info 理想状态是输出与下面类似,
data、system和meta的 Btrfs profile 均设置为raid1:> /disk/btrfs/filesystem/print ... data,raid1:1.07GB disk1:1.07GB disk2:1.07GB, used:0% system,raid1:33.6MB disk1:33.6MB disk2:33.6MB, used:0% meta,raid1:1.07GB disk1:1.07GB disk2:1.07GB, used:0%:::
:::tip 使用 BtrfsRAIDCheck 脚本来提醒你 Btrfs profile 不一致的问题! :::
-
为第二块磁盘设置
mount-filesystem=no,以防止文件重复显示。/disk/set <disk-name-2> mount-filesystem=no:::info Btrfs 有一项特性,允许你挂载 Btrfs-RAID 中的任意成员,并且仍然能够访问整个 Btrfs-RAID 阵列。该特性的一个缺点是,如果你挂载了同一个阵列两次,那么你的文件也会重复显示两次。为防止这种情况,你可以简单地禁止自动挂载 Btrfs-RAID 成员之一。 :::
-
为便于管理,你也可以更改挂载点的名称。
/disk/set <disk-name-1> mount-point-template=BtrfsRAID -
你新创建的 Btrfs-RAID 阵列现在可以通过
/BtrfsRAID/文件夹访问。
拥有 Btrfs-RAID 这样可靠的存储方案后,可以考虑按照以下推荐指南为你的 RouterOS 设备添加实用功能:
- 运行你自己的 Containers
- DLNA 媒体服务器
- SMB 服务器
- NFS 服务器
警告
当常规 RAID 与 Btrfs 文件系统一起使用时,你的 RAID 阵列将无法从 Bitrot 中自愈,常规 RAID 只能检测 Bitrot。在可能的情况下,建议使用 Btrfs-RAID(本节描述的配置)。
四磁盘 Btrfs-RAID(RAID10)
如果你想使用 Btrfs 创建一个兼顾冗余和高容量的 RAID10 阵列,请执行以下命令:
- 找到你想用于设置 Btrfs-RAID 的磁盘名称。
/disk/print
:::warning
在本示例中,使用的磁盘将被称为 <disk-name-1>,``<disk-name-2>, <disk-name-3> and <disk-name-4>,请确保将这些占位符替换为你实际的磁盘名称!
:::
-
将其中一块磁盘格式化为 Btrfs,本例中为
<disk-name-1>:/disk/format <disk-name-1> file-system=btrfs -
你可以使用以下命令查看 Btrfs 磁盘的当前状态:
/disk/btrfs/filesystem/print:::info 如果你的磁盘上已存在 RAID,而你想为简化起见移除任何过时的配置,可以使用
/disk/format <disk-name-x> file-system=wipe-quick擦除磁盘。当/disk/btrfs/filesystem/print下存在不需要的条目时,这会很有用 ::: -
为便于识别,为 Btrfs 磁盘添加一个标签:
/disk/btrfs/filesystem/set [find where present-devs=<disk-name-1>] label=BtrfsRAID -
将其它磁盘添加到现有的 Btrfs 磁盘:
/disk/btrfs/filesystem/add-device [find where present-devs=<disk-name-1>] device=<disk-name-2> /disk/btrfs/filesystem/add-device [find where present-devs=<disk-name-1>] device=<disk-name-3> /disk/btrfs/filesystem/add-device [find where present-devs=<disk-name-1>] device=<disk-name-4> -
启动 Btrfs balance 过程,以创建 RAID1 配置的 Btrfs-RAID:
/disk/btrfs/filesystem/balance-start [find where label=BtrfsRAID] data-profile=raid10 metadata-profile=raid1c4 system-profile=raid1c4:::info
data-profile、metadata-profile和system-profile有许多可能的配置。对于 RAID10 阵列,建议为system-profile和metadata-profile使用raid1c3或raid1c4。raid1c4 profile 会在不同磁盘上存储 4 份数据副本。这使数据冗余度更高,但会占用更多磁盘空间。 ::::::danger 不支持 Btrfs RAID5 和 RAID6。如果你需要此类 RAID 配置,请使用常规 RAID。请注意,在 Btrfs 上使用常规 RAID 时你将没有 bit-rot 保护。 :::
:::warning 对大多数用例,请将
metadata-profile设置为与system-profile相同的值。避免为这两个 profile 使用不同的值。 ::: -
为其它磁盘设置
mount-filesystem=no,以防止文件重复显示:/disk/set <disk-name-2> mount-filesystem=no /disk/set <disk-name-3> mount-filesystem=no /disk/set <disk-name-4> mount-filesystem=no -
为便于管理,你也可以更改挂载点的名称:
/disk/set <disk-name-1> mount-point-template=BtrfsRAID -
你新创建的 Btrfs-RAID 阵列现在可以通过
/BtrfsRAID/文件夹访问。
更换 Btrfs RAID 阵列中的磁盘
如果磁盘发生故障,或者你需要更换现有 Btrfs RAID 阵列中的磁盘,请按照以下步骤操作:
-
确保确定需要更换的正确磁盘。使用
/disk/print detail和/disk/blink确定正确的磁盘。我们假设故障磁盘为disk2。 -
弹出故障磁盘:
/disk/eject disk2 -
物理移除故障磁盘。
-
打印你的 Btrfs RAID 阵列的当前状态:
/disk/btrfs/filesystem/print -
在命令输出中查找缺失磁盘的
DEV-ID:[admin@MikroTik] /disk> /disk/btrfs/filesystem/print Flags: I - MISSING-DEVS Columns: LABEL, DEV-IDS, DEVS, DEFAULT-SUBVOLUME, SPACES, BALANCE-STATUS, UUID, WRITE-ERRORS, READ-ERRORS, FLUSH-ERRORS, CORRUPTION-ERRORS, GENERATION-ERRORS # LABEL DEV-IDS DEVS DEFAULT-SUBVOLUME SPACES BALANCE-STATUS UUID W R F C G 0 I BtrfsRAID 1 disk1 <FS_ROOT> disk1:480GB, used:0% done 9246dfaa-be9f-4e08-a560-53cb8e82023b 0 0 0 0 0 2 missing data,raid1:1.07GB disk1:1.07GB, used:0% 0 0 0 0 0 data,single:1.07GB, used:0% system,raid1:33.6MB disk1:33.6MB, used:0% meta,raid1:1.07GB disk1:1.07GB, used:0% global-reserve:3.41MB, used:0%:::info 在本例中,缺失磁盘的
DEV-ID为 "2"。 ::: -
插入一块新磁盘。
-
确定新磁盘的名称:
/disk/print:::info 在本例中,新磁盘的名称为
disk3::: -
运行以下命令以更换 Btrfs RAID 阵列中的磁盘:
/disk/btrfs/filesystem/replace-device device-to-remove-id=2 device-to-add=disk3 BtrfsRAID:::warning 请确保将
device-to-remove-id正确设置为你之前确定的DEV-ID! ::: -
检查更换状态,确保
REPLACE-STATUS标记为done。> /disk/btrfs/filesystem/print Columns: LABEL, DEV-IDS, DEVS, DEFAULT-SUBVOLUME, SPACES, BALANCE-STATUS, REPLACE-STATUS, UUID, WRITE-ERRORS, READ-ERRORS, FLUSH-ERRORS, CORRUPTION-ERRORS, GENERATION-ERRORS # LABEL DEV-IDS DEVS DEFAULT-SUBVOLUME SPACES BALANCE-STATUS REPLACE-STATUS UUID WRITE-ERRORS READ-ERRORS F C G 0 BtrfsRAID 1 disk1 <FS_ROOT> disk1:480GB, used:9% done done 9246dfaa-be9f-4e08-a560-53cb8e82023b 0 0 0 0 0 2 disk3 disk3:480GB, used:9% 0 0 0 0 0 data,raid1:40.8GB disk1:40.8GB disk3:40.8GB, used:72% system,raid1:101MB disk1:101MB disk3:101MB, used:0% meta,raid1:3.22GB disk1:3.22GB disk3:3.22GB, used:1% global-reserve:30.7MB, used:0%:::info 确保你的 Btrfs RAID 阵列在
data、system或meta方面没有不一致的 Btrfs profile。理想的 profile 取决于你的具体设置。对于 RAID1 配置,你很可能希望这些 profile 保持一致,但在 RAID10 配置中这些值可以不同。在任何 RAID 配置中,data、system或meta都不应出现singleprofile。 :::
创建 Btrfs-RAID 检查
监控 Btrfs-RAID 阵列的健康状况极其重要。其中一种方法是使用下面的脚本作为工作示例:
/system/scheduler
add interval=1w30s name=BraidBalanceStartCall on-event=BraidBalanceStart policy=ftp,read,write,policy,test,sniff start-date=1970-01-01 start-time=01:00:00
add interval=1w30s name=BraidScrubStartCall on-event=BraidScrubStart policy=ftp,read,write,policy,test,sniff start-date=1970-01-01 start-time=02:00:00
add interval=2m name=BraidBalanceStatusCheckCall on-event=BraidBalanceStatus policy=ftp,read,write,policy,test,sniff start-time=startup
add interval=2m name=BraidScrubStatusCheckCall on-event=BraidScrubStatus policy=ftp,read,write,policy,test,sniff start-time=startup
add interval=30s name=BraidHealthCheckCall on-event=BraidHealthCheck policy=ftp,read,write,policy,test,sniff start-time=startup
add interval=2m name=BraidReplaceStatusCheckCall on-event=BraidReplaceStatus policy=ftp,read,write,policy,test,sniff start-time=startup
/system/script
add dont-require-permissions=no name=BraidScrubStart owner=admin policy=ftp,read,write,policy,test,sniff source=":global btrfsscrubstatuscheck;\
\nif (\$btrfsscrubstatuscheck != \"started\") do={\
\n :set \$btrfsscrubstatuscheck (\$btrfsscrubstatuscheck \"started\");\
\n foreach i in=[/disk/btrfs/filesystem/find] do={ /disk/btrfs/filesystem/scrub-start \$i;\
\n :local temp [ /disk/btrfs/filesystem/get value-name=label \$i;]\
\n /log/info message=\"INFO: Btrfs scrub process started on \$temp\";\
\n :delay 3; \
\n }\
\n}\
\n"
add dont-require-permissions=no name=BraidBalanceStart owner=admin policy=ftp,read,write,policy,test,sniff source=":global btrfsbalancestatuscheck;\
\nif (\$btrfsbalancestatuscheck != \"started\") do={\
\n:set \$btrfsbalancestatuscheck (\$btrfsbalancestatuscheck \"started\");\
\n:local percentage;\
\n:set \$percentage (\$percentage 50);\
\n foreach i in=[/disk/btrfs/filesystem/find] do={ /disk/btrfs/filesystem/balance-start data-usage=\$percentage \$i;\
\n :local temp [ /disk/btrfs/filesystem/get value-name=label \$i;]\
\n /log/info message=\"INFO: Btrfs balance process started on \$temp\";\
\n :delay 3; \
\n }\
\n}"
add dont-require-permissions=no name=BraidBalanceStatus owner=admin policy=ftp,read,write,policy,test,sniff source=":global btrfsbalancestatuscheck;\
\nif (\$btrfsbalancestatuscheck = \"started\") do={\
\n:local arraycnt [/disk/btrfs/filesystem/print count-only as-value];\
\n:local counter [:set \$counter (\$counter 0)];\
\n:local counterdiff [:set \$counterdiff (\$counterdiff 0)];\
\n foreach i in=[/disk/btrfs/filesystem/find] do={ \
\n :local barray [ /disk/btrfs/filesystem/get value-name=balance-status \$i;]\
\n :local temp [ /disk/btrfs/filesystem/get value-name=label \$i;]\
\n if ( \$barray != \"done\" and \$btrfsbalancestatuscheck = \"started\") do={\
\n /log/info message=\"INFO: Btrfs current balance status on \$temp is \$barray\";\
\n }\
\n if ( \$barray = \"done\" and \$btrfsbalancestatuscheck = \"started\") do={\
\n :set \$counter (\$counter +1);\
\n :set \$counterdiff (\$arraycnt - \$counter);\
\n if (\$counterdiff =1) do={\
\n /log/info message=\"INFO: Btrfs balancing already done on \$counter arrays\";\
\n }\
\n }\
\n if ( \$counter = \$arraycnt) do={\
\n /log/info message=\"INFO: Btrfs array balance status on \$temp is \$barray \";\
\n :set \$btrfsbalancestatuscheck (\$btrfsbalancestatuscheck \"done\");\
\n }\
\n } \
\n}"
add dont-require-permissions=no name=BraidScrubStatus owner=admin policy=ftp,read,write,policy,test,sniff source=":global btrfsscrubstatuscheck;\
\nif (\$btrfsscrubstatuscheck = \"started\") do={\
\n:local arraycnt [/disk/btrfs/filesystem/print count-only as-value];\
\n:local counter [:set \$counter (\$counter 0)];\
\n:local counterdiff [:set \$counterdiff (\$counterdiff 0)];\
\n foreach i in=[/disk/btrfs/filesystem/find] do={ \
\n :local barray [ /disk/btrfs/filesystem/get value-name=scrub-status \$i;]\
\n :local temp [ /disk/btrfs/filesystem/get value-name=label \$i;]\
\n if ( \$barray != \"done\" and \$btrfsscrubstatuscheck = \"started\") do={\
\n /log/info message=\"INFO: Btrfs current scrub status on \$temp is \$barray\";\
\n }\
\n if ( \$barray = \"done\" and \$btrfsscrubstatuscheck = \"started\") do={\
\n :set \$counter (\$counter +1);\
\n :set \$counterdiff (\$arraycnt - \$counter);\
\n if (\$counterdiff =1) do={\
\n /log/info message=\"INFO: Btrfs scrubbing already done on \$counter arrays\";\
\n }\
\n }\
\n if ( \$counter = \$arraycnt ) do={\
\n /log/info message=\"INFO: Btrfs array scrub status on \$temp is \$barray \";\
\n :set \$btrfsscrubstatuscheck (\$btrfsscrubstatuscheck \"done\");\
\n }\
\n } \
\n}"
add dont-require-permissions=no name=BraidReplaceStatus owner=admin policy=ftp,read,write,policy,test,sniff source=":global btrfsreplacestatuscheck;\
\nif (\$btrfsreplacestatuscheck = \"started\") do={\
\n:local arraycnt [/disk/btrfs/filesystem/print count-only as-value];\
\n:local counter [:set \$counter (\$counter 0)];\
\n:local counterdiff [:set \$counterdiff (\$counterdiff 0)];\
\n foreach i in=[/disk/btrfs/filesystem/find] do={ \
\n :local barray [ /disk/btrfs/filesystem/get value-name=replace-status \$i;]\
\n :local temp [ /disk/btrfs/filesystem/get value-name=label \$i;]\
\n :local multipleprofiles [ /disk/btrfs/filesystem/get value-name=spaces \$i; ]\
\n if ( \$barray ~ \"working\" and \$btrfsreplacestatuscheck = \"started\") do={\
\n /log/info message=\"INFO: Btrfs current replace status on \$temp is \$barray\";\
\n }\
\n if ( \$barray ~ \"done\" and \$btrfsreplacestatuscheck = \"started\" and \$multipleprofiles~\"single\" ) do={\
\n /log/info message=\"INFO: Braid balance after replace-device started on \$temp\";\
\n if (\$btrfsbalancestatuscheck!=\"started\") do={\
\n /disk/btrfs/filesystem/balance-start \$temp;\
\n :set \$btrfsbalancestatuscheck (\$btrfsbalancestatuscheck \"started\");\
\n }\
\n }\
\n if ( \$barray = \"done\" and \$btrfsreplacestatuscheck = \"started\") do={\
\n :set \$counter (\$counter +1);\
\n :set \$counterdiff (\$arraycnt - \$counter);\
\n if (\$counterdiff =1) do={\
\n /log/info message=\"INFO: Btrfs replace already done on \$counter arrays\";\
\n }\
\n }\
\n if ( \$counter = \$arraycnt ) do={\
\n /log/info message=\"INFO: Btrfs array replace status on \$temp is \$barray \";\
\n :set \$btrfsreplacestatuscheck (\$btrfsreplacestatuscheck \"done\");\
\n }\
\n } \
\n}\
\n:set \$btrfsreplacestatuscheck (\$btrfsreplacestatuscheck \"started\");"
add dont-require-permissions=no name=BraidHealthCheck owner=admin policy=ftp,read,write,policy,test,sniff source="foreach i in=[/disk/btrfs/filesystem/find] do={ \
\n:local sysadmin; \
\n\
\n:set \$sysadmin \"<servername@domain.tld>\";\
\n\
\n:local temp [ /disk/btrfs/filesystem/get value-name=label \$i;]\
\n:local haserror [/disk/btrfs/filesystem/get value-name=about \$i; ]\
\n:local hasmissing [ /disk/btrfs/filesystem/get value-name=devs \$i; ]\
\n:local hasmultiprofile [ /disk/btrfs/filesystem/get value-name=spaces \$i; ]\
\n:local replacestatus [ /disk/btrfs/filesystem/get value-name=replace-status \$i; ]\
\n:local multiplediskarray [:len [/disk/btrfs/filesystem/find where label=\$temp and dev-ids~\"2\"];]\
\n\
\n if ( \$hasmissing~\"missing\" and ([:len \$replacestatus]=0)) do= {\
\n /log/info message=\"ERROR: BtrfsHealthCheck found missing array member on \$temp\";\
\n /tool/e-mail/send to= \$sysadmin subject=([/system/identity/get name].\" BtrfsHealthCheck found missing array member\") body=(\"Btrfs array where found missing array member on \" .\$temp . \" \");\
\n :delay 19; \
\n }\
\n\
\n if ( \$multiplediskarray > 0 and \$hasmultiprofile~\"single\") do= {\
\n /log/info message=\"ERROR: BtrfsHealthCheck found multiprofile on \$temp array. To start balance process, run /disk/btrfs/filesystem/balance-start \$temp command \";\
\n /tool/e-mail/send to= \$sysadmin subject=([/system/identity/get name].\" BtrfsHealthCheck found multiprofile on \" .\$temp. \" array.\") body=(\"Btrfs array where found with multiprofile status on \" .\$temp . \"To start balance process, run once\
\_ /disk/btrfs/filesystem/balance-start \" .\$temp. \" command \");\
\n :delay 19; \
\n } \
\n\
\n if (([:len \$haserror]) > 0 ) do= {\
\n /log/info message=\"ERROR: BtrfsHealthCheck found errors on \$temp\";\
\n /tool/e-mail/send to= \$sysadmin subject=([/system/identity/get name].\" BtrfsHealthCheck found errors\") body=(\"Btrfs array where found errors on \" .\$temp . \" \");\
\n :delay 20; \
\n }\
\n}\
\n"
你还需要调整 RouterOS 设备上的电子邮件服务器设置:
/tool/e-mail
set from=<raidcheck@domain.tld> port=587 server=smtp.domain.com tls=starttls
警告
请确保按照你的电子邮件服务器所需的设置调整电子邮件配置。记得调整上面脚本中的电子邮件地址。