背景と概要

ここ 1 ヶ月ほど、ブロックチェーン Chia のファーミング [1] を試している。Chia は Proof of Space and Time (PoST) というコンセンサスアルゴリズムを用いたブロックチェーンだ。このコンセンサスアルゴリズムでは、Proof of Work (PoW) におけるハッシュ計算にあたる作業を事前に大量に行い plot と呼ばれるデータを作成し、実際のブロック検証(ファーミング)時には作成した plot 中から正解に近いデータを提出するという形で検証が行われる。plot はいわゆるレインボーテーブルのようなものだ。

plot の作成にはそれなりに計算負荷と時間がかかるが、一度作った plot を使ってファーミングを行うのはほぼ負荷なく行うことができるため、環境にやさしいとされている。

当然ながら、plot を多く作成すればするほど、その plot に含まれるデータを使ってブロックを検証できる機会が多くなるため、収益を上げるためには多くの plot を作り、それを保持することが重要になる。ここで問題になるのは HDD の管理だ。作った plot で既存の HDD が埋まるたびに買い足していく(現在、1 日あたり 8 plot ほど作成できている。これは約 800 GB に相当するので、10日で8TBの HDD が埋まる計算になる)ことになる。

plot を作り続けるたびに物理的に増え続ける HDD をいかに容易に管理するかが問題となった。これを検討した結果、MergerFS が適していたのでその概要と使い方を説明する。

要件

Chia における plot 保存用 ストレージ に求められることとして、以下の要件を検討した結果、MergerFS を使うことにした。

  1. 複数の物理的なストレージを束ねて1つのストレージとして管理したい。
    HDD を買い足して接続するたびに、その HDD を plot の読み取り先や作成先として指定するのは手間だ。特に、plot の作成が大変で、現在のバージョンでは plot 作成の一時停止を行うことができない(中止すると作成中の plot が最初から作り直しになってしまう)ため、plot の作成完了を待ってから書き込み先を変更すればならず、厄介だ。

  2. 障害が起こったときの影響範囲を局所化したい。
    例えば RAID-0 では複数のHDDを束ねることができるが、1つのHDDが壊れただけで全てのデータが読み取れなくなってしまう。これで全ての plot が壊れるとさすがにもったいないので、もし HDD が壊れても、その HDD にあるデータの損害だけですませたい。

  3. 一方で、容量の効率を重視したいので、ミラーやパリティによる耐障害性の確保はしたくない。

  4. 簡単にストレージを追加したい。
    貴重な収益機会を失いたくないため、新しいストレージを追加する場合でも、手軽に、かつダウンタイムをほぼ無くストレージを追加したい。

  5. ** NFS で公開して他のマシンから rsync で plot をコピーできるようにしたい。**
    複数台の Linux マシンで plot を生成しているので、各マシンからはストレージを NFS でマウントして、生成した plot を rsync できるようにしたい。(これは要件としてあげるまでもないと考えていたが、結果的に MergerFS の利用を決定づけるものになった。後述する。)

MergerFS

MergerFS はこの用途に最適だった。指定された複数のディレクトリを結合したビューを持つファイルシステムを作成するものだ。例えば /mnt/a/mnt/b を結合して /mnt/x というファイルシステムを作ったとする。この時、/mnt/a/a.txt/mnt/b/b.txt という 2 つのファイルがあったとすれば、これらは/mnt/x/a.txt/mnt/x/b.txt のように見える。サブディレクトリがあっても問題なく動き、また書き込みも行えて、結合したディレクトリに書き込みを行うと、結合元のディレクトリのいずれかに実体が書き込まれる。

ストレージが壊れた場合でも、MergerFS が提供しているのはただの結合したビューでしかないため、壊れたストレージに存在したファイルにアクセスできなくなるだけですむ。また、容量的なオーバーヘッドもない。

