この記事はプログラミング学習者がActive storage、S3(AWS)を使って本番環境(Heroku)にデプロイするまでに躓いたところを備忘録も兼ねてまとめたものです。
内容に間違いがありましたら、ご指摘頂けると助かります。
動作環境
Ruby: 3.2.1
Rails: 7.0.0
Postgresql: 14
Docker
PC: M2 Mac OS Sonoma 14.4.1
1, Active storageの導入
Active storageとはRails5.2から導入されたファイルアップロード機能です。
Active Storageを用いることで画像、PDF、動画をローカル環境だけでなくAmazon S3、Microsoft Azure Storage、Google Cloud Storageからなるクラウドストレージにもファイルをアップロードすることができます。
※今回はローカルとAmazon S3へのアップロードを目的として設定しました。
Active Storageを使用するために事前のインストール作業を進めていきます。
$ bin/rails active_storage:install
$ bin/rails db:migrate
※Docker内の為、docker compose exec web(アプリ名)を頭に付けて実行しました。
上記のコマンドを実行することで下記の3テーブルが作成されます。
・active_storage_blobs
アップロードされたファイルに関するメタ情報(データに関するデータ-作成日、更新日等)を保存する
・active_storage_attachements
主となるモデルとactive_storage_blobsテーブルの中間に入るテーブル
・active_storage_variant_records
生成されたバリアント(元の画像から派生・生成される画像-リサイズ等)に関するレコードを保存する
Active storageのサービスはconfig/storage.yml
で宣言されています。
使用するサービスごとに名前や保管に関する詳細を設定します。
以下のコードは私が実際に使用したコードです。
test:
service: Disk
root: <%= Rails.root.join("tmp/storage") %>
local:
service: Disk
root: <%= Rails.root.join("storage") %>
# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
amazon:
service: S3
access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
region: ap-northeast-1
bucket: my-rails-bucket-for-ec-site
test
、local
、amazon
の3サービスでの保管場所などが記載されています。
使用するサービスは環境ごとに異なる可能性もある為、環境ごとに設定していきます。
今回は開発環境と本番環境で使用するサービスの設定を解説します。
開発環境の場合
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
この設定により開発環境ではlocal
サービス下にデータを保管する設定となります。
config/storage.yml
の内容を見ていただくとローカル環境(Railsアプリが配置されたディレクトリ配下のstorageディレクトリ)にファイルを保存する設定になっています。
※appディレクトリと同じ階層にあるstorageディレクトリです。
次に本番環境の場合
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :amazon
この設定により本番環境ではamazon
サービス下にデータを保管する設定となります。
config/storage.yml
にはAmazonのS3サービスに保管する設定がされています。
※config/storay.yml
のamazon
に関する設定は初期設定ではなく、既に本番環境用に変更した内容です。
config/storage.yml
のamazon
部分については初期状態が下記のようになっています。
# Use bin/rails credentials:edit to set the AWS secrets (as # aws:access_key_id|secret_access_key)
# amazon:
# service: S3
# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
# region: us-east-1
# bucket: your_own_bucket
上記のように最初はコメントアウトされていますので、使用する際はコメントアウトを解除してから必要な所を変更します。
初期設定でコメントアウトされているのはMicrosoft Azure StorageとGoogle Cloud Storageも同じです。
amazon
サービスを使用する場合、gemのインストールが必要となります。
gem 'aws-sdk-s3', require: false
bundle install
でインストールします。
require: false
は自動読み込みされないようにするためのオプションです。
アプリ起動時に読み込む必要がないので、require: false
の設定をすることで起動時のメモリ消費を抑えてアプリの起動速度を改善することができます。
2、Railsのファイル内でコードを編集
Active storageの導入が終わったら、Rails内のコードをそれに対応した内容へ変更・追加します。
先ず、Active storageと結び付けたモデルのファイルに必要なコードを追加します。
class Merchandise < ApplicationRecord
has_one_attached :image
end
今回は一つのレコード(DBの)に一つの画像を添付する設定です。
サンプルコードの場合、Merchandise
が対象のモデル名となります。
もし、一つのレコードに複数の画像を添付したい場合は書き方が以下のようになります。
class Merchandise < ApplicationRecord
has_many_attached :images
end
次にデータを表示するview
ファイルの設定です。
<% @merchandises.each do |merchandise| %>
<% if merchandise.image.attached? %>
<%= image_tag merchandise.image %>
<% end %>
<% end %>
@merchandises
にはコントローラーでMerchandise
モデルのDBデータを全て入れるように設定しています(@Merchandise.all
)。
Each分でDBのレコード一つ一つに対してActive storageで画像が添付されているかどうかをattached?
で判定しています。
画像が添付されていれば、image_tag
にmerchandise.image
を渡すことで表示できます。
merchandise.image
はレコードに画像を添付しているかどうかに関わらず存在するオブジェクトですが、image_tag
を使う場合は実際に画像が添付されていないとエラーが発生しますので、merchandise.image.attached?
で判定して実際に画像が添付されている時だけimage_tag
で画像を表示するようにしています。
3, AWSでS3を使えるように登録
AWS内のS3サービスを使って画像をアップロードできる場所を作っていきます。
※ここでは既にAWSのアカウントを持っている前提で話を進めていきます。
アカウントを持っていない方は先に新規登録をお願いします。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPublicReader",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-rails-bucket-for-ec-site/*"
}
]
}
項目名 | 概要 |
---|---|
Version | ポリシーの文法のバージョン |
Statement | 一つの枠組み。枠組みの開始を宣言するもの |
Sid | 任意で与える識別子。他のStatementと重複しなければ指定は無い |
Effect | Statementの内容を許可(Allow) or 拒否(Deny)する |
Principal | 誰が使うかを指定。サンプルコードでは*(全てのユーザー) |
Action | AWSサービスのうちで指定したものについて、何の操作を許可 or 拒否する。サンプルではS3サービスのリソース取得操作が対象 |
Resource | Statementに記載した内容をどのリソースに適用するか。サンプルではmy-rails-bucket-for-ec-site というバケットが対象 |
- バケットの作成が終わった時点で
config/storage.yml
を変更する
# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
amazon:
service: S3
access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
region: ap-northeast-1
bucket: my-rails-bucket-for-ec-site
上記の内容はregion
とbucket
を作成したバケットの詳細情報に従って変更しています。
-
AmazonでIAMユーザーを作成後、S3でアクセスキーを作成する
IAM(Identified and Access Management:アイアム)ユーザーとはAWSのサービスで認証と認可の設定を行うことができるサービスです。
許可のオプションはポリシーを直接アタッチするを選択
許可ポリシーとしてAmazonS3FullAccess
を選択
-
次にアクセスキーを作成します。
4, Rails側で認証設定を行う
先ほど作成したアクセスキーを使って認証情報を追加していきます。
- バケットの作成が終わった時点で
config/storage.yml
を変更する
# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
amazon:
service: S3
access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
region: ap-northeast-1
bucket: my-rails-bucket-for-ec-site
config/storage.yml
でaccess_key_idとsecret_access_keyの部分はここに直接入力するとセキュリティ上第三者に漏洩する可能性がある為、Credentialsを使って入力します。
外部APIのキー情報やアクセストークンなどの機密情報を外部に漏らしてしまうと悪用される可能性があります。RailsにはCredentialsという機密情報を守る機構がありますので、重要な情報は暗号化して保護することができます。
<%= Rails.application.credentials.dig...
の部分はCredentialsに入力されたデータを読み込んで処理するようになっていますので変更せずにそのままいきます。
ちなみにCredentialsの内容は通常config/credentials.yml.enc
の中に保存されていますが、直接見ても暗号化されているので中身を正しく見ることはできません。
- CredentialsのデータをAWSで得たアクセスキーに書き換える。
コンソールで以下のコードを入力することでCredentialsの内容を書き換えできます。
$ EDITOR="vim" bin/rails credentials:edit
上記のコードではvimを使ってCredentiasを編集するという内容を意味しています。
vimの部分は他のエディターでも対応可です。
VisualStudioCode = code
Atom = atom
vi = vi
VisualStudioCodeとAtomの場合、入力内容が若干変わります。
EDITOR="code --wait" bin/rails credentials:edit
EDITOR="atom --wait" bin/rails credentials:edit
私の場合、Dockerコンテナ内の起動しているweb(サービス)に対して同じ処理をしていましたので、docker用のコマンドを交えて実行しました。
docker compose exec -e EDITOR="vim" web bin/rails credentilas:edit
-e EDITOR="vim"
で-e
をつけるのは環境変数の指定なのでオプションとして必要になるからです。
ちなみに最初に上記コマンドを実行した時は以下のエラーが発生してCredentialsの編集ができませんでした。
$ docker compose exec -e EDITOR="vim" web bin/rails credentilas:edit
Adding config/master.key to store the encryption key:xxxxxxxxxxxxxx
Save this in a password manageer your team can access.
If you lose the key, no one, including you, can access anything encrypted with it.
create config/master.key
Couldn't decrypt config/credentials.yml.enc. Perhaps you passed the wrong key ?
内容としては
暗号化キー xxxxxxxxxxxxxxx を保管する為にconfig/master.key
を追加しました。
チーム全員がアクセスできる場所にこのkeyを保管してください。
このkeyを無くしたらあなたも含めて誰もこのkeyで暗号化されたデータにアクセスできません。
config/master.key
を作成しました。
config/credentials.yml.enc
を復号出来ませんでした。
あなたが間違ったkeyを通した可能性があります。
これはconfig/master.key
が無い状態でbin/rails credentials:edit
コマンドを実行した時に出るメッセージでconfig/master.key
が生成される時に発生します。
また、2回目に同じコマンドを実行したら次は以下のエラー文が出ました。
$ docker compose exec -e EDITOR="vim" web bin/rails credentilas:edit
ActiveSupport::MessageEncryptor::InvalidMessage(以下省略)
内容としては先のコマンドで作成されたconfig/master.key
が既存のconfig/credentials.yml.erc
に対して使えない為、エラーが出ているようです。
ここではconfig/credentials.yml.erc
を一度別名に変えてから(存在しないように仕向けてから)もう一度同じコマンドを実行しました。
3回目はdockerコンテナ内部にvimがインストールされていなかったので、編集することができませんでした。
コマンドを実行してもFile encryped and saved.
とだけ出力されてきました。
docker compose exec web apt-get install vim
をした後にもう一度トライすると編集することができました。
$ docker compose exec -e EDITOR="vim" web bin/rails credentials:edit
File encrypted and saved.
以下はvimコマンドにより表示された内容です。
# aws:
# access_key_id: 123
# secret_access_key: 345
# Used as the base secret for all MessageVerfiers in Rails, including the one protecting cookies.
secret_key_base:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ここでaws
, access_key_id
, secret_access_key
の三行について行頭の#
を外してコメントアウトを解除します。
access_key_id
, secret_access_key
についてはAWSで取得したアクセスキーをそれぞれ入力します。
入力した内容は下記のコマンドで見ることもできます。
$ docker compose exec -e EDITOR="vim" web bin/rails credentials:show
credntialsを編集する時にはedit
だった所をshow
に変更したコマンドです。
これで保存先をAmazon S3に変更する一連の作業が完了しました。
4, Herokuへアップロード
最終段階としてHerokuへ設定した内容をPushして反映させます。
$ heroku buildpacks:add -i 1 https://github.com/heroku/heroku-buildpack-activestorage-preview
$ git add .
$ git commit -m "任意(S3サービスを本番環境に加えた内容)"
$ git push heroku index_and_detail(私の場合):main #ブランチを切っていました。
Credentialsの内容をHeroku側で復号できるようにconfig/master.key
の内容を環境変数として用意する必要があります。
config/master.key
はRailsのファイル内に含まれていますが、.gitignoreの対象になっており、GithubやHerokuのようなGitリポジトリをそのままデプロイする本番環境の場合は別途用意しておく必要があります。
$ heroku config:set RAILS_MASTER_KEY=`config/master.key`の内容を貼り付け
$ heroku run rails db:migrate
$ heroku open
私の場合、この時点では作成しているアプリを通して画像データをアップロードする機構をまだ組み込んでいませんでしたので、seedを使って本番環境に画像データをアップロードしました。
merchandise6 = Merchandise.create!(
name: 'サワラ',
amount: 1500,
description: '800g, 40cm',
stock: 1,
how_to_cook: '刺身(炙り)'
)
merchandise6.image.attach(io: File.open(Rails.root.join('app/assets/images/spanish_mackerel.JPG')),
filename: 'spanish_mackerel.JPG')
下記のコメントで作成しているdb/seed.rb
のデータをHerokuへ取り込みます。
$ heroku run rake db:seed
結果、取り込んだ画像データをHeroku側へ表示させることができました!
参考リンク
https://railsguides.jp/active_storage_overview.html
https://zenn.dev/farstep/books/7f169cdc597ada/viewer/9b2cc7
https://qiita.com/takehanKosuke/items/79a66751fe95010ea5ee
https://toshpit.com/rails-activestorage/
https://zenn.dev/redheadchloe/articles/e924ab767b40d5
https://qiita.com/kawa3401/items/c982ccebacf2faeae48d
https://qiita.com/hmmrjn/items/7cc5e5348755c517458a
https://qiita.com/hmmrjn/items/479c9e9ce82771f1b6d7
https://qiita.com/zenfumi/items/4a7cbab59f0f7ede0d6e
https://qiita.com/show1107/items/5dbdac581d561909cf84