LoginSignup
1
1

Active storageを使ってS3(AWS)へのアップロードとHerokuデプロイ

Posted at

この記事はプログラミング学習者が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で宣言されています。
使用するサービスごとに名前や保管に関する詳細を設定します。
以下のコードは私が実際に使用したコードです。

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

testlocalamazonの3サービスでの保管場所などが記載されています。
使用するサービスは環境ごとに異なる可能性もある為、環境ごとに設定していきます。

今回は開発環境と本番環境で使用するサービスの設定を解説します。

開発環境の場合

config/envoironments/development.rb
  # 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ディレクトリです。

次に本番環境の場合

config/environments/production.rb
# 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.ymlamazonに関する設定は初期設定ではなく、既に本番環境用に変更した内容です。

config/storage.ymlamazon部分については初期状態が下記のようになっています。

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: us-east-1
#  bucket: your_own_bucket

上記のように最初はコメントアウトされていますので、使用する際はコメントアウトを解除してから必要な所を変更します。
初期設定でコメントアウトされているのはMicrosoft Azure StorageとGoogle Cloud Storageも同じです。

amazonサービスを使用する場合、gemのインストールが必要となります。

Gemfile
gem 'aws-sdk-s3', require: false

bundle installでインストールします。
require: falseは自動読み込みされないようにするためのオプションです。
アプリ起動時に読み込む必要がないので、require: falseの設定をすることで起動時のメモリ消費を抑えてアプリの起動速度を改善することができます。

2、Railsのファイル内でコードを編集

Active storageの導入が終わったら、Rails内のコードをそれに対応した内容へ変更・追加します。
先ず、Active storageと結び付けたモデルのファイルに必要なコードを追加します。

app/models/merchandise.rb
class Merchandise < ApplicationRecord
  has_one_attached :image
end

今回は一つのレコード(DBの)に一つの画像を添付する設定です。
サンプルコードの場合、Merchandiseが対象のモデル名となります。
もし、一つのレコードに複数の画像を添付したい場合は書き方が以下のようになります。

app/models/merchandise.rb
class Merchandise < ApplicationRecord
  has_many_attached :images
end

次にデータを表示するviewファイルの設定です。

app/views/merchandises/index.html.erb
<% @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_tagmerchandise.imageを渡すことで表示できます。
merchandise.imageはレコードに画像を添付しているかどうかに関わらず存在するオブジェクトですが、image_tagを使う場合は実際に画像が添付されていないとエラーが発生しますので、merchandise.image.attached?で判定して実際に画像が添付されている時だけimage_tagで画像を表示するようにしています。

3, AWSでS3を使えるように登録

AWS内のS3サービスを使って画像をアップロードできる場所を作っていきます。
※ここでは既にAWSのアカウントを持っている前提で話を進めていきます。
アカウントを持っていない方は先に新規登録をお願いします。

  • AWSにログイン → サービス(左上) → S3 を起動
    AWSスタート画面.png
    AWSでS3を選択.png

  • バケットを作成する(右端)を選択
    ※バケットは画像などのファイルをアップロードできる場所
    S3を選択した画面.png

  • バケット名を決めて、ブロックパブリックアクセスの設定をOFFにする。
    他の設定はデフォルトで問題無いです。
    バケット名の入力.png
    パブリックアクセスブロックを外す.png

  • バケットポリシーで権限や公開設定のルールを設定します。
    バケット名をクリック.png
    アクセス許可をクリック.png
    バケットポリシー編集ボタン.png
    バケットポリシーの保存.png

bucket policy
{
	"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を変更する
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

上記の内容はregionbucketを作成したバケットの詳細情報に従って変更しています。

  • AmazonでIAMユーザーを作成後、S3でアクセスキーを作成する
    IAM(Identified and Access Management:アイアム)ユーザーとはAWSのサービスで認証と認可の設定を行うことができるサービスです。
    IAMユーザーを選択.png
    ユーザー詳細.png
    ユーザー作成ボタンを押す.png
    ユーザー名の入力.png
    許可のオプションはポリシーを直接アタッチするを選択
    許可ポリシーとしてAmazonS3FullAccessを選択
    S3fullaccessを選択.png
    ユーザーの作成.png

  • 次にアクセスキーを作成します。

  • 作成したユーザーを選択.png
    アクセスキー作成を選択.png
    ローカルコードを選択.png
    アクセスキーを作成完了.png
    必ずシークレットアクセスキーをメモなどで保管しましょう。
    csvファイルとしてダウンロードすることもできます。
    アクセスキーの表示.png

4, Rails側で認証設定を行う

先ほど作成したアクセスキーを使って認証情報を追加していきます。

  • バケットの作成が終わった時点でconfig/storage.ymlを変更する
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を使って本番環境に画像データをアップロードしました。

db/seed.rb
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

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1