【Rails × AmazonS3】 RailsアプリでAmazonS3を利用し、本番環境に画像をアップロードする

# はじめに
はじめまして、ずっと大地と申します。
2023年11月より、オンラインスクールのRUNTEQにて、未経験からのWebエンジニア転職を目指して学習をしています。
初学者のうえ、記事を書くのは初めてです。
記述漏れや作成ファイルの記載忘れなど、不備があるかもしれないので、間違いなどあれば教えて頂けたらと幸いです。
また、人によって環境の違いがあるかと思うので、あくまでも私自身の環境で、記事投稿時の記録ということをお許しください。

# 概要
アプリ開発にて実装した、`Amazon S3`での本番環境の画像アップロードについて、設定が多く難しいと感じたので、記事にしたいと思います。
`CarrierWave`を導入済み、`AWS`に`rootユーザー`、`IAMユーザー`としてログインできる状態を前提とします。

https://aws.amazon.com/jp/register-flow/

# 環境
Ruby 3.2.3
Rails 7.1.3.2
CarrierWave
Amazon S3
Render.com

# 手順

### rootユーザーとしてサインイン
まずは、`rootユーザー`としてサインインします。
右上からリージョンを`アジアパシフィック（東京）`にしておきます。

### S3を検索
上部の検索欄から`S3`を検索し、`S3`を選びます。

![スクリーンショット 2024-05-09 22.39.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3773756/97b5b19f-367d-0d3d-53c8-c35d46d447aa.png)

### バケット作成
`バケット作成`のボタンを押します。

![スクリーンショット 2024-05-09 22.40.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3773756/8d4d0541-d0c7-d1f8-144e-f2e78c6b3b5d.png)

AWSリージョンは、`アジアパシフィック (東京) ap-northeast-1`で設定、`バケット名`を決め、`汎用`を選びます。
※バケットは、簡単に言うと画像を保存する場所です。

![スクリーンショット 2024-05-27 11.48.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3773756/4bf1dd59-14f4-2429-ead5-d2dbd4737793.png)

ACLを`有効`にします。

![スクリーンショット 2024-05-27 11.50.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3773756/2b36b3ff-2cbc-7a08-d73d-6ab39fcd5431.png)

下から３つの項目にチェックを入れ、他は特に変えずにスクロールしてページ下部の`バケット作成`ボタンを押します。

![スクリーンショット 2024-05-09 22.52.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3773756/4ee1c126-bf24-3d8e-6b67-13e0d972433b.png)

### バケットポリシーの設定
作成されたバケットを選択、上部に並んでいる`アクセス許可`から、`バケットポリシー`欄の`編集`を押します。

![スクリーンショット 2024-05-09 22.57.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3773756/f76a07df-4b2f-6580-5437-bb15245e2964.png)

バケットポリシーを編集します。
①、②、③を編集します。
編集ができたら、`変更を保存`を押します。
※日本語など入っていると保存できません。
```
{
    "Version": "2012-10-17",
    "Id": "Policy1544152951996",
    "Statement": [
        {
            "Sid": "Stmt1544152948221",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::①IAMユーザー時に必要な１２桁の数字:user/②IAMユーザー名"
            },
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::③バケット名"
        }
    ]
}
```
### アクセスキーの取得
`IAMユーザー`としてログインします。
上部の検索欄から`IAM`と検索し、ダッシュボードにいきます。

![スクリーンショット 2024-05-27 12.58.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3773756/651d8d12-7e97-e61a-23bf-b4b6b76781b5.png)

左側のバーから`ユーザー`を押し、オレンジ色の`ユーザーの作成`を押します。

![スクリーンショット 2024-05-27 12.59.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3773756/6cf7715c-ac1c-5b6d-22c1-326197eebdf9.png)

ユーザー名を決めて、`次へ`を押します。

![スクリーンショット 2024-05-27 13.01.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3773756/976d73f2-8bae-6257-ae17-c688157a27a4.png)

