Carrierwaveとfog-awsを利用したS3へのアップロードが正常にできるまでに試したこと

# 概要
下記の記事を参考に、Carrierwaveとfog-awsを利用したS3へのアップロードができるように設定した。

https://qiita.com/take18k_tech/items/2efd778c8dd9aae9496e#10-s3

本番環境へデプロイ後、動作を確認してみると、下記の状況となっていた。

- 画像をアップロードしても、
    - S3バケットには何も表示されない
    - /var/www/my-app/current/public/uploads配下に保存されている
- アクセスはできるが、CSSが反映されなくなっている


# 環境
- アプリ
  - ruby 3.0.2
  - rails 6.1.4
  - mysql 8.0.31

- EC2AMI
    - Amazon Linux2 AMI 
    - app server : Unicorn
    - web server : Nginx


# エラー①
/var/www/my-app/current/logをそれぞれ確認。
関係ありそうなのは下記のログ。
```var/www/my-app/current/log/unicorn.stderr.log
/var/www/my-app/shared/bundle/ruby/3.0.0/gems/carrierwave-2.2.2/lib/carrierwave/storage/fog.rb:68:in `eager_load': uninitialized constant Fog (NameError)
```

## 試したこと①
config/initializers/carrierwage.rbの記述の順番を変える。

> 参考

https://github.com/carrierwaveuploader/carrierwave/issues/2023

> Moving config.storage = :fog after config.fog_credentials = ... line will solve it.


## 変更前
```rb:carrierwave.rb
if Rails.env.production?
  CarrierWave.configure do |config|
    config.storage = :fog
    config.fog_provider = 'fog/aws'
    config.fog_directory = 'S3バケット名'
    config.asset_host = 'https://s3-ap-northeast-1.amazonaws.com/S3バケット名'
    config.fog_credentials = {
      provider: 'AWS',
      aws_access_key_id: Rails.application.credentials.dig(:aws, :access_key_id),
      aws_secret_access_key: Rails.application.credentials.dig(:aws, :secret_access_key),
      region: 'ap-northeast-1'
    }
  end
end
```

## 変更後
```rb:carrierwave.rb
if Rails.env.production?
  CarrierWave.configure do |config|
    config.fog_credentials = {
      provider: 'AWS',
      aws_access_key_id: Rails.application.credentials.dig(:aws, :access_key_id),
      aws_secret_access_key: Rails.application.credentials.dig(:aws, :secret_access_key),
      region: 'ap-northeast-1'
    }
    config.storage = :fog
    config.fog_provider = 'fog/aws'
    config.fog_directory = 'S3バケット名'
    config.asset_host = 'https://s3-ap-northeast-1.amazonaws.com/S3バケット名'
  end
end
```

## 結果

デプロイし直したが変わらず。

## 試したこと②

エラー原因を探るため、carrierwave.rb等の設定を細かく調整したい。
そこで、EC2上でcarrierwave.rbを修正→unicorn再起動を実施。
そうすると、ひとまずCSSは反映された。
ただし、画像の投稿については、投稿ボタン押下すると、
> We're sorry, but something went wrong.

のエラーとなった。

# エラー②

ログ
```var/www/my-app/current/log/production.log
~(省略)~The bucket does not allow ACLs~(省略)~
```
画面上でもエラーが出ている状態。
```console:ChromeのConsole
✖︎ GET https://s3-ap-northeast-1.amazonaws.com/バケット名/uploads/user/image/5/画像.jpeg 403 (Forbidden)
```

## 試したこと③
上記のエラーから、S3の権限設定が怪しい。

> 参考

https://take-engineer.com/the-bucket-does-not-allow-acls/

こちらを参考に、バケットの設定を変更。
- バケットを選択
- アクセス許可
- オブジェクト所有者
- ACL有効に切り替え
- オブジェクト所有者をオブジェクトライターに設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/948830/21dd8862-2e67-3293-2995-19c49f6e5ef5.png)

※ACLを無効にして設定する方法は[こちらに追記](#追記)

## 結果
- 画像の投稿、表示ともに正常にできた。
- S3バケットにアップロードされていることが確認できた。

# 補足
上記までの設定を行った上で、
- 当件とは無関係の箇所を修正し、Capistranoでデプロイ
- ブラウザでアクセスできることを確認（CSSは反映されている）
- 画像を投稿

すると、再度下記のエラーが発生し、CSSも反映されなくなった。
> We're sorry, but something went wrong.

- 再度unicornを再起動
- 画像を投稿
すると、正常に投稿ができた。

ただ、自動デプロイ後の時点で正常な動作ができないと困るため、ログを調査して対応。

```var/www/my-app/current/log/production.log
# 該当コントローラのPOST直後
~(省略)~ Completed 500 Internal Server Error in 188ms (ActiveRecord: 0.7ms | Allocations: 2273) ~(省略)~
~(省略)~ ArgumentError ( is not a recognized provider): ~(省略)~
```

config/initializers/carrierwage.rbがうまく読み込めていない？

> 参考

https://github.com/ma96o/chatspace/issues/25

https://teratail.com/questions/213761

最終的には、前述の「変更後」のcarrierwave.rb冒頭に、requireを追加すると、デプロイ直後から正常な状態で動作することが確認できた。

```rb:carrierwave.rb
require 'carrierwave/storage/abstract'
require 'carrierwave/storage/file'
require 'carrierwave/storage/fog'

if Rails.env.production?
  CarrierWave.configure do |config|
    config.fog_credentials = {
      provider: 'AWS',
      aws_access_key_id: Rails.application.credentials.dig(:aws, :access_key_id),
      aws_secret_access_key: Rails.application.credentials.dig(:aws, :secret_access_key),
      region: 'ap-northeast-1'
    }
    config.storage = :fog
    config.fog_provider = 'fog/aws'
    config.fog_directory = 'S3バケット名'
    config.asset_host = 'https://s3-ap-northeast-1.amazonaws.com/S3バケット名'
  end
end
```
※require追加を反映する直前にEC2インスタンスの再起動を行ったので、そちらも影響しているかも？


# 最後に
- ACLは昔から存在している制御機能であり、大まかな設定しかできない
- ポリシーでの管理では、細かい設定ができる
- 現在はACL無効を選択できるようになり、アクセス制御はポリシーによって一括管理することが可能となった
とのことなので、ポリシーによる制御を実現していきたい。

> 参考

https://dev.classmethod.jp/articles/s3-bucket-owner-enforced/

https://qiita.com/K5K/items/ef6a02aee760b128a25b

https://www.bioerrorlog.work/entry/s3-bucket-policy-vs-iam-policy

https://dev.classmethod.jp/articles/amazon-s3-acl-basics/



# 追記
```var/www/my-app/current/log/production.log
~(省略)~The bucket does not allow ACLs~(省略)~
```
こちらのエラーの対応方法について、
config/initializers/carrierwage.rbにconfig.fog_public = false を記述することで、下記設定でもS3へのアップロードが可能になることがわかった。
- ACL無効
- （IAMポリシーにAmazonS3FullAccessをアタッチしていれば）S3バケットポリシーは記述なしでOK
※CloudFrontをOAIで利用する場合は、S3バケットポリシー設定が必要。
- パブリックアクセスは全てブロックしてOK


> 参考

https://zenn.dev/tanktabox/articles/b199e654677078