Heroku上のSolidus製ECサイトからAmazon S3に画像をアップロードする
Solidusで構築したECサイトの画像用StorageとしてAmazon S3を導入する方法を紹介します。
前提
- Amazon Web Service(AWS)アカウント取得済み
- Heroku CLIインストール済み(
brew tap heroku/brew && brew install heroku
) - ImageMagick*1インストール済み(
sudo port install ImageMagick
)
Gemインストール
2019年7月現在、Solidusは画像添付機能をpaperclip
というGemに依存しています。
ちなみにインストール済みのGemが依存しているGemは以下のコマンドで確認できます。
$ gem dependency Gem名
例:
$ gem dependency paperclip Gem paperclip-5.3.0 activemodel (>= 4.2.0) activerecord (>= 4.2.0, development) activesupport (>= 4.2.0) appraisal (>= 0, development) aruba (~> 0.9.0, development) aws-sdk (>= 2.3.0, < 3.0, development) bourne (>= 0, development) bundler (>= 0, development) capybara (>= 0, development) cucumber-expressions (= 4.0.3, development) cucumber-rails (>= 0, development) fakeweb (>= 0, development) fog-aws (>= 0, development) fog-local (>= 0, development) generator_spec (>= 0, development) launchy (>= 0, development) mime-types (>= 0) mimemagic (~> 0.3.0) mocha (>= 0, development) nokogiri (>= 0, development) railties (>= 0, development) rake (>= 0, development) rspec (~> 3.0, development) shoulda (>= 0, development) terrapin (~> 0.6.0) timecop (>= 0, development)
paperclipで扱う画像データをS3にアップロードするために今回はaws-sdk
(version 3.0未満)とfog-aws
というGemを利用します。
# Gemfile gem 'aws-sdk', '< 3.0' gem 'fog-aws'
$ bundle update
paperclip初期設定ファイルの作成
/config/initializersにpaperclip.rbを新規作成します。
$ touch config/initializers/paperclip.rb
paperclip.rbを次のように編集します。*2
# paperclip.rb if ENV['S3_ACCESS_KEY_ID'] Paperclip::Attachment.default_options.merge!( storage: :fog, fog_credentials: { provider: 'AWS', aws_access_key_id: ENV['S3_ACCESS_KEY_ID'], aws_secret_access_key: ENV['S3_SECRET_ACCESS_KEY'], region: ENV['S3_REGION'], host: ENV['S3_HOST'] }, # S3バケットの指定 fog_directory: ENV["S3_BUCKET"] #---アップロードするデータを一般公開しない場合の設定--- # fog_public設定をfalseにすることでS3のURLへの直接アクセスを禁止できる # config.fog_public = false # S3の認証URLの有効期限(秒)の設定 # config.fog_authenticated_url_expiration = 60 #---------------------------------------------- ) Spree::Image.attachment_definitions[:attachment].delete(:url) Spree::Image.attachment_definitions[:attachment].delete(:path) end
Amazon S3にバケットを用意する
バケットの作成
公式の手順に従ってバケットを作成してください。
開発環境でもS3をストレージとして使う場合は、本番環境と合わせて2つのバケットを作成します。
このステップでの要点をあげると、以下のようなものがあります。
- バケット名は既存のバケット名と重ならないようにする
- 日本での利用を想定している場合、リージョンは「アジアパシフィック(東京)」を選択(最寄りのリージョンを設定するのはデータレイテンシーを減らすため)
- 「既存のバケットから設定をコピー」できる(ただしバケットポリシーを除く)
バケットポリシー
対象となるバケットを選択し、「アクセス権限」 -> 「バケットポリシー」をクリックします。
今回S3に保存するデータは、サイトを訪れたユーザーの誰もが見ることができる商品画像です。また、画像をストレージに追加したり削除したりはできますが、アップロードした画像を編集することはありません。、それらを踏まえ、バケットポリシーで一般公開と読み取り専用の設定を行います。
バケットポリシーの空欄に次の内容を記述してください。ただし、「bucketname」は対象のバケット名に置き換えてください。
{ "Version": "2012-10-17", "Id": "PublicRead", "Statement": [ { "Sid": "ReadAccess", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::bucketname/*" } ] }
ブロックパブリックアクセス (バケット設定)
パブリックポリシーを不正に変更できないように「アクセス権限」 -> 「ブロックパブリックアクセス」で以下の項目をオンにしておきましょう。
「新しいパブリックバケットポリシーを介して許可されたバケットとオブジェクトへのパブリックアクセスをブロックする」
今後バケットポリシーを変更する際はこの設定をオフにしてから変更してください。
アクセス・コントロール・リスト
「アクセス権限」 -> アクセス・コントロール・リスト -> パブリックアクセス -> Everyoneをクリックし、「オブジェクトの一覧」を有効化します。
IAMユーザーの作成
公式の手順に従ってIAMユーザーを作成しましょう。
AWS アカウントでの IAM ユーザーの作成 - AWS Identity and Access Management
ここで想定するIAMユーザーはECサイトの管理者です。 管理者には画像をS3にアップロードしたり、削除したりできる権限を与えたいので、アクセス権限はReadOnlyではなくFullAccessを設定します。
要点
- ユーザー名は任意
- AWS アクセスの種類:「プログラムによるアクセス」を有効化
- アクセス権限:AmazonS3FullAccess
- シークレットアクセスキーはユーザー作成直後のcsvダウンロード画面以外では確認できないので注意
環境変数の設定
環境変数の設定方法には.bash_profileに記述する方法とdotenvというgemを使う方法の主に2種類があります。 個人的にシステム側の環境設定を汚したくないので、今回はdotenvを利用してみます。
なお、dotenvは本来開発環境での環境変数の読み込みのためにつくられた背景から、基本的には本番環境の環境変数設定に利用することを勧めていません。その方針に従い、development環境とtest環境のみにdotenvを使用することとします。
Gemインストール
# Gemfile gem 'dotenv-rails', groups: [:development, :test]
$ bundle
環境変数の設定
本番環境以外の環境変数設定
プロジェクトのルートディレクトリに環境変数設定ファイルを作成します。
$ touch .env.development $ touch .env.test
このファイルの内容にはパスワードが含まれているため、絶対に外部に流出させたくありません。誤ってリポジトリに上げて一般公開してしまうことがないよう.gitignoreに環境設定ファイルを無視する記述をしておきましょう。
# .gitignore .env* # ファイル名が「.env」で始まるファイルをステージングできなくする
もし一度でもgit add
コマンド等でステージングしてgitの追跡対象に加えていると.gitignoreの設定が反映されません。その場合、次のコマンドを使ってキャッシュを削除します。
$ git rm --cached 無視したいファイル名
git status
コマンドで無視するファイルの名前が表示されなくなればOKです。
$ git status
では次に作成したファイルに環境変数を定義します。環境変数名はpaperclip.rbに記述したそれぞれの変数と対応するようにします。
.env.development
S3_BUCKET= S3_ACCESS_KEY_ID= S3_SECRET_ACCESS_KEY= S3_REGION=ap-northeast-1 S3_HOST=s3-ap-northeast-1.amazonaws.com
「=」の右側にバケット名、アクセスキーID、シークレットアクセスキーを書いてください。引用符は不要です。.env.testについても同様に記述してください。なお、上で設定しているS3_REGION_NAMEとS3_HOST_NAMEは東京リージョンのものです。
ファイルの編集が終わったら環境変数にアクセスできることをRailsコンソール上で確認しましょう。
$ RAILS_ENV=development rails c Loading development environment (Rails 5.2.3) >> ENV['S3_ACCESS_KEY_ID'] => "S3ACCESSKEYIDDEV"
本番環境(Heroku)での環境変数設定
次のコマンドを利用して、本番環境用S3バケット情報をコンソールから設定します。
$ heroku config:set S3_BUCKET=bucket_name $ heroku config:set S3_ACCESS_KEY_ID=access_key_id $ heroku config:set S3_SECRET_ACCESS_KEY=secret_access_key $ heroku config:set S3_REGION=ap-northeast-1 $ heroku config:set S3_HOST=s3-ap-northeast-1.amazonaws.com
設定情報はheroku config
で確認できます。
動作確認
開発環境での動作確認
まずDBをリセットします。
$ bundle exec rails db:reset Dropped database 'db/development.sqlite3' Dropped database 'db/test.sqlite3' Created database 'db/development.sqlite3' Created database 'db/test.sqlite3' ... Loading seed file: stores Loading seed file: store_credit Loading seed file: countries Loading seed file: return_reasons Loading seed file: states Loading seed file: stock_locations Loading seed file: zones Loading seed file: refund_reasons Loading seed file: roles Loading seed file: shipping_categories Create the admin user (press enter for defaults). Email [admin@example.com]: Password [test123]: Done!
途中、管理者アカウントの作成があることに注意です。
続いて商品サンプルデータを再インストールします。
$ bundle exec rails g spree:install ... Admin user has already been previously created. Would you like to create a new admin user? (yes/no) no No admin user created. ... ************************************************** Solidus has been installed successfully. You're all ready to go! Enjoy!
サーバーを起動してhttp://localhost:3000/にアクセスします。
$ bundle exec rails s
無事、商品画像が表示されたでしょうか?
問題なさそうであればブラウザのDeveloper ToolsでページのHTMLを解析してみましょう。
上図では商品画像を示すimgタグのsrc属性に画像URLが格納されています。
このURLは次の形式で構成されています。
https://バケット名.s3.amazonaws.com/プロジェクトのpublicフォルダ下から画像保存先までのパス
あとは管理画面にログインしたり、商品を登録したり、カートに入れた商品を会計したりして挙動を確かめてみてください。
本番環境(Heroku)での動作確認
以下のコマンドを順に打ち込んで確認していきます。 ログインとherokuアプリの作成が済んでいる場合、上2つをスキップしてください。
$ heroku login $ heroku create アプリ名(任意) ------ $ heroku pg:reset DATABASE $ bundle exec rake assets:precompile $ git push heroku master $ heroku addons:create heroku-postgresql $ heroku run rails db:migrate $ heroku run rails g spree:install $ heroku restart
無事デプロイできたら、開発環境での動作確認と同様にしてサイトの挙動を確かめてみましょう。
*1:ImageMagickには多くの脆弱性が報告されているため使用に際しては十分注意が必要です。
*2:host:の項目がない状態のpaperclip.rbでS3への画像アップロードに成功している旨の記事が多く見られましたが、私の環境ではうまくいきませんでした。バージョンアップで要求が変更されたのか、何か他のミスがあったのか...。