`ポリシーを直接アタッチする`を選んで、`AmazonS3FullAccess`にチェックを入れます。`s3`と検索するとすぐ見つかります。

![スクリーンショット 2024-05-27 13.03.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3773756/b755a4f1-343d-3df8-de12-7a5bdc538c4f.png)

#### アクセスキーの発行
※ここはキーの発行なので画像を載せていません。
使いたいユーザーを選び、`セキュリティ認証情報`から、`アクセスキーを作成`を押します。
`外部から...`を選んで、`完了`を押すと、アクセスキーが発行されます。
後で`.env`ファイルに記述するので、控えておいてください。
キーは後で表示されませんので、ご注意ください。

### 各設定ファイルの編集
`Gemfile`に下記を追記し、`bundle install`します。
```Gemfile
gem "aws-sdk-s3"
```

`config/environments/production.rb`の記述を、下記のように`local`から`amazon`に変更します。
```config/environments/production.rb
#config.active_storage.service = :local
config.active_storage.service = :amazon
```

`config/storage.yml`を以下のようにします。
環境変数になっている部分は後述します。
作成したバケット名を記述してください。
```config/storage.yml
amazon:
  service: S3
  access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>  
  secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
  region: ap-northeast-1
  bucket: バケット名
```
再び`Gemfile`に追記し、`bundle install`します。
```
gem 'fog-aws'
```
`carrierwave`が導入してあれば、`app/uploders/avatar_uploder.rb`のようなファイルがあると思うので、以下のようにします。
```app/uploders/avatar_uploder.rb
class AvatarUploader < CarrierWave::Uploader::Base
  # 略
  
  if Rails.env.production?
    storage :fog # 本番環境ではfogを使用
  else
    storage :file # 開発環境とテスト環境ではfileを使用
  end
  
  # 略
end
```
`config/initializers/carrierwave.rb`を作成し、以下のようにします。
コピペで問題ありませんが、①バケット名は自分のものを記述してください。
環境変数になっている部分は、`config/storage.yml`と同じ記述です。
```config/initializers/carrierwave.rb
require 'carrierwave/storage/abstract'
require 'carrierwave/storage/file'
require 'carrierwave/storage/fog'

CarrierWave.configure do |config|
    config.storage :fog
    config.fog_provider = 'fog/aws'
    config.fog_directory  = '①バケット名'
    config.fog_credentials = {
      provider: 'AWS',
      aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
      aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
      region: 'ap-northeast-1',
      path_style: true
    }
end 
```
`.env`に以下のように追記をします。
ファイルを作ったことがない方は作成してください。
ここでキーを打つことで、`config/storage.yml`と、`config/initializers/carrierwave.rb`の環境変数が使われ、GitHubに大事な情報が上がらなくなります。
```.env
AWS_ACCESS_KEY_ID=アクセスキーを記述
AWS_SECRET_ACCESS_KEY=シークレットキーを記述
```

### デプロイ
デプロイ先の設定にもキーを追加します。
私の場合は、Render.comなので、以下のように記載します。

![スクリーンショット 2024-05-27 13.25.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3773756/ffdbae79-a913-61be-2705-bccc21c4b1e5.png)

デプロイします。
本番環境で画像をアップロードする実装を試します。

`rootユーザー`としてログインし、`バケット`の`オブジェクト`に画像が保存されていたら実装完了です。
お疲れ様でした！！

![スクリーンショット 2024-05-10 16.31.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3773756/7198181c-8f56-e3c3-5e63-6109ba14c368.png)

# おわりに
AWSと聞くだけで少し身構えますが、実装ができて良かったです。
実装にあたって参考にさせていただいた記事や、詰まった時に助けていただいた記事は下に載せておきます。
ここまで読んでくださり、ありがとうございました。

# 参考記事・引用元

https://qiita.com/iijima-naoya-45b/items/0bdf75bde960787c9c04

https://qiita.com/moai891/items/69d84d41eb920917890b

https://www.jpcyber.com/support/create-iam-user-and-iam-policy

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/about-object-ownership.html