$ cd /mnt/merged # /mnt/a と /mnt/b をマージしたディレクトリを /mnt/merged とする
$ sudo xattr -w user.mergerfs.branches '+</mnt/c' ./.mergerfs

として、マージされたディレクトリのルートに存在する仮想的なファイルに対して拡張属性をセットすることでオンラインでのディスクの追加や削除ができる。

似たような、複数のディレクトリを束ねたビューを提供するファイルシステムとして、古くから mhddfs がある。MergerFS が優れている点はいくつかあるが、個人的に大きかった点を2点上げると、1つは上述したようにオンラインでのディスク追加や削除ができることだ。もう1つは、束ねたディレクトリに対して NFS 越しに rsync でファイルをコピーしても問題が起こらないことだ。(=要件5)

mhddfs で作成したディレクトリを NFS で公開して、そこに対して rsync を行うと、ESTALL が発生し正しくファイルをコピーできないことがある。この問題[2]は、正確には mhddfs ではなく fuse の問題であるのだが、同じく fuse でマウントされる MergerFS はこの問題を解決するためのオプションが用意されている。そこで、最終的に MergerFS を利用することにした。

MergerFS のインストールと使い方

Debian 10 (buster) の環境にインストールした。まだ apt によるインストールはできないが、MergerFS のリリースページには .deb ファイルが用意されているのでこれを利用できる。

$ curl -LO https://github.com/trapexit/mergerfs/releases/download/2.32.4/mergerfs_2.32.4.debian-buster_amd64.deb # バージョン、ディストリビューションとアーキテクチャは適宜読み替えること
$ sudo dpkg -i ./mergerfs_2.32.4.debian-buster_amd64.deb

以上でインストールができる。インストールした後は、以下のようにマウントする。

$ sudo mount -t fuse.mergerfs -o defaults,allow_other,use_ino,cache.files=partial,dropcacheonclose=true,category.create=mfs /mnt/a:/mnt:b /mnt/merged

以上で、/mnt/a/mnt/b' をマージしたディレクトリが /mnt/merged にマウントされる。オプションは少し長いが、とりあえず default,allow_other を付けておけば動作するが、公式のドキュメント BASIC SETUP の "You need mmap" にならった。

NFS を通して公開したい場合に不都合無く動作させるためには、もう少しオプションを足す必要がある。NFS clients returning ESTALE / Stale file handle のところに書いてあるよう、noforget,use_ino,inodecalc=path-hash のオプションを加える。以下のようになる。

$ sudo mount -t fuse.mergerfs -o defaults,allow_other,use_ino,cache.files=partial,dropcacheonclose=true,category.create=mfs,nonempty,noforget,inodecalc=path-hash /mnt/a:/mnt:b /mnt/merged

起動時に自動でマウントするためには

/mnt/a:/mnt/b       /mnt/merged      fuse.mergerfs   defaults,allow_other,use_ino,cache.files=partial,dropcacheonclose=true,category.create=mfs,nonempty,noforget,inodecalc=path-hash      0       0

のような行を /etc/fstab に加えれば良い。

まとめ

本記事では、Chia の plot を保存するために、複数台のストレージを、MergerFS を使って束ねると管理しやすいことを説明し、その設定方法について述べた。現在のところ上手く動いているのでChia の ファーミングを行っている人は検討すると良いかもしれない。Chia のせいでストレージが手に入りずらくなっているのはマジでクソ。


  1. Bitcoin とか Ethereum のような PoW ブロックチェーンにおけるマイニングのようなこと。Chia ではマイニングではなくファーミングと呼ぶ。 ↩︎

  2. これは、 rsync がまず一時ファイルを作成してファイルをコピーし、コピーが完了した後にファイルをリネームする挙動にある。mhddfs でマウントしたディレクトリを NFS で公開すると、リネーム前後で inode 番号が変わってしまうことがあるようだ。rsync はリネーム時に inode 番号が変わらないことを期待しているためエラーが起こってしまう。 ↩︎