🗃️

新しいファイルシステム Bcachefs

2024/09/06に公開

自宅サーバーを再構築するにあたり、以前利用していたZFSからBcachefsに乗り換えました。

私が利用しているArch Linuxの場合、ZFSは通常のリポジトリ経由では取得できず、またカーネルを更新する際にZFSが対応しているか事前に調査が必要など、管理面でのコストが発生しており多少不満を感じていました。Bcachefsはカーネルにマージされているので、その不満の解消が期待できます。

はじめに

この記事は下記のユーザーガイドと手元で構築したBcachefsの実際の挙動をもとに書いています。

新たにBcachefsを始めたい人や、管理の仕方について知りたい人を対象としています。

uname -sr

Linux 6.10.8-arch1-1

フォーマット

Bcachefsでは事前にフォーマットを行う必要があります。単体のディスクに加え、複数のディスクをまとめて利用することも可能です。

実例がないと解説がしにくいため、先にコマンドを書いておきます。8TBx8のストレージと2TBのSSDでのキャッシュで構築します。レプリカ数は3です。

bcachefs format \
  --errors=ro \
  --replicas=3 \
  --label=hdd.hdd1 /dev/disk/by-id/ata-WDC_WD80EAZZ-00BKLB0_WD-CA0TX67K \
  --label=hdd.hdd2 /dev/disk/by-id/ata-WDC_WD80EAZZ-00BKLB0_WD-CA0UGRAK \
  --label=hdd.hdd3 /dev/disk/by-id/ata-WDC_WD80EAZZ-00BKLB0_WD-CA0UGYXK \
  --label=hdd.hdd4 /dev/disk/by-id/ata-WDC_WD80EAZZ-00BKLB0_WD-CA0UHDKK \
  --label=hdd.hdd5 /dev/disk/by-id/ata-WDC_WD80EAZZ-00BKLB0_WD-CA0URTJK \
  --label=hdd.hdd6 /dev/disk/by-id/ata-WDC_WD80EAZZ-00BKLB0_WD-CA0USJGK \
  --label=hdd.hdd7 /dev/disk/by-id/ata-WDC_WD80EAZZ-00BKLB0_WD-CA0UT8RK \
  --label=hdd.hdd8 /dev/disk/by-id/ata-WDC_WD80EAZZ-00BKLB0_WD-CA2KGR5L \
  --durability=0 --discard \
  --label=nvme.cache1 /dev/disk/by-id/nvme-ADATA_LEGEND_800_2N052L2HSH2C \
  --foreground_target=nvme \
  --promote_target=nvme \
  --background_target=hdd

--errors=ro エラー時の挙動を指定

ファイルシステムでエラーが発生した時の挙動を設定します。定期的にステータスを確認するのであれば continue が良いかもしれません。

オプション 動作
continue エラーをログに出力し何もしない
ro 読み取り専用モードに切り替え
panic システムを停止

--replicas=[n] レプリカ数を指定

レプリカ数を指定します。このオプションに指定した数から1を引いたディスクまで同時故障してもデータ損失がないことが保証されます。

レプリケーションの挙動はRAID10相当 同時故障に注意

RAIDzはパリティを分散して全てのディスクに書き込む手法を採用していますが、Bcachefsでは --replicas に指定された数だけ同じデータを別のディスクに書き込む手法を採用しています。

2台のディスクが同時に故障したとしてもRAIDz2やRAID6の場合はデータは必ず復元できますが、Bcachefsで --replicas=2 を指定した場合は運が悪いとデータ損失が発生します。同時故障について考慮する必要がある場合は、想定同時故障HDD + 1の数をReplicasに指定するのが良いでしょう。

BcachefsでもRAIDz2やRAID6相当のパリティを分散する手法は開発中ですが、2024年09月現在対象のオプションの説明文では (DO NOT USE YET) と記載されています。

bcachefs format --help
    // ...
    --erasure_code          Enable erasure coding (DO NOT USE YET)

