BTRFSについて調べてみた
知人の一人が停電でfsckが入りまくったことをSNSに投稿していたのを見て、我が家は全てExt4なので別段fsckは入らなかったが、より停電等に強いというBtrfsに移行すべきか検討するための下調べとして、そもそもBtrfsがなにか少し調べてみた。
何故Btrfsに関心を持つのか
Ext4は一旦変更点をジャーナルとして書き出した後にアトミックに変更を反映させるため、ファイルシステム内部に矛盾が生じにくく、停電等による電源断時にも必ずしもfsckを走らせる必要が無い。また非常に枯れていて安定しており、読み書きの性能も高い。したがって、現在においてもLinuxシステムを構築する際のHDDあるいはSSD等のストレージのフォーマット形式としては、Ext4が標準かつ第一選択肢である。
可用性の確保
Ext4自体は単なるファイルシステムであってRAIDなどの可用性を高める機能は持たない。過去にディスク故障でデータを失った経験があり、データーの可用性の確保には関心がある。最悪再インストールすれば復旧できるroot(/)は、たまにしか変更しない設定ファイル類をどこかに手動でバックアップしておけば、可用性が限定的でも実用上問題ない。一方、自分で作ったデーターがある /home については何らかの方法によってより高い可用性を確保したい。大昔には定期的にFDDにバックアップを取っていた。その後DAT(Digital Audio Tape)やDVD-Rにバックアップ先を変えてきたが、HDDの容量が大きくなるにつれ、これらのメディアでは枚数が増え過ぎてバックアップが困難になってしまった。
そこで同一容量のHDDを3台調達し、Linuxのmd-raid (Software RAID) でRAID1構成を組むようにした。定期的にシステムの電源を落とし、RAID1を構成するHDDの一つを物理的に取り外してシステムを一旦再起動、md-raidにRAID1の片系に障害が発生したことを認識させてから再度電源を落とし、取り外した2台目のHDDの代わりに3台目のHDDを接続してシステムを起動する。そしてmdadmで3台目のHDDをRAID1に再度追加する。こうすることで、データーは常に物理的に2台のHDDに書き込まれているので、1台のHDDに障害があってもデーターが失われることはない。また、HDDを交換した時点のデーターは取り外したHDDに残っているので、必要なデーターを誤消去してしまったとしても、そのデーターが前回のHDDに交換時にあったものならば、そこから復旧することができる。このような運用を初めてもう10年以上になるが、それ以来データーを失うことがなくなり満足している。
ただ、システムの電源を落としHDDを物理的に交換するのは結構面倒な作業である。5インチベイ用の交換可能なSATAのディスクマウントを使っているので交換するためにケースを開ける必要はないが、それでも面倒なためにだんだんと頻度が落ちてしまった。昨今売られているPCケースには外部からアクセスできる5インチベイがそもそも無いので、もしPCケースを新しいものにすると毎回ケースを開けなければならない。交換作業をした後のRAID1再構成にはかなり時間がかかるので、その間に1台目のHDDに故障があるとデーターの損失につながってしまう。
Btrfsを使ってスナップショットをもっとスマートに取る
そもそもHDDの交換は、その時点のスナップショットを保存するためのものなのだが、スナップショットをもっとスマートに保存することができないだろうか?その答えがBtrfsである。Btrfsはファイルシステムとしてスナップショットをサポートしている。しかも、スナップショットは内部的には参照カウント数を増やすだけなので、スナップショットの取得のためにシステムを長時間停止する必要がない。取得前後でファイルの内容が変わっていなければストレージ容量もほとんど消費しない。
さらに嬉しいのは、今はExt4の下位でmd-raidを使って実現しているRAID1の機能も、Btrfsの中で実現できるところだ。そしてBtrfsのRAID1にはmd-raidにはない利点がある。一つは、より柔軟な構成ができる、というところである。3台の同一容量のストレージでRAID1を構成することを考える。仮に各ストレージはそれぞれ4TBだとする。
Btrfsの場合: 3台でRAID1を構成すると、データーは2重化され、最終的な容量は (3台の合計容量 x 1/2 =) 6TBになる。
Btrfsの場合: 3台でRAID1C3を構成すると、データーは3重化され、最終的な容量は4TBになる(つまり、md-raidで3台でRAID1を構成するのと同じ)
md-raidの場合: 3台でRAID1を構成すると、データーは3重化され、最終的な容量は4TBになる(つまり、Btrfsで3台でRAID1C3を構成するのと同じ)。
md-raidの場合: 3台の内1台をspareに指定できる。この場合、同じデーターはactiveな2台のストレージに書き込まれる。最終的な容量は4TBになる。もしactiveなストレージの1台に異常がある場合、spareのストレージが自動的にRAID1に組み込まれる。Btrfsにはspareを自動的に組み込む仕組みはないが、下記の様に、より徹底したチェック(btrfs scrub)とストレージ入れ替え(btrfs device replace)機能がある。
RAID1を組むと、全てのストレージの合計容量の1/2の容量のストレージとして認識される、というのは面白い。しかも、3台は必ずしも同一容量でなくてもよいのである。ミラーリングというと、全てのストレージが最初から最後まで完全にコピー(ミラー)になっている、という語感だが、BtrfsにおけるRAID1とは同じデーターがストレージのうちのどれか2台に書かれている、という意味であり、ミラーリングとは若干ニュアンスが異なる。
Btrfsのチェックサムによる障害検出
md-raidの場合、デバイスをアクセした際に何らかのエラーが発生するとそれを障害と認識する。一見すると何のエラーも発生していないが、実はデーターがストレージに正しく書き込まれていなかった、というようなもの……
を検出する事はできない。一方Btrfsにはscrubというコマンドがあり、このコマンドを明示的に実行すると、ストレージの全データー(ファイルの中身)およびメタデーター(ファイルの名前やタイムスタンプ他の情報)を全て読み出し、チェックサム検証を行う。エラーがデバイスドライバーから返ってきていなくても、何らかの障害があればチェックサムによって検出することができる。データーおよびメタデーターの全てをチェックサムで検証できるというのは、md-raidを含む通常のRAIDに比べて明らかに利点であると言える。
Btrfsの設計思想だと思われるのは、何らかの障害がある状態だとファイルシステムをマウントできなくなることである。md-raidだと、RAID1のディスクが一つ存在しなくても、起動時にメッセージ表示されるだけで何の問題もなくマウントしシステムが起動できてしまう。メッセージを読み落とすと、そのまま気が付かずに片肺状態で使い続けてしまうかもしれない。Btrfsの場合は、システムを起動する際にGrubから -o degraded という起動オプションを明示的に渡さないとマウントできない。これは、おそらくエラーが検出されるような状態は不安定なので、すぐさまデーター保全等のメンテナンス作業に入るべきだ、ということでこのような設計になっているのではないかと想像する。
色々と仮想環境でRAID1を作成し試してみたが、ストレージの内容をランダムに壊すと、たまたまそこをアクセスしたときにエラーを検出し、そのファイルについて透過的に修正を行うようである。アクセスしていないファイルについても明示的に btrfs scrub すればエラーを検出し、正しく修正された。RAID1構成になっているので、2つのコピーのどちらが正しいのかをチェックサムを使って自動的に判断し、チェックサムが整合している方の値で不整合の方を上書きするらしい。md-raidにもscrubという概念はあるが、RAID1の場合2つのストレージを比較することで不一致は検出できるが、チェックサム等がないためどちらが正しいのか判定できない。そのため、md(4)マニュアルによれば For RAID1, this involves copying the contents of the first drive onto all other drives. とあり、常に1つ目のストレージが正しいと仮定するようなので、1つ目のストレージのデーターが壊れた場合データーが破損してしまう。
ZFSをLinux上で使うのは避けるべきである
そもそもBtrfsを採用するかどうかを(OSとしてLinuxを使うことを暗黙の前提として)検討するためにインターネット上の情報を検索すると、「BtrfsはZFSの劣化コピーだからZFSを使ったほうが良い」という見解がたくさん見つかる。結論から言うとこれは誤解もしくは理解不足である。
より正確に表現すると、確かにZFSは優れた性質を持つファイルシステムであり、もしZFSを使いたいというのが第一の要求条件ならOSとしてSolarisなりFreeBSDを選んだ上でZFSを使うことを検討すべきである。もしOSとしてLinuxを使いたいというのが第一条件でCoW(Copy on write。スナップショット等の基盤となる機能)機能をもつファイルシステムを検討してるのならば、ZFSは選択すべきでなく、Btrfsが現実的な選択肢だ、ということになる。
ZFSがLinuxカーネルに取り込まれていないために、Linux上でZFSを使うと下記のような課題があることがその理由である。
Linuxカーネルの更新に追従してZFSも自力で更新しなければならない
実行時にLinuxカーネルに取り込まれるZFSモジュールのバイナリを再配布することにライセンス上の懸念があり、最終ユーザーが自分でZFSのビルドを行わなければならない
ZFSが必ずしもGNU/Linuxに最適化されていない
Linuxカーネルの更新に追従した更新の困難さ
Linuxカーネルとユーザーアプリケーションの間のAPI/ABIについては、開発統括のLinus Torvaldsが強い信念をもって後方互換性を維持している。一方、カーネル内部のAPI/ABIについてはしばしば互換性のない形で変更されている。もちろんLinuxカーネルの一部を構成している機能については、カーネルの新版がリリースされる際に全体として問題ないか確認される。したがって、カーネルの一部であるBtrfsについては、最新版のLinuxカーネルでも常に問題なく使えると考えて良い。一方、ZFSのような外部モジュールは、最終的にカーネルに組み込んで動作させる必要がある以上カーネルの内部API/ABIに依存しておりその変更の影響を受けるが、Linuxカーネル開発の変更管理において、外部モジュールへの影響は一切考慮されない。そのため、ある日突然昨日までビルドできていたモジュールがビルドできなくなる、ということが発生し得る。
ZFSではないが、最近類似のケースとしてnvidiaのモジュールで問題があった。nvidiaのグラフィックスカード用のドライバーはGPL非互換でLinuxカーネルに取り込まれていないので、ZFSと同じように自分でビルドする必要がある。私が使っているDebian GNU/Linux(以下Debian)では、DKMSというビルド支援のシステムがあり半自動でビルドができるため、ビルドそのものは難しいことではない。ところが、ある日Debianのカーネルのセキュリティーパッチを適用したところ、nvidiaのビルドが失敗し、起動後の画面がVGAにフォールバックしてしまったのである。調べたところ、ある内部API/ABIがGPLライセンスのモジュール専用に変更されたために、GPLライセンスではないnvidiaモジュールからそれらを呼び出すことができなくなり、ビルドが失敗したのであった。緊急回避策としてカーネルのバージョンを古いものに戻した上で、ソースの当該部分を自分で修正して対応するしかなかった。
Linuxカーネルはマイナーバージョン変更であってもZFSのような外部のモジュールのことは一切考慮せずに内部API/ABIを変更することがあるし、またDebian的にもnvidiaモジュールはnon-free扱い、つまりサポート対象外のas-isで提供されるものなので、バグレポートを上げても迅速な対応は期待できない。
DebianではZFSはcontrib扱いなので、non-free扱いのnvidiaと同じくサポート対象外である。前述のような事態が生じた際に自己解決できる技術力があるのならば自分の責任と判断で外部モジュールをビルドして使えば良いが、機能や安定性やパフォーマンス比較以前の問題として、一切のサポートがない……
にも関わらず、ファイルシステムのような極めてクリティカルなモジュールを広く一般に利用推奨するのは無責任……
も甚だしい。例えば「RAID5がBtrfsでは未だに実験的機能だがZFSでは安定して動くのでLinuxでもZFSを使おう」などという言明を信じた結果、私が最近nvidiaで経験したような事態に直面してシステムが起動できなくなったりしても自業自得なのだが、そういう可能性があることを知りながらZFSを薦めることはもはや技術者倫理で扱うべき課題であって、一人の技術士(情報工学)として看過できない。
例外は、ZFSをサポートしているUbuntuのようなOSの有料サポートを購入しているケースである。この場合は、Ubuntu提供元のCanonical社がOSの一部としてLinuxカーネルとの互換性も含めてZFSを(技術的には)サポートしてくれるだろうが、Canonical社から有料のUbuntuのサポートを購入する計画のない人には関係の無いことだ。
ZFSがLinuxカーネルの一部になることはあるのか?
結論から言うと、ZFSがLinuxカーネルの一部になることは今後もなさそうである。
ZFSの問題の一つは、現在のZFSが採用しているCDDLライセンスはLinuxカーネルが採用しているGPLと互換性がないということである。具体的には、もしZFSをLinuxカーネルに取り込んだとした場合に、Linuxカーネルの一部として行われるZFSの変更部分をGPLで保護できない、つまりその成果をクローズド・ソースの製品に組み込まれてしまってもそれを拒否できないし、別ファイルとして追加する限り、自由でもオープンでもないライセンスで機能追加をすることも可能なので、機能追加部分がLinuxカーネルにフィードバックされない可能性もある。
Linuxカーネル開発の成果を利用できるにもかかわらず、自分が行った成果は公開しないということが許されるのでは、互恵的にソフトウェアを発展させていくことはできない。Linus Torvaldsは、成果をフィードバックしない人をLinux開発者がサポートすることはない、と述べている。
ZFSがLinuxカーネルの一部になるためには、ZFSにGPLライセンスを適用可能になることが必要である。
ちなみに、互恵的にソフトウェアを発展させていくためにソースコードの公開や改変等の自由を著作権に基づいて義務付けるのがコピーレフトの考え方であり、その考え方に基づき作成されたのがGPLライセンスである。ところが、ソフトウェアの自由を守ることよりも、不自由な使われ方であっても自分の作ったソフトウェアが世の中に使われる機会の増えることのほうが大切という価値観を持つ人々がいる。私はこの考え方に全く与しない……
が、BSD系の開発者はソフトウェアの自由を守ることを軽視して、GPLのコンポーネントをOSから排除することに意義を見出しているらしい。
万が一Oracle社がZFSをGPLラインセンスでリリースすることがあったとして、かつOpenZFSの開発者たちが彼らの行った改変部分もGPLライセンスに変更することに合意することがあったとすれば、OpenZFSの成果物がLinuxカーネルに取り込まれる可能性も出てくるが、残念ながらOracle社にはZFSをGPLライセンスでリリースする意思が(おそらく)ないことに加え、BSD系の開発者がGPLライセンスを奇妙にも忌避していることを考えると、ZFSがLinuxカーネルに取り込まれる可能性は限りなく小さい。
OpenZFSは技術的にもLinuxに最適されていない
ZFSは、最初はSun社(現Oracle社)によってSolaris OSのために作られた。残念ながら、SolarisはLinuxやWindowsに比べて市場競争力を失っており、今後Oracle社がZFSを含むSolaris OSに継続的な投資をすることは期待できない。現行のSolaris 11.4がリリースされたのはもう5年も前のことである。その意味では、本家ZFS(Oracle ZFS)はSolarisとともに消えゆく運命と言える。既存のSolarisユーザーならばいざ知らず、多少の技術的優位性があるからといって、これから新規にSolaris OSとZFSを使い始めることは合理的判断とは言えない。
ZFSを含むSolaris OSはクローズドソースだが、幸いなことにOracle社に買収される前のSun Microsystems社はZFSを含むSolarisをCDDLライセンスのもとオープンソースとして公開していた。この公開されていた版を元にしたOpenZFSは、LinuxおよびFreeBSDを対象としてコミュニティーによる開発が継続している。現在Linux上でZFSを使う、といった場合、それはOpenZFSのことである。
Marc Merlinによれば、OpenZFSはLinux memory filesystemをうまく扱っておらずメモリを共有できないために大量のメモリを消費するという。これは、おそらく zfs doesn't mark its native kernel slabs as being reclaimable と関連している未解決の課題のようだ。また、CoWによる軽量コピーを実行する cp --reflink=always ……
もOpenZFSでは機能しないという。これは、おそらく COW cp (--reflink) doesn't work across different datasets: Invalid cross-device link #15345 と関連している未解決の課題のようだ。これらはMarcが指摘した10年前から未解決のままになっているらしい。それ以外にも、詳細な分析が Why not ZFS にある。結論として、OpenZFSはFreeBSDとLinuxの両方を対象としているために、あるいはLinuxカーネルが内部の独自機能についてGPL互換でない外部モジュールから使えないようにしているために、Linuxに特化した最適化や改善は行われにくい、あるいはあえて非効率な実装になっていることもあるようである。これらの課題が今後解決される可能性も低い。従って、技術的にもLinux上でのOpenZFSの利用は推奨できない。