0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

一人アドカレ 6 日目: GarageHQの紹介

Posted at

イントロ

お家サーバーのメリットといえばHDDやSSDを好き放題増設できることですよね?!
色々なOSSを立ち上げる際にも、S3互換ストレージがあるととても便利。

ということでご紹介するのが、GarageHQ

なぜGarageHQなのかというと、minioの件があったからです・・・
その件については別記事でも書いてる(というかGarageHQの紹介をすでにしている)ので、そちらを参照してください。

とはいえ、RustFSはBeta版だったので、他のものを探しました。
SeaweedFSやCephなども検討しましたが、セットアップが簡単(と言われていたの)でGarageHQを選択。

GarageHQの使い方

環境情報
$ docker version
Client: Docker Engine - Community
 Version:           29.1.3
 API version:       1.52
 Go version:        go1.25.5
 Git commit:        f52814d
 Built:             Fri Dec 12 14:49:51 2025
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          29.1.3
  API version:      1.52 (minimum version 1.44)
  Go version:       go1.25.5
  Git commit:       fbf3ed2
  Built:            Fri Dec 12 14:49:51 2025
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v2.2.0
  GitCommit:        1c4457e00facac03ce1d75f7b6777a7a851e5c41
 runc:
  Version:          1.3.4
  GitCommit:        v1.3.4-0-gd6d73eb8
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

前提条件

  • Docker がインストールされていること
  • Traefik が導入されていること(リバースプロキシサーバー)
  • HDDやSSDがマウントされていること(fstabなどの設定は省きます)
  • 複数のノードやHDDを準備すること(シングルノードでも動きますが、GarageHQの推奨は3Way以上です)
    • 今回は1サーバーに2つのノードと2つのストレージを割り当てる例を紹介します