参考:
https://www.reddit.com/r/bcachefs/comments/s7nkxr/erasure_code/

--label=hdd.hdd1 /dev/disk ディスクを指定

ラベル付きで物理ディスクを指定します。管理上の都合から /dev/disk/by-id/ を利用していますが、 /dev/sda 等直接ブロックデバイスを指定しても構いません。

ラベルは . 区切りでグループ名の指定が可能です。上記の例の場合は hdd グループの hdd1 といった形で指定されます。グループ名は後述するターゲットでキャッシュの宛先に指定することができます。

物理ディスクサイズが異なっていても問題ありません。Bcachefsは常により空き容量の多いデバイスを優先するため、全てのディスクが同じ割合で埋まるように調節して書き込みされます。

--durability=0 --discard SSDキャッシュ用設定

キャッシュのみに利用するSSDのために、 --durability=0 で信頼性が不要で永続的なストレージとして利用しないことを定義します。また、SSD向けにTRIM/discardを有効化します。

また、--durability オプションを使ってそのディスクの信頼性を明示的にBcachefsに伝えることができます。例えば、Linux上からは1つに見えるブロックデバイスがハードウェアRAIDで2台の冗長性を持っている場合 --durability=2 としておくことでBcachefsがレプリカ数を調整してくれます。

bcachefs format コマンドはディスク指定の順番に注意

やや複雑な点ですが、 bcachefs format コマンドには順番の概念が存在します。 --durability はデフォルトで1ですが、このオプションを指定して以降に追加されたデバイスは0として追加されます。

下記のように、複数のデバイスをフォーマットする場面を考えてみましょう。デフォルトでは暗黙的に --durability=1 が指定されており、3つのHDDは durability=1 として扱われます。その後 --durability=0 を指定すると、後続のディスクは durability=0 として扱われます。

bcachefs format
  --errors=ro 
  ( --durability=1 (defaultで暗黙的に指定) )
  --label hdd.hdd1 /dev/sda (durability=1)
  --label hdd.hdd2 /dev/sdb (durability=1)
  --label hdd.hdd3 /dev/sdc (durability=1)
  --durability=0 
  --label ssd.cache0 /dev/nvme0 (durability=0)
  --label ssd.cache1 /dev/nvme1 (durability=0)
  --durability=2 
  --label hdd.hdd4 /dev/raida (durability=2)
  --label hdd.hdd5 /dev/raidb (durability=2)

前述のSSDキャッシュの説明とあわせ、SSDキャッシュを追加するときは必ず --durability=0 を指定した後にデバイスを追加しましょう。

--foreground_target 他 キャッシュ等の設定

ターゲットを指定します。それぞれ下記のような動作をします。基本的に background_target にはHDD、 foreground_targetpromote_target にはSSDを指定するのが良いでしょう。同じデバイスも指定できます。

オプション 動作
--foreground_target 新規データの優先書き込み先
--promote_target 読み取りデータのキャッシュ先 再度同じファイルにアクセスされたときに高速化
--background_target データの長期保存先 foreground_targetからバックグラウンドで徐々に書き込まれる

マウント

/mnt/ada にマウントすると仮定します。マウントに必要なUUIDは External UUID を利用します。 format 時に表示されるものをメモしておくか、 sudo bcachefs show-super [メンバのブロックデバイス] で再表示できます。

mount.bcachefs UUID=729347e7-24a2-4f80-93ad-885e74f4fd8e /mnt/ada

ブロックデバイスを全指定することでもマウントできます。数が増えると面倒臭いので個人的にはUUIDがおすすめです。

mount -t bcachefs /dev/sda:/dev/sdb:/dev/sdc /mnt/ada

上記のコマンドそのままでは、デバイスが破損したりブロックデバイスが足りない場合は下記のエラーがdmesgに出力されマウントに失敗します。

bch2_fs_open() bch_fs_open err opening /dev/sda: insufficient_devices_to_start
bch2_mount() error: insufficient_devices_to_start

