こんにちは。ForSchool事業部サーバーサイドエンジニアのましばです。
今回はDocker + Rails + MinIOを利用して、開発環境でもS3のようなファイルアップロードを行うことができる環境を構築します。 同様の内容を取り扱った記事は複数存在しますが、少し古いものもあったので整理も込めて記事にします。
MinIO
MinIOはS3と互換性のあるストレージサービスです。 公式サイトではクラウド上での利用を推していますが、ブラウザからストレージ内のファイルを閲覧できたりCLIツールが存在したりとローカル環境でも使いやすいサービスです。
GitHubのREADMEではいくつかの利用方法が書いてありますが、今回はDockerで利用していきます。
環境
- Docker Desktop 4.3.2
- minio:latest(RELEASE.2022-01-08T03-11-54Z)
- Ruby 3.1.0
- Ruby on Rails 7.0.1
docker-composeで環境を構築
今回はMinIOを試すことが目的なのでなるべく最小の構築を目指します。
公式のRubyのDockerfileは少し古いのでちょっと改変します。
Rails7ではNode.jsが必須ではなくなったので、Dockerfileがだいぶシンプルになった気がします。
# Dockerfile FROM ruby:3.1 RUN mkdir /app WORKDIR /app COPY Gemfile /app/Gemfile COPY Gemfile.lock /app/Gemfile.lock RUN bundle install
docker-compose.yaml
は以下のようにしました。
MinIOコンテナもここで作成しています。
# docker-compose.yaml version: "3.9" services: db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: password ports: - "3306:3306" volumes: - ./tmp/db:/var/lib/mysql web: build: . command: ["rails", "s", "-p", "3000", "-b", "0.0.0.0"] volumes: - .:/app ports: - "3000:3000" depends_on: - db minio: image: minio/minio:latest ports: - "9000:9000" - "9001:9001" volumes: - ./tmp/minio/data:/data command: "server /data --console-address :9001" environment: MINIO_ROOT_USER: user MINIO_ROOT_PASSWORD: password
--console-address
はブラウザからMinIOのコンソールにアクセスする際のポートを指定します。
MINIO_ROOT_USER
およびMINIO_ROOT_PASSWORD
はブラウザ、RailsアプリケーションからMinIOにアクセスする際の情報になります。
MinIO公式ガイドもありますのであわせてご確認ください。
以降はDocker公式サイトのビルド手順にほぼ従います。
簡単にですが手順を記載します。
Step1. Gemfile, Gemfile.lockを作成し、Gemfileを記述します。
touch Gemfile touch Gemfile.lock
# Gemfile source "https://rubygems.org" gem "rails"
Step2. 初回のみrails new
してbuildを実行します。
docker-compose run --no-deps web rails new . --force --database=mysql docker-compose build
Step3. config/database.yaml
のpasswordとhostを修正します。
# config/database.yaml default: &default adapter: mysql2 encoding: utf8mb4 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: root password: password # MYSQL_ROOT_PASSWORDの値を追加 host: db # 変更
Step4. docker-compose up
してmigrationを実行します。
docker-compose up -d docker-compose exec web rails db:create
ここまで実行するとlocalhost:3000
にRailsのスタートページが表示されます。
デザイン変わったんですね。
MinIOへアクセスしてみる
ブラウザからアクセスしてバケットを作成する
MinIOが起動しましたが、とりあえず起動しただけでバケットも存在しないため、ブラウザからアクセスして作成します。
localhost:9001
にアクセスするとログイン情報を求められるので、docker-compose内で定義したMINIO_ROOT_USER
とMINIO_ROOT_PASSWORD
を入力します。
サイドメニューからBuckets
を選択し、Create Bucket
から名前をつけて作成します。
このままではprivateな状態なので、バケット一覧からManage
を選択して、Access Policy
をPublic
に指定します。
これでpublicなバケットが作成されました。
Railsからファイルをアップロードする
作成したバケットにRailsから画像をアップロードできるようにします。
今回はcarrierwaveとfog-awsを利用します。
まずはgemをinstallします。
gem "carrierwave" gem "fog-aws"
Rails側でファイルをアップロードできる機能を作ります。
どんなのでもいいのですが、今回はUserモデルを作ってavatarとして画像をアップロードしてみることにします。
scaffoldでUserモデルと対応するコントローラーを作成します。
docker-compose exec web rails g scaffold User name:string avatar:string docker-compose exec web rails db:migrate
AvatarUploader
を作成します。
docker-compose exec web rails g uploader avatar
# user.rb class User < ApplicationRecord mount_uploader :avatar, AvatarUploader end
carrierwaveとfogを用いてファイルをアップロードする設定をします。
# app/uploaders/avatar_uploader.rb # storage :file storage :fog
# config/initializers/carrierwave.rb CarrierWave.configure do |config| config.fog_provider = "fog/aws" config.cache_storage = :fog config.asset_host = "http://localhost:9000/bucket" config.fog_directory = "bucket" # https://github.com/carrierwaveuploader/carrierwave#using-amazon-s3 を参考 config.fog_credentials = { provider: "AWS", aws_access_key_id: "user", # MINIO_ROOT_USER aws_secret_access_key: "password", # MINIO_ROOT_PASSWORD region: "ap-northeast-1", endpoint: "http://minio:9000", path_style: true # これを追加します } end
これで設定ができたので、user作成時にavatarを選択して作成し、詳細ページで表示されるようにします。
<%# _form.html.erb %> <div> <%= form.label :avatar, style: "display: block" %> <%= form.file_field :avatar %> </div>
<%# show.html.erb %> <% if @user.avatar? %> <div> <%= image_tag @user.avatar.url %> </div> <% end %>
これでユーザー作成時にアバター画像を投稿できるようになりました。
コンテナ起動時にバケットを作成する
MinIO自体はこれで使えるのですが、バケットを手作業で作るのは面倒なのでコンテナ起動時に作成されるようにします。
MinIOにはMinIO Client(mc)というCLIが存在するのでこちらを利用してバケットを作成します。
docker-composeに以下を追加します。
createbuckets: image: minio/mc depends_on: - minio entrypoint: > /bin/sh -c " /usr/bin/mc alias set myminio http://minio:9000 user password; /usr/bin/mc mb myminio/bucket --region=ap-northeast-1; /usr/bin/mc policy set public myminio/bucket; exit 0; "
やっていることはシンプルで、myminio
というエイリアスでminio:9000
を登録し、bucket
というバケットを作成し、ポリシーをpublicにしているだけです。
alias設定時のユーザー情報はMINIO_ROOT_USER
とMINIO_ROOT_PASSWORD
を、バケット名やリージョンはcarrierwaveの設定と合わせておく必要があります。
こうするとコンテナ起動時にバケットの作成、ポリシーの設定までやってくれるので、コンソールからバケットを作成する必要がなくなります。
終わりに
MinIOでローカルの開発環境でストレージ機能を使ってみました。
思っていたより簡単に環境構築と設定ができ、コンソールも充実していて使いやすかったです。
今回はとりあえずバケットを作るだけでしたが、ドキュメントを見る限りもうちょっと凝った設定もできそうなので今後試してみようと思います。
*1:privateバケットにファイルをアップロードして署名付きのURLを返せるようにしたい…