AWS-S3を使用した画像保存

画像をAWSのS3を使ってアップロードする方法について学習したので、備忘録として以下にまとめます。
デプロイはCapistranoを使用します。

#AWS-E3とは
まずはAWSについて、記述いたします。
Amazon Web Service（以下、AWS)：
Amazonが提供するインフラ系クラウドサービス。
クラウドなのでどこからでもアクセスや設定ができること、セキュリティに優れていること、様々なサービスを連携して強力なインフラを構築できることが特徴です。
https://aws.amazon.com/jp/registration-confirmation/

AWS S3：
様々なファイルを保存しどこからでも呼び出すことができるクラウドストレージサービスのひとつ。
AWSが提供しており、高いセキュリティと利便性を兼ね備えています。

#なぜやるのか。
画像をアップロードするアプリやページを作成・使用する際、投稿された画像を保存しておく場所が重要となります。
仮に画像の保存場所を、アプリケーションの/public以下とすると、アプリケーションのディレクトリ内に画像を保存している状態となってしまいます。
この状態だと、大量の画像がアップロードされた時に容量が圧迫されるという問題が発生します。
そのような事態を避けるために、Amazon Web Service S3を使用します。

#使い方
##S3での保存先を準備
バケット
S3では、クラウドのストレージをバケットと呼びます。（要はデータを入れるバケツです笑）
バケットを作成しRails側から指定することで、そのバケットに画像をアップロードしていきます。

こちらは、AWSのHPからE3に移動後、「バケットの作成」から名前とリージョンを決めるだけで簡単に作成できます。
リージョン
バケットが実際に存在しているサーバーの場所です。以下のような形で、バケットを作成しましょう。
バケットの名前は任意で決めて大丈夫ですが、他のユーザーと重複した名称は使えません。
＊必要に応じてバケットポリシーも編集しましょう。
バケットポリシー
どのようなアクセスに対してS3への読み書きを許可するか決めることができる仕組み。

##画像のアップロード先をS3に変更
まずはこのあと記述する用語について、説明します。
fog：
画像をアップロードする際、外部のストレージを選択し、アップロードするのを補助してくれるGemです。
AWSのサービスのみを利用する場合は、fog-awsというバージョンをインストールします。

それでは、早速のgemのインストールから始めましょう。
###gem編集

```rb:Gemfile
gem 'fog-aws'
```
いつものbundle installをしましょう。

```:terminal
$bundle install
```

続いて、image_uploader.rbを編集して、Uploaderをfogに設定します。

```rb:app/uploaders/image_uploaders
# Choose what kind of storage to use for this uploader:
 storage :file
# storage :fog

#以下のように変更しましょう。（fileを削除・fogを記述）
# Choose what kind of storage to use for this uploader:
 storage :fog
```

###CarrierWaveの設定ファイルを新規作成します。
アプリケーションのルートから/config/initializers直下に、carrierwave.rbというファイルを作成してください。
＊過去にCarrierWaveに関してまとめた記事を投稿してますので、興味ある方は参照ください。
CarrierWaveを使ってみた-画像のアップロード＋画像リサイズ機能の実装https://qiita.com/Tatsu88/items/66374abda7245a006ea0

```rb: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_credentials = {
    provider: 'AWS',
    aws_access_key_id: Rails.application.secrets.aws_access_key_id,
    aws_secret_access_key: Rails.application.secrets.aws_secret_access_key,
    region: 'ap-northeast-1' #ご自身の設定したregionを入れましょう。（今回’東京’で入力してます。）

  }

  config.fog_directory  = 'バケット名'
  config.asset_host = 'https://s3-リージョン名.amazonaws.com/バケット名'
end
```

###.gitignoreを編集しよう
設定ファイルの中にはGitHub上のリモートリポジトリなど、インターネットに公開すべきではないものがあります。
例えば、この後に扱うaws_access_key_idやaws_secret_access_keyが該当です。
これらをインターネット上に公開してしまうと第三者にAWSのリソースを悪用されてしまい、高額請求の被害に遭ってしまう恐れがあります。そのためにこれらは**絶対にGitHub上に公開してはいけません。**

上記のようにGitHub上のリモートリポジトリにプッシュしたくないファイルがある場合には.gitignoreを編集します。
.gitignoreに書かれたディレクトリやファイルはGitのコミット対象から外れるため、プッシュする際にリモートリポジトリに登録されることを防ぐことができます。
この後に編集するsecrets.ymlはAWSのアクセスキーを含むため、.gitignoreに追記してプッシュされないようにしましょう。

```:.gitignore
#ファイルの最下部に下記を追記
config/secrets.yml
```
*.gitignoreに記載した変更は、一度gitの監視下に置かれてしまったファイルには適用されません。過去にgitで監視下に置いていて今回.gitignoreに記載した変更を反映させるためには、config/secrets.ymlをgitの監視から外す必要があります。
次のコマンドを実行して、config/secrets.ymlをgitの監視から外してください。

```:.gitignore
$ git rm --cached config/secrets.yml
```

###環境変数を設定
続いて、S3への接続に必要な認証情報を、環境変数として設定しましょう。
CSVファイルにはそのままaws_access_key_idとaws_secret_access_keyというカラムがあるので、こちらに書かれた値をローカル環境と本番環境の両方で設定します。
この作業においては、コマンドは基本的にどこのディレクトリで入力していただいても問題ありません。
特に理由がなければホームディレクトリまたはルートディレクトリで実行しましょう。

####ローカル環境変数の設定
ローカル環境変数の設定場所は、MacOS環境によって変数の設定場所がbash_profileとzshrcで異なります。
必ず自分のMacOS環境を先に確認してから進めましょう。
今回はMacOS、Catalinaを想定して設定するので、zshrcにローカル環境変数を設定します。