その場合は -o degrated を追加するとデータ損失がない場合に限りマウントできます。また、 -o very_degrated を追加するとデータ損失があるかもしれない状況でも無理矢理マウントできます。

mount.bcachefs UUID=729347e7-24a2-4f80-93ad-885e74f4fd8e -o degrated /mnt/ada

/etc/fstab 記載例

UUID=729347e7-24a2-4f80-93ad-885e74f4fd8e	/mnt/ada	bcachefs	defaults,nofail	0 0

障害復旧

ディスクの破損などが発生した場合の交換手順について簡単に記載しておきます。

-o degrated をつけてマウントする

Degratedモードでマウントをします。

mount.bcachefs UUID=729347e7-24a2-4f80-93ad-885e74f4fd8e -o degrated /mnt/ada

問題のあるデバイスを削除する

/dev/sde が故障したと仮定します。パスで削除・インデックスで削除の2通りの方法が利用できます。コマンドの実行にはかなり時間がかかります。 screen などを利用して切断に備えるのが良いでしょう。

Linux上からブロックデバイスが見える場合は下記の方法で削除できます。

# パスで削除
bcachefs device remove /dev/sde

ブロックデバイスがLinux上から消滅している場合はデバイスのindexを用いて削除することもできます。

# 生きているデバイスからメタデータを取得し問題のあるデバイスのIndexを確認
bcachefs show-super /dev/sda
// ...
Device:                                    4
  Label:                                   hdd5 (5) # かっこ内がIndex
  UUID:                                    4ff2352d-b962-4d67-8bd9-d272cc55b601
  Size:                                    7.28 TiB

# 削除 Indexでの削除にはマウント先のパスが必要
bcachefs device remove 5 /mnt/ada

デバイスを交換し追加する

交換元と交換先のSATAポートが同じならブロックデバイスのパスは変わらないはずです。

bcachefs device add --label=hdd.hdd5 /mnt/ada /dev/sde

不足しているレプリカを再作成

この状態ではレプリカが不足しているため、再作成が必要です。

bcachefs data rereplicate

TIPS: HDDを交換せず別のデバイスにレプリカを再作成する

RAIDzやRAID5, 6とは異なり、破損したHDDに存在するデータを他のHDDの空き容量に書き込むこともできます。すぐに障害のあるHDDを交換することができない場合などに有効です。上記と同じく /dev/sde が故障したと仮定します。

下記コマンドを実行し /dev/sde をReadOnlyに設定します。その後、 evacuate コマンドでその中にあるデータを退避させることが可能です。

bcachefs device set-state /dev/sde readonly
bcachefs device evacuate /dev/sde

SubvolumeとSnapshot

Bcachefsはサブボリューム単位でのスナップショット作成をサポートしています。サブボリュームやスナップショットはLinux上から通常のディレクトリのように見えます。

サブボリュームの作成

bcachefs subvolume create コマンドを利用します。また、検証用にファイルを touch しておきます。

cd /mnt/ada
bcachefs subvolume create sub0
touch /mnt/ada/sub0/hello.world

スナップショットの作成

サブボリュームを指定してスナップショットを作成します。作成したスナップショットはLinux上からは通常のディレクトリのように見えます。

bcachefs subvolume snapshot /mnt/ada/sub0 /mnt/ada/sub0-20240906

ファイルがスナップショット先にも存在することが確認できます。

ls /mnt/ada/sub0* 
/mnt/ada/sub0:
hello.world

/mnt/ada/sub0-20240906:
hello.world

作成したスナップショットは新たなサブボリュームとして振る舞うため、必要に応じてスナップショットのスナップショットを作成することも可能です。

サブボリューム・スナップショットの削除

サブボリュームやスナップショットを削除するために bcachefs subvolume delete コマンドを利用する他、 rm -rfrmdir を用いて削除することができます。

bcachefs subvolume delete /mnt/ada/sub0

オプションの指定はフォーマット後も可能

