はじめに
プライベートでの共同開発において、「開発環境と本番環境の差異をできるだけ少なくしたい」という思いからローカルで動かせるS3互換ストレージを調べていました。業務でfake-s3
というサービスを使用したことがありますが、MinIOというサービスも現場で使われている例を発見したのでそちらを導入してみました。
MinIOについて
MinIOとはAWS S3互換のオブジェクトストレージのことです。
- 特徴
- ローカルに仮想的な S3 環境を構築できる
- ローカルの仮想環境なので不用意にAPIを叩いてもお金も掛からない
シナリオ
簡単なアプリを作成した後、MinIOを導入し、投稿した画像がMinIOに格納されるかを確認してみます。
1. アプリの土台を作成する
以下の機能をもつ簡易的なブログアプリを作成する。
- 記事一覧機能
- 記事詳細機能
- 記事投稿機能
動作確認
MinIOを用いない状態で画像を投稿し、storageフォルダにファイルが格納されることを確認する
2. MinIOの導入
提供されているMinIOのdockerイメージを使用してMinIOを導入します。
動作確認
MinIOを導入した状態で画像を投稿し、MinIOのバケットににファイルが格納されることを確認する
1. アプリの土台を作成する
1. こちらの記事を参考にDockerでrailsのアプリケーションを新規作成します。
2. 必要モデルの生成
$ rails g model article
3. マイグレーションファイルの編集
一応タイトルと内容を追加しておきます。
class CreateArticles < ActiveRecord::Migration[6.0]
def change
create_table :articles do |t|
t.string :title
t.text :body
t.timestamps
end
end
end
4. データベースに反映
$ rails db:migrate
5. 必要コントローラーの生成
$ rails g controller articles
6. ActiveStorageの導入
$ rails active_storage:install
$ rails db:migrate
class Article < ApplicationRecord
has_one_attached :image # 追加
end
7. 必要アクションの定義
class ArticlesController < ApplicationController
# 一覧
def index
@articles = Article.all
end
# 詳細
def show
@article = Article.find(params[:id])
end
# 新規投稿画面
def new
@article = Article.new
end
# 新規投稿
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render :new
end
end
private
def article_params
params.require(:article).permit(:title, :body, :image)
end
end
8. ルーティングの設定
Rails.application.routes.draw do
resources :articles
end
9. viewの作成
<h1>Articles</h1>
<%= link_to '新規登録', new_article_path %>
<div>
<ul>
<% @articles.each do |article| %>
<li id="article_<%= article.id %>">
<%= link_to article.title, article %>
</li>
<% end %>
</ul>
</div>
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Body:</strong>
<%= @article.body %>
</p>
<p>
<% if @article.image.attached? %>
<%= image_tag @article.image %>
<% end %>
</p>
<%= link_to 'Back to Articles', articles_path %>
<h1>New Article</h1>
<%= form_with model: @article, local: true do |form| %>
<div>
<%= form.label :title %><br>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :body %><br>
<%= form.text_area :body %><br>
</div>
<div>
<%= form.label :image %><br>
<%= form.file_field :image %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
<%= link_to 'Back to Articles', articles_path %>
動作確認
1. 新規投稿フォームから画像を投稿してみます。
URL: http://localhost:3000/articles/new
2. 投稿詳細画面を確認
URL: http://localhost:3000/articles/1
3. ファイルの格納場所を確認
以下の記述によりstorage
ディレクトリにバイナリファイルが保存される。
...
config.active_storage.service = :local
...
...
local:
service: Disk
root: <%= Rails.root.join("storage") %>
2. MinIOの導入
1. docker-composeにMinIOの設定を追加する
MinIOのバージョンアップにより指定する環境変数がMINIO_ACCESS_KEY
とMINIO_SECRET_KEY
からMINIO_ROOT_USER
とMINIO_ROOT_PASSWORD
に変更となりました。
version: "3"
services:
db:
(省略)
web:
(省略)
minio:
image: minio/minio:latest
command: ["server", "/data", "--console-address", ":9001"] # --console-address オプションの追加
ports:
- 9000:9000
- 9001:9001 # コンソール用のポートフォワード設定追加
volumes:
- minio:/data
environment:
MINIO_ROOT_USER: minio # 元 MINIO_ACCESS_KEY
MINIO_ROOT_PASSWORD: miniosecret # 元 MINIO_SECRET_KEY
volumes:
mysql-data:
minio:
driver: local
2. storage.ymlを編集
local:
# service: Disk
# root: <%= Rails.root.join("storage") %>
service: S3
access_key_id: "minio"
secret_access_key: "miniosecret"
region: "us-east-1"
endpoint: "http://minio:9000"
bucket: rails-blog-minio
force_path_style: true
3. docker-compose.ymlを編集したのでbuildして再度コンテナ立ち上げる
$ docker-compose up -d --build
4. http://localhost:9001 にアクセスする
5. ログイン
docker-compose.yml
に記述したUsername、Passwordを入力すると以下のような画面になります。
この時点ではまだBucketは作成されていません。
6. Bucketの作成
サイドメニューの「Buckets」 → 「Create Bucket」でバケットを作成します。
名前はconfig/storage.yml
のbucket
で指定したバケット名にします。
動作確認
1. 画像を投稿する
この状態で画像を投稿すると以下のようなエラーが発生します.
RuntimeError (Cannot load `Rails.config.active_storage.service`:
Missing service adapter for "S3"):
S3のためのアダプターがないと言われるので以下をGemfileにaws-sdk-s3
を追加してinstallします。
gem "aws-sdk-s3", require: false
2. 再度画像を投稿して投稿詳細画面を確認してみます.
正しく画像が表示されていることが分かります。
3. MinIOコンソール画面Bucketの中身の確認
以下のようにオブジェクトが一つ格納されていることが確認できます。
4. storageディレクトリを確認
ファイルの格納先をMinIOに切り替えたため追加で記事を投稿してもstorage
ディレクトリに新しくファイルが格納されていません。
※画像の取得ができない場合は/etc/hostsにminioを追加してみてください。
...
127.0.0.1 minio
...
おまけ
- 先ほどはconsole画面から手動でBucketを作成しましたが、dockerを立ち上げた時点で作成されているとより運用しやすいと思うのでdocker-composeを再度編集していきます。
-
{}
で示した、MINIO_ROOT_USER, MINIO_PASSWORD, bucket-nameを置き換えて指定してください
version: "3"
services:
db:
(省略)
web:
(省略)
minio:
(省略)
# ↓ 追加
createbuckets:
image: minio/mc
depends_on:
- minio
entrypoint: >
/bin/sh -c "
until (/usr/bin/mc config host add myminio http://minio:9000 {MINIO_ROOT_USER} {MINIO_ROOT_PASSWORD} miniosecret) do echo '...waiting...' && sleep 1; done;
/usr/bin/mc mb myminio/{bucket-name};
/usr/bin/mc policy download myminio/{bucket-name};
exit 0;
"
volumes:
mysql-data:
minio:
driver: local
終わりに
以上で開発環境(ローカル)にいながら本番環境を意識した開発ができるようになりました。
参考