```:teminal
# ローカル環境
$ vim ~/.zshrc

# iを押してインサートモードに移行し、下記を追記。
export AWS_SECRET_ACCESS_KEY='ご自身のSECRET_ACCESS_KEYを入力'
export AWS_ACCESS_KEY_ID='ご自身のACCESS_KEY_IDを入力'

# 編集が終わったらescapeキーを押してから:wqと入力して保存して終了

# 編集した.zshrcを読み込み直して、追加した環境変数を使えるようにする
$ source ~/.zshrc
```

####本番環境での環境変数設定

```:terminal
#EC2にログインしなおします
$ cd
$ cd .ssh
$ ssh -i [pem鍵の名前].pem ec2-user@[作成したEC2インスタンスと紐付けたElastic IP]
（ダウンロードした鍵を用いて、ec2-userとしてログイン）

#環境変数を記述
$ sudo vim /etc/environment
# iを押してインサートモードに移行し、下記を追記する。既存の記述は消去しない。
AWS_SECRET_ACCESS_KEY='ご自身のSECRET_ACCESS_KEYを入力'
AWS_ACCESS_KEY_ID='ご自身のACCESS_KEY_IDを入力'
# 編集が終わったらescapeキーを押してから:wqと入力して保存して終了
```
しっかりと設定できているか確認します。

```:terminal
# 編集した環境変数を適用するために一旦ログアウトします。
$ exit
#再度ログイン
$ ssh -i [pem鍵の名前].pem ec2-user@[作成したEC2インスタンスと紐付けたElastic IP]

# 先ほど記述した環境変数が適用されているか確認。
$ env | grep AWS_SECRET_ACCESS_KEY

$ env | grep AWS_ACCESS_KEY_ID
```
*Capistranoは実行時に~/.bash_profileを参照しないようなプログラムが実行されるため、~/.bash_profileに環境変数を定義しても反映されません。
よって/etc/environmentに定義します。

Capistranoを使用する本番環境(EC2)の場合・・・定義場所は/etc/environment
ローカル環境(MacOSなど)の場合・・・定義場所は~/.bash_profile（MacOSがMojave以前）、~/.zshrc（MacOSがCatalina以降）

###secrets.ymlに登録
config/secrets.ymlに外部に公開したくないIDやパスワードなどを記述して管理します。
先ほど設定した環境変数をここで読み込むように記述を変更しましょう。

```rb:config/secrets.yml
development:
  secret_key_base: ~~~~~~~~
  aws_access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>
  aws_secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>

test:
  secret_key_base: ~~~~~~~~

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  aws_access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>
  aws_secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>
```

###capistranoの記述を変更
ここまで設定した環境変数をcapistranoでの自動デプロイで利用するためには、明示的に環境変数を指定する必要があります。
Config/deploy.rb に、次の記述を追加しましょう。

```rb:config/deploy.rb
set :default_env, {
  rbenv_root: "/usr/local/rbenv",
  path: "/usr/local/rbenv/shims:/usr/local/rbenv/bin:$PATH",
  AWS_ACCESS_KEY_ID: ENV["AWS_ACCESS_KEY_ID"],
  AWS_SECRET_ACCESS_KEY: ENV["AWS_SECRET_ACCESS_KEY"]
}
```
これで、環境変数を明示的に指定することができました。
しかし、secrets.ymlは.gitignoreに追加されているため、そのままデプロイしても、本番環境のリポジトリには追加されません。
こちらも、明示的に本番環境にアップロードするよう、記述を追加する必要があります。

```rb:config/deploy.rb
# secrets.yml用のシンボリックリンクを追加
set :linked_files, %w{ config/secrets.yml }

#デプロイ処理が終わった後、Unicornを再起動するための記述
after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
  task :restart do
    invoke 'unicorn:restart'
  end

#config/secrets.ymlを本番環境のshared/config/secrets.ymlに反映するための設定
  desc 'upload secrets.yml'
  task :upload do
    on roles(:app) do |host|
      if test "[ ! -d #{shared_path}/config ]"
        execute "mkdir -p #{shared_path}/config"
      end
      upload!('config/secrets.yml', "#{shared_path}/config/secrets.yml")
    end
  end
  before :starting, 'deploy:upload'
  after :finishing, 'deploy:cleanup'
end
```

上記で記述は完了です。
いよいよデプロイ作業となりますが、下記項目をチェックしましょう。
#デプロイ前のチェック
##1.MySQLの起動を確認
MySQLが立ち上がっていないとデプロイが失敗します。以下のコマンドで再起動をしておきましょう。

```:terminal(EC2サーバー)
$ sudo service mysqld restart
```

##2.unicornのプロセスをkill
自動デプロイを実行する前にunicornのコマンドをkillしておきましょう。

```:terminal(EC2サーバー)
#まず、プロセスを確認
$ ps aux | grep unicorn

#続いてプロセスをkill
$ kill <確認したunicorn rails masterのPID>
```

##3.ローカルでの修正を全てmasterにpush
ローカルでのコードの変更が、全てmasterにpushされていることを確認しておきましょう。

以上で準備は完了です！デプロイしましょう！

#デプロイ実行！
ローカルのターミナルで以下のコマンドを実行しましょう。

```:terminal
# アプリケーションのディレクトリに移動してから実行
$ bundle exec cap production deploy
```

以上となります。最後までご覧いただき、ありがとうございました！
今後も学習した事項に関してQiitaに投稿していきますので、よろしくお願いします！
記述に何か誤りなどございましたら、お手数ですが、ご連絡いただけますと幸いです。