ZeroFS
ZeroFS で RustFS のバケットを NFS 経由の POSIX ファイルシステムとして提供します。
このガイドでは、S3 バケットを POSIX ファイルシステムとして提供するログ構造化ファイルシステムである ZeroFS を、オブジェクトストレージのバックエンドとして RustFS に接続します。LSM ツリーのルートを RustFS のバケットに置いて ZeroFS を実行し、Linux ホストから NFS でファイルシステムをマウントし、マウント経由でファイルを書き込み、ZeroFS を再起動してもバケット内のオブジェクトとして残ることを確認します。手順は ZeroFS v2.3.5(ghcr.io/barre/zerofs)と rustfs/rustfs-x86-musl:v2.3.1 の組み合わせで確認済みです。
Docker と、NFS クライアント(nfs-common)を備えた Linux ホストが必要です。このデプロイはローカルでの連携テスト用であり、本番向けではありません。
アーキテクチャ
ZeroFS は、ファイルシステム操作をバケット内の LSM キーバリューツリーへ変換します。書き込みは先行書き込みログに入り、SST セグメントへフラッシュされ、コンパクションはバックグラウンドで実行されます。クライアントからは、NFS(または 9P/NBD)経由の通常のディレクトリツリーに見えます。
設定を生成する
接続用のプレースホルダーをすべて置き換えて、バケットと設定ファイルを作成します。
rc mb rustfs/zerofs-demo
docker run --rm -u root:root -v /opt/zerofs:/cfg -w /cfg \
--entrypoint /bin/sh ghcr.io/barre/zerofs:latest -c "zerofs init"続けて zerofs.toml を編集し、storage セクションと AWS セクションが RustFS を指すようにします。
[cache]
dir = "/cache"
disk_size_gb = 5.0
[storage]
url = "s3://zerofs-demo/zerofs-data"
encryption_password = "${ZEROFS_PASSWORD}"
[servers.nfs]
addresses = ["0.0.0.0:2049"]
[servers.rpc]
addresses = ["0.0.0.0:7000"]
[aws]
access_key_id = "${AWS_ACCESS_KEY_ID}"
secret_access_key = "${AWS_SECRET_ACCESS_KEY}"
endpoint = "http://<your-rustfs-endpoint>:9000"
default_region = "us-east-1"
allow_http = "true"endpoint と allow_http の組み合わせにより、ZeroFS の接続先が AWS から RustFS へ切り替わります。カスタムエンドポイントではパススタイルアクセスが使われるため、追加のオプションは不要です。
ZeroFS を実行する
設定ファイル、認証情報、書き込み可能なキャッシュディレクトリを指定してサーバーを実行します(イメージは UID 1001 で動作します)。
mkdir -p /opt/zerofs/cache && chown -R 1001:1001 /opt/zerofs/cache
docker run -d --name zerofs --network oo-rustfs_default -p 2049:2049 \
-e ZEROFS_PASSWORD=<encryption-password> \
-e AWS_ACCESS_KEY_ID=<your-access-key> \
-e AWS_SECRET_ACCESS_KEY=<your-secret-key> \
-v /opt/zerofs/zerofs.toml:/zerofs.toml:ro \
-v /opt/zerofs/cache:/cache \
ghcr.io/barre/zerofs:latest run -c /zerofs.tomlINFO zerofs::nfs: NFS server listening on 0.0.0.0:2049NFS でマウントする
ホストで、ZeroFS が推奨するオプションを使ってエクスポートをマウントします。
apt-get install -y nfs-common
mkdir -p /mnt/zerofs
mount -t nfs -o async,nolock,rsize=1048576,wsize=1048576,tcp,port=2049,mountport=2049,hard \
127.0.0.1:/ /mnt/zerofsマウントは通常の POSIX ファイルシステムです。
echo "written via zerofs nfs mount on rustfs" > /mnt/zerofs/hello.txt
dd if=/dev/urandom of=/mnt/zerofs/data/inner/blob.bin bs=1M count=8永続化と RustFS 内のオブジェクトを確認する
保留中の書き込みをバケットへ強制的に送り、ZeroFS を再起動して再マウントします。
docker exec zerofs zerofs flush -c /zerofs.toml
umount /mnt/zerofs
docker restart zerofs
mount -t nfs -o async,nolock,rsize=1048576,wsize=1048576,tcp,port=2049,mountport=2049,hard \
127.0.0.1:/ /mnt/zerofs再起動前に書き込んだ内容は、そのまま残っています。
cat /mnt/zerofs/hello.txt
sha1sum /mnt/zerofs/data/inner/blob.binwritten via zerofs nfs mount on rustfs
645faaefe499f9ebda8edceeeb1a6261999aece7 /mnt/zerofs/data/inner/blob.binバケットを一覧し、ファイルの実体がどこにあるかを確認します。
rc ls rustfs/zerofs-demo/ -r | head -4zerofs-data/compacted/01M40Q1RA0X3AVG76VC0SBYE58.sst
zerofs-data/manifest/0000000000000000.json
zerofs-data/wal/00000000000000000001.sstファイルは 1 対 1 では保存されません。LSM ツリー内のキーと値として保存されるため、ZeroFS はバケットをファイルシステムとして提供できます。

停止またはリセットする
バケット内のオブジェクトを残したまま、デモを解体します。
umount /mnt/zerofs
docker rm -f zerofs保存データを削除します(ファイルシステム自体が消去されます)。
rc rm rustfs/zerofs-demo/ --recursive --forceトラブルシューティング
[servers.webui] で Failed to parse config file: missing field uid になる
ZeroFS v2.3.5 では、[servers.webui] セクションがある場合に uid と gid が必要です。uid = 1000 と gid = 1000 を追加するか、Web UI が不要ならセクションごと削除します。
キャッシュディレクトリの作成時に Permission denied になる
コンテナは UID 1001 で動作します。キャッシュボリュームをマウントしたあと、コンテナを起動する前に chown -R 1001:1001 <cache-dir> を実行します。
マウントが失敗する、またはハングする
NFS クライアントパッケージ(nfs-common)がインストールされていること、およびクライアントから到達できるネットワークで ZeroFS が 0.0.0.0:2049 を待ち受けていることを確認します。port=2049,mountport=2049 が重要なのは、ZeroFS が mountd のトラフィックを同じポートで提供するためです。
コンテナのクラッシュ後に書き込みが消える
ZeroFS は書き込みをバッファし、定期的にフラッシュします。デモでは docker exec zerofs zerofs flush -c /zerofs.toml を実行します。本番で厳密な耐久性が必要なクライアントには、fsync、または fsync が安定したストレージへの書き込み完了を待つ 9P マウントを推奨します。
次のステップ
- ほかの ZeroFS バックエンドを採用する前に、S3 互換性に関する注意を確認します。
- アクセスキーの管理で、本番用の専用認証情報を作成します。
- キャッシュサイズ、9P/NBD の提供、同じバケット上の高可用性レプリケーションについては、ZeroFS の設定ガイドに従います。