🪨

minioがdockerイメージを配布しなくなったので新しいS3互換ストレージを探す

に公開

MinIO が Docker イメージ配布を停止して困った話

via https://gigazine.net/news/20251023-minio-stops-distributing-free-docker-images/
MinIO(みにお、みんあいおー)が公式の Docker イメージ配布を停止しました。

背景には収益化の難しさがあるとされており、商用サポートや有償契約への誘導を強める意図があったようです。

MinIO は非常に完成度の高い OSS ですが、このあおりを受けて、運用実験やローカルでS3互換ストレージを作るのが大変になったので代替となる S3 互換ストレージプロダクトを探すことにしました。

幸い、すでに詳細な調査をされている先駆者の方がいらっしゃいます。
https://light-of-moe.ddo.jp/~sakura/diary/?p=1931
考察が興味深いのでリンク先を読んでおくとよいです。

先駆者の方が紹介されていたS3互換ストレージのプロダクトは次の通り

私が紹介するのは

まず、MinIO のように Docker イメージで手軽にローカル展開できることを条件にします。
そう考えると

  • Cephのように分散ストレージベースのもの
  • Dockerイメージが提供されていないもの

これらを除外すると、候補は以下のようになります。

プロダクト名 Dockerイメージ
RustFS https://rustfs.com/en/download/?platform=docker
Garage https://hub.docker.com/r/dxflrs/garage
SeaweedFS https://github.com/seaweedfs/seaweedfs?tab=readme-ov-file#quick-start-for-s3-api-on-docker
s3gw https://s3gw.tech/#get-started

好きなのを選んでください。私はGarageかSeaweedFSにする予定です。

付録:compose.yaml

ひとまず、SeaweedFSを動かせるcompose.yaml付録として置いておきます。

マウント先は/mnt/hdd/seaweedfs/ です。先に作成しておいてください。

動作確認用コマンド

※EXTERNAL_IPはローカルで環境するなら127.0.0.1です

EXTERNAL_IP=YOUR-IP
docker-compose up -d

compose.yaml

services:
  master:
    image: chrislusf/seaweedfs
    container_name: seaweed-master
    network_mode: host
    command: master -ip.bind=${EXTERNAL_IP} -port=9333 -port.grpc=19333 -mdir=/data/master
    volumes:
      - /mnt/hdd/seaweedfs/master:/data/master
    restart: unless-stopped

  volume:
    image: chrislusf/seaweedfs
    container_name: seaweed-volume
    network_mode: host
    depends_on:
      - master
    command: volume -mserver=${EXTERNAL_IP}:9333 -publicUrl=${EXTERNAL_IP}:8080 -port=8080 -dir=/data/volume -max=1
    volumes:
      - /mnt/hdd/seaweedfs/volume:/data/volume
    restart: unless-stopped

  filer:
    image: chrislusf/seaweedfs
    container_name: seaweed-filer
    network_mode: host
    depends_on:
      - volume
    command: filer -master=${EXTERNAL_IP}:9333 -ip.bind=${EXTERNAL_IP} -port=8888 -port.grpc=18888 -defaultStoreDir=/data/filer
    volumes:
      - /mnt/hdd/seaweedfs/filer:/data/filer
    restart: unless-stopped

  s3:
    image: chrislusf/seaweedfs
    container_name: seaweed-s3
    network_mode: host
    depends_on:
      - filer
    environment:
      - AWS_ACCESS_KEY_ID=test
      - AWS_SECRET_ACCESS_KEY=testtest
    command: s3 -filer=${EXTERNAL_IP}:8888 -ip.bind=${EXTERNAL_IP} -port=8333
    restart: unless-stopped

操作感も MinIO に近く、ローカル用途であれば十分実用的です。

残念な点としては、マウント先が NFS 上では動作しない点です。

起動確認できたら、実行確認用Python3スクリプト使ってみましょう。

touch upload.txt download.txt
python seaweedfs_s3_test.py

seaweedfs_s3_test.py

import boto3
from botocore.client import Config

# ===== 設定 =====
ENDPOINT_URL = "http://127.0.0.1:8333"   # EXTERNAL_IP に置き換え
ACCESS_KEY = "test"
SECRET_KEY = "testtest"
REGION = "us-east-1"
BUCKET_NAME = "my-bucket"

UPLOAD_FILE = "upload.txt"
DOWNLOAD_FILE = "download.txt"
OBJECT_KEY = "sample/upload.txt"

# =================

# S3クライアント作成
s3 = boto3.client(
    "s3",
    endpoint_url=ENDPOINT_URL,
    aws_access_key_id=ACCESS_KEY,
    aws_secret_access_key=SECRET_KEY,
    region_name=REGION,
    config=Config(signature_version="s3v4"),
)

# バケット作成(存在しなければ)
def create_bucket():
    try:
        s3.create_bucket(Bucket=BUCKET_NAME)
        print(f"Bucket created: {BUCKET_NAME}")
    except s3.exceptions.BucketAlreadyOwnedByYou:
        print(f"Bucket already exists: {BUCKET_NAME}")

# ファイルアップロード
def upload_file():
    with open(UPLOAD_FILE, "w") as f:
        f.write("Hello SeaweedFS S3!\n")

    s3.upload_file(UPLOAD_FILE, BUCKET_NAME, OBJECT_KEY)
    print(f"Uploaded: {OBJECT_KEY}")

# ファイルダウンロード
def download_file():
    s3.download_file(BUCKET_NAME, OBJECT_KEY, DOWNLOAD_FILE)
    print(f"Downloaded to: {DOWNLOAD_FILE}")

# オブジェクト一覧
def list_objects():
    resp = s3.list_objects_v2(Bucket=BUCKET_NAME)
    print("Objects:")
    for obj in resp.get("Contents", []):
        print(f" - {obj['Key']} ({obj['Size']} bytes)")

if __name__ == "__main__":
    create_bucket()
    upload_file()
    list_objects()
    download_file()

output

Bucket created: my-bucket
Uploaded: sample/upload.txt
Objects:
 - sample/upload.txt (20 bytes)
Downloaded to: download.txt

使える様です。

上手く使えなかったらエラーメッセージをChatGPTにでも投げると解決策を提示してくれます。

MinIOの代替になれば幸いです。

アップルワールド Tech Blog

Discussion