Ubuntu Weekly Recipe

第893回Dockerでオブジェクトストレージ「SeaweedFS」構築する

近年、更新や書き換え頻度の少ない大容量データの保管、配信といった用途において、オブジェクトストレージの利用は当たり前となりました。オブジェクトストレージ上に置いたファイルを、CDN経由で配信するといった使い方も一般的です。

今回はセルフホストできるオープンソースのオブジェクトストレージであるSeaweedFSを紹介します。

SeaweedFSとは

クラウド上に構築された本番環境であれば、クラウドベンダーが提供するオブジェクトストレージを使うのが、対障害性やスケーラビリティ、コスト面からも最適でしょう。ですがオンプレミス環境や家庭内であれば、セルフホストできるオブジェクトストレージがほしくなります。またクラウドサービスは利用に費用がかかりますから、開発環境向けには無料で使えるスタブとして、APIに互換性のある代替プロダクトがあると便利です。

S3とAPI互換性のあるオブジェクトストレージとして、長らく人気だったのがMinIOです。本連載でも第655回で紹介しています。

MinIOはDockerコンテナをひとつ起動するだけで動かせる、非常にシンプルなオブジェクトストレージです。Webブラウザからオブジェクトを操作できるインターフェイスも同梱されており、開発用途では非常に便利でした。ところが2025年10月頃、MinIOはプロジェクトの方針を転換し、バイナリのリリースを停止しました。Dockerイメージの配布も行われなくなってしまったため、セキュリティアップデートに追従するためには、ユーザーは各自でソースコードをチェックアウトし、イメージをビルドしなくてはなりません。そのためMinIOは、もはや手軽に使えるとは言いづらくなってしまいました。

そんなMinIOからの移行先としては、RustFSGarageなど、いくつかのプロダクトが注目されています。今回紹介するSeaweedFSも、そんな中のひとつです。

SeaweedFSは、シンプルでスケーラビリティの高い分散ファイルシステムです。もともとはFacebookのHaystack論文を実装することから始まったプロダクトです。実際のファイルを管理するボリュームサーバーと、そのボリュームサーバーを管理するマスターサーバーに分離した構成が特徴で、これによって高い高速性を実現していると謳っています。

SeaweedFSの構成

SeaweedFSは、機能ごとに分かれた設計となっています。

機能 用途 必須か
master クラスタ全体の管理。クライアントに対するボリュームサーバーの割り当てなど。 必須
volume 実際のデータの格納先。 必須
filer オブジェクトストレージ上に、従来のファイルシステム同等のアクセスを提供。 推奨(実質必須)
s3/webdavなど S3互換APIやWevDAVといったアクセス手段を提供。 オプション