Bcachefsにはさまざまなオプションが指定できます。オプションは対応するタイミングや単位で切り替えが可能になっています。

例えば、 --data_replicas はフォーマット時のみならずマウント時や実行時、さらにinode単位で設定することができます。下記はマウント済みの /mnt/ada にある very_important フォルダのレプリカ数を5に設定するコマンドです。

bcachefs setattr --data_replicas=5 --recursive /mnt/ada/very_important

このコマンドを使うと、次回以降 /mnt/ada/very_important フォルダへの書き込みは5台のディスクに分散されて行われます。すでに存在するデータも対象にしたい場合は、下記のコマンドを実行します。

bcachefs data rereplicate

利用可能なオプションとタイミングについてはページ最下部に載せてあります。

終わりに

構築し利用を開始した段階では、Bcachefsは十分に実用可能な段階に達していると感じています。ユーザーランドのツールである bcachefs コマンドは出力や使い勝手の点でやや荒削りな部分を感じますが、この辺りは徐々に改善されるでしょうし、一旦構築をしてしまえば利用には困りません。

一方で、柔軟なオプション設定に対して情報量が少ないのがネックに感じました。ディスクの交換といったよくあるワークロードでも、日本語はもちろん英語でも情報が限られているように感じます。ユーザーガイドやWiki、Redditなどの情報を読み込み、オペレーションをする人がきちんと理解をしていないと現段階ではうまく扱うことができないのではないかな?と感じています。

この記事が初めてBcachefsを使う方の参考になれば幸いです。

オプション一覧

2024年09月現在、ユーザーマニュアルに記載されているものの一覧です。

オプション/機能 フォーマット時 マウント時 実行時 inode単位 説明
block_size ファイルシステムのブロックサイズ(デフォルト4k)
btree_node_size Btreeノードサイズ(デフォルト256k)
errors ファイルシステムエラー時のアクション
metadata_replicas メタデータのレプリカ数
data_replicas ユーザーデータのレプリカ数
replicas metadata_replicasとdata_replicasの両方のエイリアス
metadata_checksum メタデータ書き込みのチェックサムタイプ
data_checksum データ書き込みのチェックサムタイプ
compression 圧縮タイプ
background_compression バックグラウンド圧縮タイプ
str_hash 文字列ハッシュテーブルのハッシュ関数
metadata_target メタデータ書き込みの優先ターゲット
foreground_target フォアグラウンド書き込みの優先ターゲット
background_target バックグラウンドでデータを移動するターゲット
promote_target 読み取り時にデータをコピーするターゲット
erasure_code イレイジャーコーディングの有効化
inodes_32bit 新しいinode番号を32ビットに制限
shard_inode_numbers 新しいinode番号の上位ビットにCPU idを使用
wide_macs 完全な128ビット暗号化MACを保存(デフォルト80)
inline_data インラインデータエクステントの有効化(デフォルトon)
journal_flush_delay 自動ジャーナルコミット前の遅延(ミリ秒、デフォルト1000)
journal_flush_disabled sync/fsyncでのジャーナルフラッシュの無効化
journal_reclaim_delay 自動ジャーナル再利用前の遅延(ミリ秒)
acl POSIX ACLの有効化
usrquota ユーザークォータの有効化
grpquota グループクォータの有効化
prjquota プロジェクトクォータの有効化
degraded データが縮退した状態でのマウントを許可
very_degraded データが欠落した状態でのマウントを許可
verbose マウント/リカバリ中の追加デバッグ情報
fsck マウント中にfsckを実行
fix_errors fsck中にエラーを自動修復
ratelimit_errors fsck中のエラーメッセージをレート制限
read_only 読み取り専用モードでマウント
nochanges ジャーナル再生でも書き込みを発行しない
norecovery ジャーナルを再生しない(非推奨)
noexcl デバイスを排他モードで開かない
version_upgrade ディスク上のフォーマットを最新バージョンにアップグレード
discard discard/TRIMサポートの有効化

Discussion