手順

  1. GarageHQ用のディレクトリを準備してください。

    GarageHQの推奨では、metaデータはSSD、データ本体はHDDに保存することが推奨されています。
    ここでは例として、ローカルストレージがSSD、外付けHDDが/mnt/garage1/mnt/garage2にマウントされているとします。

  2. compose.ymlを作成します。

    domain.tldは自分のドメインに置き換えてください。https://s3.domain.tldでS3互換ストレージ、https://*.web.domain.tldで公開したS3バケット、https://admin.domain.tldで管理APIにアクセスできるようにしています。
    (管理APIは用意しましたが、今のところ使うことはなかったです)

    compose.yml
    services:
        garage-node1:
        image: dxflrs/garage:v2.1.0
        container_name: garage-node1
        restart: always
        ports:
            - 3901:3901 # rpc port(node sync)
        expose:
            - 3900
            - 3902
            - 3903
        environment:
            GARAGE_RPC_SECRET: ${GARAGE_RPC_SECRET}
            GARAGE_ADMIN_TOKEN: ${GARAGE_ADMIN_TOKEN}
            GARAGE_METRICS_TOKEN: ${GARAGE_METRICS_TOKEN}
        volumes:
            - ./garage-node1.toml:/etc/garage.toml
            - ./storage1-meta:/var/lib/garage/meta
            - /mnt/garage1:/var/lib/garage/data
        labels:
            traefik.enable: true
            traefik.docker.network: traefik-network
    
            traefik.http.services.garage-s3.loadbalancer.server.port: 3900
            traefik.http.routers.garage-s3.rule: Host(`s3.domain.tld`) || HostRegexp(`^.+\.s3\.domain\.tld$`)
            traefik.http.routers.garage-s3.service: garage-s3
            traefik.http.routers.garage-s3.entrypoints: websecure
            traefik.http.routers.garage-s3.tls: true
            traefik.http.routers.garage-s3.tls.certResolver: cloudflare
            traefik.http.routers.garage-s3.tls.domains[0].main: "*.s3.domain.tld"
            traefik.http.routers.garage-s3.tls.domains[0].sans: "s3.domain.tld"
    
            traefik.http.services.garage-web.loadbalancer.server.port: 3902
            traefik.http.routers.garage-web.rule: HostRegexp(`^.+\.web\.domain\.tld$`)
            traefik.http.routers.garage-web.service: garage-web
            traefik.http.routers.garage-web.entrypoints: websecure
            traefik.http.routers.garage-web.tls: true
            traefik.http.routers.garage-web.tls.certResolver: cloudflare
            traefik.http.routers.garage-web.tls.domains[0].main: "*.web.domain.tld"
            traefik.http.routers.garage-web.tls.domains[0].sans: web.domain.tld
    
            traefik.http.services.garage-admin.loadbalancer.server.port: 3903
            traefik.http.routers.garage-admin.rule: Host(`admin.domain.tld`)
            traefik.http.routers.garage-admin.service: garage-admin
            traefik.http.routers.garage-admin.entrypoints: websecure
            traefik.http.routers.garage-admin.tls: true
            traefik.http.routers.garage-admin.tls.certResolver: cloudflare
    
            traefik.http.middlewares.garage-s3-cors.headers.accessControlAllowOriginList: "*"
            traefik.http.middlewares.garage-s3-cors.headers.accessControlAllowMethods: "GET,POST,PUT,DELETE,OPTIONS"
            traefik.http.middlewares.garage-s3-cors.headers.accessControlAllowHeaders: "*"
            traefik.http.middlewares.garage-s3-cors.headers.accessControlAllowCredentials: "true"
            traefik.http.middlewares.garage-s3-cors.headers.accessControlMaxAge: "3600"
    
            traefik.http.routers.garage-s3.middlewares: garage-s3-cors@docker
        networks:
            - traefik-network
        garage-node2:
        image: dxflrs/garage:v2.1.0
        container_name: garage-node2
        restart: always
        ports:
            - 3911:3911 # rpc port(node sync)
        expose:
            - 3900
            - 3902
            - 3903
        environment:
            GARAGE_RPC_SECRET: ${GARAGE_RPC_SECRET}
            GARAGE_ADMIN_TOKEN: ${GARAGE_ADMIN_TOKEN}
            GARAGE_METRICS_TOKEN: ${GARAGE_METRICS_TOKEN}
        volumes:
            - ./garage-node2.toml:/etc/garage.toml
            - ./storage2-meta:/var/lib/garage/meta
            - /mnt/garage2:/var/lib/garage/data
        labels:
            traefik.enable: true
            traefik.docker.network: traefik-network
    
            traefik.http.services.garage-s3.loadbalancer.server.port: 3900
            traefik.http.routers.garage-s3.rule: Host(`s3.domain.tld`) || HostRegexp(`^.+\.s3\.domain\.tld$`)
            traefik.http.routers.garage-s3.service: garage-s3
            traefik.http.routers.garage-s3.entrypoints: websecure
            traefik.http.routers.garage-s3.tls: true
            traefik.http.routers.garage-s3.tls.certResolver: cloudflare
            traefik.http.routers.garage-s3.tls.domains[0].main: "*.s3.domain.tld"
            traefik.http.routers.garage-s3.tls.domains[0].sans: "s3.domain.tld"
    
            traefik.http.services.garage-web.loadbalancer.server.port: 3902
            traefik.http.routers.garage-web.rule: HostRegexp(`^.+\.web\.domain\.tld$`)
            traefik.http.routers.garage-web.service: garage-web
            traefik.http.routers.garage-web.entrypoints: websecure
            traefik.http.routers.garage-web.tls: true
            traefik.http.routers.garage-web.tls.certResolver: cloudflare
            traefik.http.routers.garage-web.tls.domains[0].main: "*.web.domain.tld"
            traefik.http.routers.garage-web.tls.domains[0].sans: web.domain.tld
    
            traefik.http.services.garage-admin.loadbalancer.server.port: 3903
            traefik.http.routers.garage-admin.rule: Host(`admin.domain.tld`)
            traefik.http.routers.garage-admin.service: garage-admin
            traefik.http.routers.garage-admin.entrypoints: websecure
            traefik.http.routers.garage-admin.tls: true
            traefik.http.routers.garage-admin.tls.certResolver: cloudflare
    
            traefik.http.routers.garage-s3.middlewares: garage-s3-cors@docker
        networks:
            - traefik-network
    
    networks:
        traefik-network:
        external: true
    
  3. .envファイルを作成します。

    GARAGE_RPC_SECRET="" # openssl rand -hex 32 で生成したランダムな文字列を設定, 各ノードで同じ値にすること
    GARAGE_ADMIN_TOKEN="" # openssl rand -base64 32 で生成したランダムな文字列を設定
    GARAGE_METRICS_TOKEN="" # openssl rand -base64 32 で生成したランダムな文字列を設定, ただしPrometheusでメトリクスを取得する際にトークンを使う場合のみ必要なだけ
    
  4. 各ノードの設定ファイルを作成します。

    ここでは同じサーバー内に2つのノードを立てる例を紹介しますが、別々のサーバーに立てる場合はrpc_public_addrをそれぞれのサーバーのIPアドレスに変更してください。

    garage-node1.toml
    # メタデータとデータの保存場所
    metadata_dir = "/var/lib/garage/meta"
    data_dir = "/var/lib/garage/data"
    db_engine = "sqlite" # lmdbがデフォルトだが、停電などの不正なシャットダウンで壊れやすいためsqliteに変更
    # 自動スナップショット(推奨)
    metadata_auto_snapshot_interval = "6h"
    # クラスター設定
    replication_factor = 2 # 2Wayレプリケーション, 3Way以上が推奨
    compression_level = 2
    # RPC通信設定(ノード間通信)
    rpc_bind_addr = "[::]:3901"
    rpc_public_addr = "100.xxx.xxx.xxx:3901" # 自分のサーバーのIPアドレスに変更(Public用なら公開IP, Tailscale内での通信を想定しているならTailscaleのIPアドレス)
    # 他のノードとの接続設定
    bootstrap_peers = [] # コマンドで追加する
    # S3 API設定
    [s3_api]
    api_bind_addr = "[::]:3900"
    s3_region = "garage"
    root_domain = ".s3.domain.tld"
    # S3 Web(使用しない場合はコメントアウト可)
    [s3_web]
    bind_addr = "[::]:3902"
    root_domain = ".web.domain.tld"
    # 管理API設定
    [admin]
    api_bind_addr = "[::]:3903"
    
    garage-node2.toml
    # メタデータとデータの保存場所
    metadata_dir = "/var/lib/garage/meta"
    data_dir = "/var/lib/garage/data"
    db_engine = "sqlite" # lmdbがデフォルトだが、停電などの不正なシャットダウンで壊れやすいためsqliteに変更
    # 自動スナップショット(推奨)
    metadata_auto_snapshot_interval = "6h"
    # クラスター設定
    replication_factor = 2 # 2Wayレプリケーション, 3Way以上が推奨
    compression_level = 2
    # RPC通信設定(ノード間通信)
    rpc_bind_addr = "[::]:3911"
    rpc_public_addr = "100.xxx.xxx.xxx:3911" # 自分のサーバーのIPアドレスに変更(Public用なら公開IP, Tailscale内での通信を想定しているならTailscaleのIPアドレス)
    # 他のノードとの接続設定
    bootstrap_peers = [] # コマンドで追加する
    # S3 API設定
    [s3_api]
    api_bind_addr = "[::]:3900"
    s3_region = "garage"
    root_domain = ".s3.domain.tld"
    # S3 Web(使用しない場合はコメントアウト可)
    [s3_web]
    bind_addr = "[::]:3902"
    root_domain = ".web.domain.tld"
    # 管理API設定
    [admin]
    api_bind_addr = "[::]:3903"
    
  5. GarageHQの起動

    下記コマンドを実行したらhttps://s3.domain.tldにアクセスできるようになります。

    docker compose up -d
    
  6. ノード間の接続

    この辺が少し面倒なところです。当然ながら適当にノード2つを建てたからといって勝手に連携するわけがありません。
    簡潔にいうと、docker compose exec garage-node2 /garage node idでノードIDを取得します。
    その後、docker compose exec garage-node1 /garage node connect <ノード2のノードID>@<ノード2のIPアドレス>:3911をすることで互いを認識するようになります。
    (connectする前に、docker compose exec garage-node1 /garage statusでノード2が認識されず、設定後は認識されていることを確認すると良いでしょう)

    2つのノードが認識されたら、garage-node1.tomlbootstrap_peersにノード2の情報を、garage-node2.tomlbootstrap_peersにノード1の情報を追加しておけば再起動しても問題ありません。(設定しない場合、リセットされるかは未調査)
    その後各ノード間のストレージのゾーンやサイズの割り当てを行うことで使えるようになります。
    細かい部分についてはドキュメントをご確認ください

    https://garagehq.deuxfleurs.fr/documentation/cookbook/real-world/#controlling-the-daemon

    ただ、CLIは結構ややこしいので、garage-webuiというものを使うのがおすすめです。
    こちらの使い方は別のアドベントカレンダーをご確認ください。

GarageHQの感想

  • 良いところ
    • シンプルでセットアップが簡単(と言われている気がする)
    • S3互換ストレージとして使える
    • 複数ノードでのレプリケーションが可能
    • ハードウェア要件が低い
  • 不満・使いこなせていないところ
    • ドキュメントは少し慣れが必要
    • GUIが公式で用意されていない(サードパーティ製のものを使う必要がある)
    • 大規模運用には向いていない(あくまでホームユース向け)
    • ライセンスがAGPLv3なので、商用利用には向いていない
    • データ自体はGarageでは暗号化されない

以上、GarageHQの紹介でした!
私自身、minioの経験はシングルノードでの利用しかなかったので、マルチノードでのS3互換ストレージはGarageHQが初めてでした。他のOSSとの比較ができてない部分は申し訳ないです。
traefikを使うことで、サブドメイン形式のバケットへのアクセスも、パス形式のアクセスもできるため、traefikには感謝しかないです!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?