基本的にmasterとvolumeさえあれば、オブジェクトストレージとして動作します。しかしfilerなしでの運用は非現実的でしょう(詳しくは後述⁠⁠。SeaweedFSでは、fidというIDベースでオブジェクトにアクセスしなくてはなりません。ですがfilerを動かせば従来のファイルシステムのように、ファイル名でアクセス可能になるためです。またオブジェクトストレージである以上、S3互換のAPIによるアクセスがしたいケースも多いでしょう。S3互換APIの利用にも、filerの存在が前提となっています。

Composeの設定と起動

今回は必須のmaster、volumeにくわえ、filerと、S3互換APIでのアクセスを可能にするs3を動かします。Docker Composeを使って一気に起動してしまいましょう。以下のcompose.yamlを用意してください。

見ての通り、コンテナイメージはすべて同一のものを利用します。というのもSeaweedFSの機能はweedという単一のコマンドとして実装されており、サブコマンドによって使い分けるようになっているためです。これはコンテナのエントリーポイントに指定されているシェルスクリプトに、commandで指定したパラメータを渡すことで実現されています。

services:
  master:
    image: chrislusf/seaweedfs:4.03
    ports:
      - 9333:9333
      - 19333:19333
    command: 'master -ip=master -ip.bind=0.0.0.0'
    volumes:
      - master_data:/data
  volume:
    image: chrislusf/seaweedfs:4.03
    ports:
      - 8080:8080
      - 18080:18080
    command: 'volume -ip=volume -master="master:9333" -ip.bind=0.0.0.0 -port=8080'
    volumes:
      - volume_data:/data
    depends_on:
      - master
  filer:
    image: chrislusf/seaweedfs:4.03
    ports:
      - 8888:8888
      - 18888:18888
    command: 'filer -ip=filer -master="master:9333" -ip.bind=0.0.0.0'
    volumes:
      - filer_data:/data
    tty: true
    stdin_open: true
    depends_on:
      - master
      - volume
  s3:
    image: chrislusf/seaweedfs:4.03
    ports:
      - 8333:8333
    command: 's3 -filer="filer:8888" -ip.bind=0.0.0.0'
    depends_on:
      - master
      - volume
      - filer

volumes:
  master_data:
  volume_data:
  filer_data:

以下のコマンドでコンテナ一式を起動します。

$ sudo docker compose up -d

API経由でSeaweedFSにアクセスしてみる

手始めに、SeaweedFSのAPIを使ったアクセスを体験してみましょう。まずmasterコンテナに対し、新しいファイルをアップロードするため、IDの割り当てを要求します。masterコンテナは9333番ポートでAPIを公開しています。

$ curl http://localhost:9333/dir/assign
{"fid":"3,0265da323b","url":"volume:8080","publicUrl":"volume:8080","count":1}

レスポンスJSON内のfid「3,0265da323b」を控えておいてください。

続いてhello.txtというテキストファイルを作成します。これを今度はvolumeコンテナの8080番ポートに対してアップロードします。アップロード先として先ほど控えたidを指定してください。

$ echo hello > hello.txt

$ curl -F file=@$(pwd)/hello.txt http://localhost:8080/3,0265da323b
{"name":"hello.txt","size":6,"eTag":"353dd8be","mime":"text/plain"}

アップロードした先のURLに対してGETしてみましょう。

$ curl http://localhost:8080/3,0265da323b
hello

アップロードしたhello.txtの中身が表示されました。

S3互換APIでSeaweedFSにアクセスする

前節の解説でわかるように、SeaweedFSのAPIを直接利用する場合、ファイル名ではなくfidを使う必要があります。これは人間にとって、お世辞にも直感的ではありません。というわけでオブジェクトストレージのデファクトスタンダードである、Amazon S3互換APIを使いましょう。ここでは定番のAWS CLIを例に解説します。インストールにはSnapを使うのが手軽です。

$ sudo snap install aws-cli --classic

AWSでは、アクセスキーとシークレットを使って認証します。ですがSeaweedFSはデフォルトで認証が設定されていません。とはいえ認証情報が空のままでは、awsコマンドが動かないため、アクセスキーとシークレットに適当な文字列を設定してください。ここでは環境変数AWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYを使っていますが、aws configureを実行してプロファイルを作成しても構いません。

$ export AWS_ACCESS_KEY_ID="TEST_KEY"
$ export AWS_SECRET_ACCESS_KEY="TEST_SECRET"

awsコマンドは、--endpoint-urlオプションでAPIのエンドポイントを指定できます。ここにs3コンテナが公開している8333番ポートを指定してください。これでs3サブコマンドがそのまま動作します。例えば以下は、my-bucketというバケットを作成する例です。

$ aws --endpoint-url http://localhost:8333 s3 mb s3://my-bucket
make_bucket: my-bucket

先ほど作成したhello.txtを、s3 cpコマンドでmy-bucketにアップロードします。

$ aws --endpoint-url http://localhost:8333 s3 cp hello.txt s3://my-bucket/
upload: ./hello.txt to s3://my-bucket/hello.txt 

s3 lsコマンドで、ファイルが存在することを確認してみましょう。

$ aws --endpoint-url http://localhost:8333 s3 ls s3://my-bucket/
2025-12-17 01:10:10          6 hello.txt
図1 APIに互換性があるため、S3に対応していればクライアントを選ばない。例えばNextcloudの外部ストレージにもできる
図2 NextcloudにSeaweedFSを接続し、写真をアップロードしてみた例

FUSEでUbuntuにマウントする

awsコマンドによるオブジェクトの操作は、慣れないとやりづらいかもしれません。SeaweedFSは、FUSEを使ってディレクトリツリーに直接マウントできます。ここではホストマシンに[1]ファイルシステムとしてマウントしてみましょう。

マウントにもweedコマンドを使うため、以下のコマンドでホストにインストールしてください。

$ wget https://github.com/seaweedfs/seaweedfs/releases/download/4.03/linux_amd64.tar.gz
$ tar xf linux_amd64.tar.gz 
$ sudo cp weed /usr/local/bin

/mnt/weedディレクトリを作り、ここにマウントします。

$ sudo mkdir /mnt/weed
$ sudo weed mount -dir=/mnt/weed -filer=localhost:8888 -volumeServerAccess filerProxy -map.uid=$(id -u):0 -map.gid=$(id -g):0

ポイントは2つです。⁠-volumeServerAccess filerProxy」を指定するところと、⁠-map.uid」⁠-map.gid」で、自分のUIDとGIDをfiler上のrootと対応づけるところです。

マウントしたディレクトリにファイルを書き込もうとした際、クライアントはvolumeServerAccessに指定された方法で、volumeサーバーと通信します。デフォルト値はdirectで、この場合クライアントは、masterから通知されたvolumeサーバーのIPアドレスに直接アクセスしようとします。ところが今回の例では、volumeサーバーとmasterサーバーがDockerのコンテナネットワーク内で通信している都合上、⁠volume」というサービス名が通知されてしまいます。コンテナネットワークの外にいるホストは、このサービス名を名前解決できず、ファイルアクセスに失敗してしまいます。ですがfilerProxyを指定しておくと、volumeサーバーへのアップロードを、filerがプロキシしてくれるのです[2]

S3クライアントを使ってファイルをアップロードすると、filer上でそのファイルのオーナーはrootになります。そのためそのままマウントすると、ファイルに権限がなく、読み書きができません。⁠-map.uid」⁠-map.gid」にそれぞれ「ローカルのUID/GID:filer上のUID/GID」を指定することで、ローカルのユーザーにマッピングできます。

weed mountは、デフォルトではフォアグラウンドで動作します。マウントが完了したら別のターミナルを立ち上げて、/mnt/weedの中を見てみましょう。

$ ls /mnt/weed/
buckets

$ ls /mnt/weed/buckets/
my-bucket

$ ls -l /mnt/weed/buckets/my-bucket/
total 1
-rw-rw---- 1 mizuno mizuno 6 Dec 17 01:10 hello.txt

bucketsディレクトリの中に、先ほど作成したバケット名のディレクトリがあります。そしてその中に、アップロードしたhello.txtの存在が確認できました。オーナーが自分になっているため、catコマンドで中身を読むことも可能です。

$ cat /mnt/weed/buckets/my-bucket/hello.txt
hello
図3 別のUbuntuデスクトップからFUSEマウントして、ファイラーからストレージ内を見てみた。問題なくファイルの読み書きも可能

今回は、MinIOに代わるセルフホスト可能なオブジェクトストレージの選択肢として、SeaweedFSを紹介しました。

SeaweedFSはMaster、Volume、Filerのように役割が明確に分かれているため、将来的にデータ量が増大した際にも、柔軟にスケールアウトできるという強みを持っています。S3互換APIが使用できるため、NextcloudのようなWebアプリケーションのバックエンドとして利用しても便利です。さらにFUSEマウントによって、Ubuntu上の通常のディレクトリとして扱える点は、既存のアプリケーションやスクリプトをそのまま流用したい場合に大きなアドバンテージとなるでしょう。MinIOと比較するとやや複雑な構成となっていますが、動かすだけであれば、見ての通りComposeで一発です。

ローカル環境でセルフホストできるオブジェクトストレージをお探しの場合は、SeaweedFSを試してみてください。

おすすめ記事

記事・ニュース一覧