docker + carrierwave + amazon S3環境の画像アップロードで発生したエラーと対策

## 発生事象

前提として、開発環境では問題なくAWSに画像アップロードされている事が確認取れていた状態で以下の事象が発生していました。

### 1: デプロイ出来ない問題
・環境変数がデプロイ時に定義されていないと言われ、デプロイ失敗する状況が発生。

### 2: 本番環境でAWSに画像アップロード出来ない問題

・本番環境にて画像アップロード時にエラーが発生し、アップロード出来ない状況が発生。

## 結論
1,2の事象の原因は、dockerfileの設定でした。


## 実行環境
本番環境：`dockerfile`
開発環境：`dockerfile.dev`


初めてのdocker環境構築であったため、所属スクールの環境設定資料を参考に`dockerfile.dev`を作成して使用。

しかし、初期の環境構築のタイミングで、`dockerfile.dev`とは別に`dockerfile`というファイルも作成した（らしい）。
正直な話、`dockerfile`作成した記憶が無く、いつの間にか`dockerfile`をメインで使用していると錯覚していた。

最初のこの時点で、開発環境と本番環境が異なる環境になっていた模様。

---
carrierwaveやAWSの設定は他に分かりやすい資料が山のようにあるので本記事では言及しません。私が一番最初に参考した記事はこちらです。

https://qiita.com/mattan5271/items/96721426cff2b1d67cdd#%E5%89%8D%E6%8F%90

調べてみると、AWSに以下のgemを使う話が出てきますが、どれも使用できるのでお好きなgemをお使いください。
・ gem 'fog-aws'
・ gem "aws-sdk-s3", require: false
・ gem 'carrierwave-aws'

## 発生したエラー

環境設定は問題なく進み、開発環境でAWSに画像アップロードができるようになったところで、いざ本番環境へデプロイ！

### 1: デプロイ出来ない問題が発生


```
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
bin/rails aborted!
ArgumentError: Missing required arguments: aws_access_key_id, aws_secret_access_key (ArgumentError)
raise ArgumentError, "Missing required arguments: #
```

これは下記で設定している環境変数がassets:precompileの時点で読み込めないために発生しているエラー。
`config/initializers/carrierwave.rb`
```
CarrierWave.configure do |config|
    config.storage :fog
    config.fog_provider = 'fog/aws'
    config.fog_directory  = 'myicode' # バケット名
    config.fog_public = false
    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
    }
```

これの解決方法は環境変数のダミー値を渡すことで解決出来る。
下記記事の"Access to Environment Variables at Build Time"が参考文献です。

https://fly.io/docs/rails/getting-started/existing/#access-to-environment-variables-at-build-time

```
Precompiling assets for production without requiring secret RAILS_MASTER_KEY
# 変更前のコード
# RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile

# 変更後のコード
RUN SECRET_KEY_BASE_DUMMY=1 AWS_ACCESS_KEY_ID=dummy AWS_SECRET_ACCESS_KEY=dummy ./bin/rails assets:precompile
```

これでデプロイ時のassets:precompileにて発生する環境変数のエラーは解消出来た。


### 2.本番環境でAWSに画像アップロード出来ない問題

デプロイ出来ない問題が解決した後に発生したのがこのエラー。
`/rails/public/uploads`を作成する権限が無いというものである。
```
Errno::EACCES (Permission denied @ dir_s_mkdir - /rails/public/uploads):
```

これは、`app/uploaders/image_uploader.rb`の以下記載に画像を保存するURLが記載されており、このフォルダが作成出来ないということを示している。
```
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
```


#### ここで２つの大きな勘違いをした。
---
>① 開発環境でAWSにアップロード出来ているのだから、本番環境でアップロード出来ないのは、環境設定には問題ないだろう。

特に、`dockerfile`と`dockerfile.dev`に分かれているという事が理解出来ていなかったため、ほとんど同じ環境だろうと思いこんでいた。
そして設定の差異は、`image_uploader.rb`の中で設定された程度の差異しか無いだろうと考えていた。

>② AWS保存ではなく、ローカル保存の設定になっている。エラーの意味はローカルフォルダの作成権限が無い事を示しているのだろう。

ここは正直、AIの言葉を鵜呑みにしてしまったが故に発生した勘違いでもある。
エラーコードをAIに投げて、ローカルの事を示しているのだと理解した。

しかし、冷静に考えると、AWS内でも同様のディレクトリ構成でファイル作成されるため、このエラー文だけでは、ローカルの権限異常なのか、AWSの権限異常なのかは判断出来ないはずであった。

解決策としては、`dockerfile`に下記のコードを追記することで解決した。
```
...
RUN useradd rails --create-home --shell /bin/bash && \
    chown -R rails:rails db log storage tmp
# 下記の1文を追加した。
RUN mkdir -p /rails/public/uploads/tmp && chown -R rails:rails /rails/public/uploads
USER rails:rails
...
```

やったこととしては、権限付与しただけで、環境構築について理解した上でエラーと直面できていれば、もう少し早く解決出来たように思う。

ひとまず、これにてエラーは無事解決し、本番環境においてもAWSに画像をアップロードできるようになった。


## 反省点
・環境構築について理解不十分なまま進めてしまった。
・`dockerfile`の作成要否をしっかり考えるべきである。
・errorを正確に読み取れていなかった。

以上となります。

同様の事象が発生することはレアケースだと思いますが、もし同じ事象で苦しんでいる方がいれば、助けになれることを願います。