MinIO を使ってローカルでの開発環境の外部ストレージを Amazon S3 からローカルのコンテナへ置き換える

この記事は個人ブログと同じ内容です

www.ritolab.com


ローカルでのアプリケーション開発は、できるだけ外部のサービスに依存させたくないものです。

API が提供されている外部サービスであればモックすれば良いですし、ストレージに関しても、開発時はローカルにファイルを設置するようにして、確認環境以降は外部ストレージに置くようにする事は可能です。

一方で、AWS SDK を使ってストレージ操作をしてるとそのソースコードがきちんと動くものなのか、動作確認がローカルで出来ない状態にはなります。(ローカルでのファイル操作には AWS SDK を用いないため)

今回はそれらを払拭するために、MinIO を使って、ローカルでの開発環境の外部ストレージを S3 から MinIO に置き換えてみます。

MinIO

MinIO は、Amazon S3 互換のオブジェクトストレージサーバーです。

min.io

MinIO は S3 と互換性を持つので、AWS SDK を通じてアクセス・操作が可能です。

そして MinIO には、Docker Image が公式から提供されています。

hub.docker.com

つまり、ローカル環境の外部ストレージを「S3(インターネット上のサービス)」から「MinIO のコンテナ」に置き換えることができれば、インターネットへの通信がなくせて(外部サービスに依存せず)、閉じた開発が可能になります。

開発環境

MinIO を導入する環境として、docker compose で作成されたローカルでのコンテナ環境を想定します。

docker-compose.yml

MinIO のコンテナを定義します。

minio:
    image: 'minio/minio'
    container_name: minio
    environment:
        MINIO_ROOT_USER: minioadminuser
        MINIO_ROOT_PASSWORD: minioadminpassword
    entrypoint: bash
    command: -c "/opt/bin/minio server /export --address :9999 --console-address :9001"
    volumes:
        - ./docker/minio/data:/export
    ports:
        - '9000:9999'
        - '9001:9001'

command で MinIO の起動を行なっていますが、ポイントが 2 点あります。

  • --address :9999 で、MinIO の API のポートを指定しています。API のデフォルトポートは 9000 ですが、ポート番号を変更する必要がなければここを記載する必要はありません。
  • --console-address :9001 でコンソールの ポートを指定しています。コンソール(GUI)の IP に関しては起動時に自動的に決定されます。docker compose で動かす場合はコンソールの IP をマッピングしてあげる必要があるため IP を指定して固定にするようにしています。

volumes では MinIo ストレージの永続化を行なっているのですが、この場合だと data ディレクトリ配下に切ったディレクトリがそのままバケットや階層になるので永続化しておくとかなり使いやすかったです。

イメージはこんな感じ。(ホスト側に docker/minio/data というコンテナ用のディレクトリを作ってその配下をつなげた場合)

docker
└─ minio
    └─ data
        ├─ my-bucket-1
        │   └─ sub-1
        │       └─ sample.png
        ├─ my-bucket-2
        │   └─ sample.png
        └── my-bucket-3

my-bucket-(n) はディレクトリですが、MinIO のコンソールを開くとそのままバケットとして認識されます。(起動後に追加しても認識されるのでバケットや階層の追加はかなり簡単)

ports では API と コンソール(GUI)のポートをマッピングしています。

これらの設定で起動させると、MinIO のストレージが動作します。

コンソールへは、localhost:9001 でアクセスします。

f:id:ro9rito:20211110083724p:plainf:id:ro9rito:20211110083721p:plainf:id:ro9rito:20211110083719p:plain

Laravel Flysystem での設定

環境は用意できましたが、Laravel の Flysystem を用いてストレージ操作を行なっている場合の設定のポイントも残します。

確認すべきは 3 点です。設定ファイルに urlendpoint そして use_path_style_endpoint の設定項目があるかを確認して、なければ追加します。

config/filesystems.php

's3' => [
    'driver' => 's3',
    'key' => env('AWS_ACCESS_KEY_ID'),
    'secret' => env('AWS_SECRET_ACCESS_KEY'),
    'region' => env('AWS_DEFAULT_REGION'),
    'bucket' => env('AWS_BUCKET'),
    'url' => env('AWS_URL'), // <- これがあるか確認
    'endpoint' => env('AWS_ENDPOINT'), // <- これがあるか確認
    'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), // <- これがあるか確認
],

最近のバージョンならありますが、バージョンアップしながら使い続けている場合だとこの辺のプロパティは手動で追加しない限りは付いていない場合もあるので、もしなければつけます。

あとは、ローカルの .env の項目です。

.env

AWS_ACCESS_KEY_ID=minioadminuser
AWS_SECRET_ACCESS_KEY=minioadminpassword
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=YOUR_BUCKET_NAME 
AWS_USE_PATH_STYLE_ENDPOINT=true
AWS_ENDPOINT=http://minio:9999
AWS_URL=http://localhost:9000
  • AWS_USE_PATH_STYLE_ENDPOINT は ture で設定します。
  • AWS_ENDPOINT は、同一ネットワーク内からの MinIO コンテナへのアクセス URL を指定します。(例えばバックエンドからの取得はこっち)
  • AWS_URL は、外部からリソースとして参照する(例えばブラウザからの参照)場合の URL を指定します。

これらを設定すると、MinIO ストレージでの操作ができるようになります。

署名付き URL を取得する際のつまづき

実際に操作してみて 1 点躓いたポイントがありました。

temporaryUrl() メソッドを用いて署名付き URL を取得すると、出力される URL は http://minio:9000/〜 となるのですが、この URL は同一ネットワークからのアクセス用の URL なので、例えば HTML で img タグに指定する場合は http://localhost:9000/〜 の URL を使いたい。となります。

そこで Filesystem のソースを掘っていくと、「temporary_url っていう設定値を持っていたらベースの URL を置き換えるよ」という記述があり、これは助かると思って設定してみたのですが、確かにベースの URL は変更されましたが、その URL では SignatureDoesNotMatch となってしまい使えませんでした。

ソースを見てみると、署名を付加されたものに対して単純にベースの URL を置換している(今回でいえば minio:9000 を localhost:9000 に置換)だけなので、署名を再生成しているわけでもなくただの URL 改ざん状態になっているため機能しない状態でした。

最終的には署名付き URL 取得用の disk 設定を config 側に 1 つ追加して事なきを得ましたが、これはこれで冗長な気がしないでもないので、docker compose だからこそ出てくる差分ではあるもののもっと上手く設定で吸収できればやりたいところでした。

まとめ

ローカルにベタで保存でも問題ないとは思いつつ、それでも環境によって切り替える手間が省ける、AWS SDK を用いたファイル操作の動作確認が行えるという点ではローカル環境のストレージを S3 から MinIO に置き換えるのは一定アリかなとは思いました。

取得や保存、削除といった操作に関しては問題なく、既存の実装をほぼ変更する事なく S3 から MinIO への切り替えは行えました。(署名付き URL の取得は確認が必要)

何よりローカルの開発環境がインターネット上にあるサービスに依存しなくなるのはうれしいですね。