[Rails]Active Storageの画像をAWS S3へ保存する
はじめに
前回の記事ではActive Storageを使った画像のアップロード機能を実装しました。
今回ではAmazon S3を使ってユーザーにアップロードされたファイルを保存します。
ファイルの保存と管理をS3に任せることで、アプリケーションサーバーの容量を節約し、パフォーマンスを高めることができます。
Amazon S3とは
Amazon S3(Simple Storage Service)は、Amazon Web Services(AWS)の一部で提供されているオンラインストレージサービスです。S3は、スケーラビリティに優れ、セキュリティが強化され、高い可用性を持つクラウドストレージソリューションであり、インターネット上でファイルを保存、管理、公開することができます。
手順
Active Storageを使ってアップロードされた画像をAWS S3に保存する大まかな手順になります。
-
AWSアカウントの作成: AWSの管理コンソール(https://aws.amazon.com/) でアカウントを作成します。
-
S3バケットの作成: AWS管理コンソールから、S3コンソールにアクセスし、新しいバケットを作成します。バケットは、画像が保存されるコンテナとして機能します。
-
AWS認証情報の設定: AWS S3にアクセスするための認証情報(アクセスキーとシークレットキー)を取得し、Railsプロジェクトの環境変数に設定します。環境変数に設定することで、認証情報をプロジェクト内で安全に保持できます。
-
Gemのインストール:
aws-sdk-s3GemをRailsプロジェクトにインストールします。これは、AWS S3との通信に必要なGemです。
# Gemfile
gem 'aws-sdk-s3'
bundle install
-
config/storage.ymlの設定:
config/storage.ymlファイルを作成して、S3へのアクセス設定を行います。
10〜15行目にコメントアウトされた内容をアンコメントアウトします。
amazon:
service: S3
access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
region: <%= ENV['AWS_REGION'] %>
bucket: <%= ENV['AWS_BUCKET'] %>
-
Active Storageの設定:
config/environments/production.rbファイルで、S3をアクティブストレージのバックエンドとして設定します。
# config/environments/production.rb
config.active_storage.service = :amazon
AWSのアカウントを作成し、バケットの作成から行っていきます。
バケットを作成する
s3.console.aws.amazon.comにアクセスし、右上のバケットの作成をクリックします。

バケットの名前とリージョンを決めます。

ACL有効をONにします
ACLはバケットやオブジェクト (ファイル) へのアクセスを制御するための機能です。
ブログにアップされた画像の読み取り権限を一般のユーザーに許可しておくことで、ブログの閲覧者がそのコンテンツにアクセスできるようになります。デフォルトはプライベートとなります。

バケットのパブリックアクセスを設定します。

バケットを作成をクリックします。バケットが作成されたことを確認します。

IAMポリシーを作成する
ブログアプリ用のIAMポリシーを作成します。
IAMポリシー (IAM Policy) は、Amazon Web Services (AWS) のIdentity and Access Management (IAM) サービスにおいて、リソースへのアクセスを制御するための設定です。
IAMポリシーには、特定のAWSリソースに対して許可されたアクションや条件が含まれています。
IAMポリシーを作成することで、AWSリソースへのアクセス権を細かく制御できます。
AmazonのコンソールからIAMを選択し、ポリシーをクリックします。
サービスをS3にします。

アクセスレベルのトグルリストから該当する項目をチェックします。
Railsガイドを参考にしながらやっていきます。
Active Storageのコア機能では、s3:ListBucket、s3:PutObject、s3:GetObject、s3:DeleteObjectという4つのパーミッションが必要です。パブリックアクセスの場合はs3:PutObjectAclも必要です。ACLの設定といったアップロードオプションを追加で設定した場合は、この他にもパーミッションが必要になることがあります。
五つの項目にチェックを入れます。
リスト:
- ListBucker
読み取り:
- GetObject
書き込み:
- DeleteObject
- PutObject
許可の管理:
- PutObjectAcl

こちらのポリシーを先に作成したバケットだけに使うのでリソースを特定にします。

次へをクリックしポリシーの名前を入力します。

ポリシーの作成をクリックします。
IAMユーザーを作成する
ダッシュボードからユーザーをクリックします。
ユーザー画面からユーザーを追加をクリックします。
ユーザー名を入力し次へをクリックします。

ポリシーを直接アタッチするを選択し、画面の下にあるリストから先に作成したポリシーにチェックを入れます。

次へクリックし、ユーザーの作成をクリックします。

アクセスキーを作成する
アプリへアクセスするためにアクセスキーを作成します。
作成したユーザーをクリックし、セキュリティ認証情報をクリックします。

下にスクロールし、アクセスキーを作成をクリックします。

AWS の外部で実行されるアプリケーションを選択し、次へいきます。
EC2などを使ってアプリをデプロイされる場合上のAWS コンピューティングサービスで実行されるアプリケーションをクリックします。

説明があれば入力します。説明がいらないので飛ばします。
アクセスキーを作成をクリックします。

一度VSCodeを閉じてターミナルにEDITOR="code --wait" rails credentials:edit入れて実行します。
そうするとこちらのファイルが開かれます。
アクセスキーとシークレットアクセスキーを入れていきます。
# aws:
# access_key_id: 123
# secret_access_key: 345
# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: ********
aws:
access_key_id: ********
secret_access_key: ********
ファイルが暗号化されました。
EDITOR="code --wait" rails credentials:edit
File encrypted and saved.
EDITOR="code --wait" rails credentials:edit
EDITOR="code --wait" rails credentials:edit コマンドは、Railsアプリケーションのクレデンシャルを編集するためのコマンドです。このコマンドを実行すると、Visual Studio Code(通常のコマンドではなく code --wait オプションを指定して起動)が起動し、クレデンシャルファイルを編集するためのエディターとして使用されます。
クレデンシャルファイルは、Rails 5.2以降で導入された config/credentials.yml.enc ファイルです。このファイルには、アプリケーションの機密情報や秘密鍵などの重要なデータが暗号化されて保存されます。このファイルはバージョン管理されず、デプロイ時には復号されてアプリケーションで使用されます。
rails credentials:edit コマンドを実行すると、指定したエディターでクレデンシャルファイルを編集できます。編集が終了すると、エディターを閉じることで変更内容が保存され、暗号化された config/credentials.yml.enc ファイルが更新されます。
なお、EDITOR="code --wait" の部分は、環境変数 EDITOR にエディターのパスとオプションを指定しています。ここでは Visual Studio Code (code) を起動し、ファイルの編集を行うために --wait オプションを追加しています。このオプションを付けることで、エディターが閉じられるまでコマンドがブロックされるため、編集中に別のプロセスが実行されることを防ぎます。
development.rbを編集する
ストレージサービスを:amazonにします。
- config.active_storage.service = :local
+ config.active_storage.service = :amazon
storage.yamlを編集する
リージョンとバケットを書き換えます。
作成したバケットの内容と一致する必要があります。
public: trueを追加します。
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: rails-blog-test
public: true
aws-sdk-s3をインストールする
gem "aws-sdk-s3", require: false
bundle install
CORSを設定する
CORS (Cross-Origin Resource Sharing) は、ウェブブラウザのセキュリティメカニズムの一つで、異なるオリジン(ドメイン、プロトコル、ポート番号)からのリソース共有を制御するための仕組みです。
同一オリジンポリシー (Same-Origin Policy) は、ブラウザによって異なるオリジン間のスクリプトやリソースのアクセスを制限するセキュリティルールです。同一オリジンポリシーにより、Webページは同じオリジンのスクリプトやデータにのみアクセスできます。これにより、クロスサイトスクリプティング(XSS)やクロスサイトリクエストフォージェリ(CSRF)などの攻撃を防ぐことができます。
しかし、一部のシナリオでは異なるオリジンのリソースに対するアクセスが必要な場合があります。例えば、ウェブサイトのフロントエンドが異なるオリジンのAPIにアクセスする場合や、外部の画像やスタイルシートを読み込む場合などです。このような場合、CORSを使って異なるオリジン間のリソース共有を許可することができます。
CORSを設定しないと、クロスドメインでの画像やフォントの表示が制限されてしまいます。
S3に保存された画像をweb上に表示されなくなります。
バケットにアクセスし、アクセス許可クリックします。

下にスクロールし、Cross-Origin Resource Sharing (CORS)の編集をクリックします。
Railsが用意してくれた設定を使います。
AllowedOriginsを編集して、ローカル環境と本番環境のURLを入力します。

一度サーバーを再起動してエラーがないことを確認します。
画像をアップロードしログを確認します。
S3にアップロードされましたね。
18:42:38 web.1 | S3 Storage (0.2ms) Generated URL for file at key: 3nc0ikvqqe06bcmw2ne864zxxja0 (https://rails-blog-test.s3.ap-northeast-1.amazonaws.com/3nc0ikvqqe06bcmw2ne864zxxja0)
18:42:38 web.1 | Redirected to https://rails-blog-test.s3.ap-northeast-1.amazonaws.com/3nc0ikvqqe06bcmw2ne864zxxja0
18:42:38 web.1 | Completed 302 Found in 16ms (ActiveRecord: 1.0ms | Allocations: 5711)
S3のコンソール画面にアクセスし、画像がアップロードされたことを確認します。

次は削除の機能も試します。
ブログを編集し画像を削除します。
S3の画像も一緒に削除されたことを確認します。

production.rbを編集する
S3が問題なく動いてるので本番環境のストレージサービスを:amazonにします。
ローカル環境のストレージサービスを:localに戻します。
- config.active_storage.service = :local
+ config.active_storage.service = :amazon
本番環境のURLをアクセスし、同じようにActive Storageのアップロードと削除機能を確認します。
終わり
Active StorageとAWS S3による画像のアップロードとストレージでした。
Discussion