patorashのブログ

方向性はまだない

開発環境でAWS S3の代替としてminioを使う

ほんの最近まで、開発環境でもAWS S3を使っていたのですが、minioっていうのがあるよと同僚に教えてもらっていたので、とりあえずissueに積んだまま数ヶ月が過ぎていました。そしてちょうどこの間、間隙を縫って作業できそうな時間ができたので、minioに移行しました。

minioとは?

minioはAWS S3とAPIの互換性のあるオープンソースのストレージサーバーです。

github.com

minioのメリット

minioの利点としては、対象ディレクトリに同名のファイルがあれば、わざわざRailsアプリを介してDBに登録処理を行わなくてもよいというところです。自分の環境でDBのバックアップデータを作り、minioのディレクトリをごっそり他の開発者に渡してパスを通したら、DBとファイルの紐付けがすでに終わっている状態になるのでGood。AWS S3だと、そんなことをしたら自分以外の人がファイルを削除したら自分のところにはDBにデータ残ってるのにS3からはファイルが消えてる、みたいになるので、個別にバケットを作らないといけなかったため、アップロード処理も大変だしすごく面倒でした。

minioのデメリット

デメリットはほとんどないと思いますが、完全にS3のAPIと互換性があるわけではない、というのが注意点です。後述しますが、これで結構ハマりました。

dockerで導入

minioはDockerHubでイメージが公開されているため、それを使いました。

https://hub.docker.com/r/minio/minio/

docker-composeに含める

自分の環境では、PostgreSQL, Redis, Elasticsearchはdocker-composeで起動するようにしているので、minioもdocker-compose.ymlの中にいれるようにしました。minioの部分だけ抽出して書きます。

version: "3"
services:
  minio:
    image: minio/minio:latest
    ports:
      - "9000:9000"
    command: [server, /data]
    volumes:
      - ~/docker/minio/data:/data
    environment:
      MINIO_ACCESS_KEY: access_key
      MINIO_SECRET_KEY: access_secret
    restart: always

あとは、docker-compose up -dで起動するとOKです。

Railsのpaperclipで使う

paperclipで使う方法は、paperclipのwikiで公開されています。

Paperclip with minio · thoughtbot/paperclip Wiki · GitHub

一応、私のしている設定を書いておきます。

ENV['AWS_ACCESS_KEY']に、minioのアクセスキーを、ENV['AWS_SECRET_ACCESS_KEY']に、minioのシークレットキーを設定します。リージョンとバケット名は自分のところでは環境変数にしていますが(dotenvを使ってる)、固定ならば固定でもいいと思います。

app/config/environments/development.rb

config.paperclip_defaults = {
    storage: :s3,
    s3_protocol: :http,
    s3_host_name: 'localhost:9000',
    s3_credentials: {
        access_key_id: ENV['AWS_ACCESS_KEY'],
        secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
    },
    s3_region: ENV['AWS_REGION'],
    bucket: ENV['S3_BACKET_NAME'],
    s3_options: {
        endpoint: 'http://localhost:9000', # for aws-sdk
        force_path_style: true # for aws-sdk (required for minio)
    },
    s3_permissions: :private,
    url: ':s3_path_url',
    path: ':class/:attachment/:id/:style/:filename'
}

肝な部分は、エンドポイントをminioに向けている点や、protocolをhttpにしている点などですね。

ハマったポイント

ハマったポイントですが、S3とminioはpermissionに関しては完全に互換性があるわけではないです。paperclipでは、あくまで上記の設定はデフォルトなので、Model側で設定すれば上書きできるんですね。それで、一部のファイルのpermissionをpublic-readにしていたのですが、読めませんでした。デフォルトの設定(ここではprivate)が勝ってしまうのです。

今度は逆にデフォルトの設定をpublic-readにしてみたところ、元々public-readにしていたファイルは読めるようになりましたし、privateにしていたものは、expiring_urlメソッドで有効期限付きURLを発行してみたのですが、有効期限が過ぎるとちゃんと見えなくなったのですが、URLのクエリを取り除くと普通に見えてしまいました。これはアカン。

解決策

このままだとS3とminioでpermissionに整合性が取れない状態になるので、

  1. s3_permissionはデフォルトでprivateに設定
  2. 今までpublic-readに設定していたファイルもprivateに変更
  3. 全てのファイルにアクセスする場合にはexpiring_urlメソッドで有効期限付きURLを発行する

という方針に切り替えました。

これで、検証環境で試したところ、うまくいったので、OKとします。