Rails5でherokuデプロイする！CarrierWave+fog+S3+redis+mailerを使う！

herokuを使ったRailsアプリのデプロイについての記事です！scaffoldで作ったアプリをデプロイするだけだとだいぶ楽ですが、通常のちゃんとしたアプリの機能を備えたものをデプロイするのは中々作業が多いです。ただ、この記事の通りやれば、デプロイできるので、安心してください！

## 自分の環境とアプリの前提

### バージョン

Rails 5.2.3
Ruby 2.6.3

## 前提

- 画像の投稿あり
- セッション管理はredis
- mailerを使用

## Railsアプリをデプロイするための基本的な流れ
やることを整理すると以下の作業があります。

- herokuへの登録
- herokuの機能を自分のPCに紐付け(herokucliをダウンロード)
- config/routes.rbの設定
- Gemfileの設定
- config/datebase.ymlの設定
- config/environments/production.rbの設定
- redisの設定（使っている人だけ）
- S３の設定
- yarn、node関係
- rubybuild
- credentials.yml.encを使うときはherokuにmasterキーを登録
- mailer使う場合はmailerの設定

めっちゃありますね。AWSでのデプロイよりは簡単なんですが、それでもこれだけあります！私のやったことをまとめて書いていきます！

## herokuへの登録
まずは、herokuに登録します。

https://signup.heroku.com/login

会員登録が完了したら、今度はherokuの機能を自分のPCに紐付けましょう。cliをダウンロードします。

https://devcenter.heroku.com/articles/heroku-cli

私はmacですので、ターミナルでherokuのページでの指示通り以下のコマンドでインストールしました。homebrewを使ってインストールします。

```
$ brew tap heroku/brew && brew install heroku
```

## Railsアプリとherokuの紐づけ

まずは、自分のPCからherokuにログインします。ネットに繋げていないといけないので、ご注意を！

PCからherokuにログイン
```
$heroku login
```

そうすると、herokuに登録したemailとpasswordの入力を求められるので、指示された通りに打ち込んでください。


## heroku上にアプリのフォルダを作成

以下のコマンドで、作成

```
$ heroku create 好きなアプリ名
```

doneとなっていれば完了！

[![Image from Gyazo](https://i.gyazo.com/f553434ff8e6a70421092fb0afe34bfe.png)](https://gyazo.com/f553434ff8e6a70421092fb0afe34bfe)

Name is already taken...」となっていたら、名前がすでに使われているので、違う名前にしましょう！


[![Image from Gyazo](https://i.gyazo.com/ba1da6016de97cc33ac8f7d218595f36.png)](https://gyazo.com/ba1da6016de97cc33ac8f7d218595f36)


## config/routes.rb

configフォルダの中のroutes.rbについて、トップページのルーティングをきちんと作ります。
大事なのはトップページを何かしらに設定していていないとページがありませんというエラーがで続けます。


## Gemfileの設定
次に、Gemfileを変更していきます。

私の場合はミドルウェアにmysqlを使っているので、こんな感じに書き直しました。

```ruby:Gemfile

色々省略

gem 'mysql2', '>= 0.4.4', '< 0.6.0', group: [:development, :test]


group :production do
  gem 'pg'
end

```
意味は、「mysqlはdevelopment環境とtest環境でしか適用しない」です。
これからデプロイする予定であるherokuではPostgreSQL(略してpg)というデータベースが最初からインストールされているので、その環境に合うようにこのような変更をしています。

さて、Gemfileを変更したので忘れずにbundle installをしておきましょう。ただし、以下のようにオプションを指定します。

```
$bundle install --without production
```

このオプションをつけることによって、group :produciton do~end内以外のgemをインストールすることができます。

ちなみに、このオプションは一度打ち込めば、アプリ内部の/bundle/configというところに書き込まれるため、それ以降は--without producitonを打たなくても本番環境以外のgemをインストールしてくれるようになります。その他のbundle install時のオプションは全てここに書き込まれます。

```ruby:.bundle/config
#このようになっていたらOKです
BUNDLE_WITHOUT: "production"
```

## config/datebase.ymlの設定

さきほどGemfileを変更したことによりこのアプリは本番環境でPostgreSQLを使います、という宣言（と機能をインストールするための準備）は完了したのですが、実際にデータベースと接続する記述はまだ完了していません。その接続の設定をする箇所がconfigフォルダの中にあるdatabase.ymlというファイルで、これを変更する必要があります。

ファイル内の下の方にあるproduction環境についての設定を、以下のように記述します。インデントに注意です！ずれてるとダメです！

```ruby:config/database.yml
production:
  <<: *default
  adapter: postgresql
  encoding: unicode
  pool: 5
```

## config/environments/production.rbの設定

Railsは本番環境でのプリコンパイルがデフォルトでオフになっています。
画像を表示するために以下の記述をfalseからtrueに変更して対応することも可能ですが、毎回読み込む度にコンパイルして配信するファイルを作るためパフォーマンスが悪くなります。
この記述により、assets以下のフォルダから動的にコンパイルしながらページを読み込むことになります。

```ruby:config/environments/production.rb
#デフォルトでfalseとなっている以下の箇所をtrueに変更
  config.assets.compile = true
```

設定としてはこちらのやり方が推奨です。

```config/environments/production.rb
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
```
環境変数'RAILS_SERVE_STATIC_FILES'をtrueで設定してpublic配下のコンパイルしたファイルを静的に配信するようにします。


ここから先が結構厄介でした。。。。

## S3の設定（Ruby + Heroku + carrierwave )

herokuデプロイで、投稿した画像データを表示するには、AWSのS3を設定し、そこに画像を保存する必要があります。

ここでの流れは、

①AWSのアカウントを作成する
②AWS Identity and Access Management (IAM)でユーザーとグループ(AmazonS3FullAccess)作成（アクセスキーとシークレットアクセスキーは後から使うのでcsvはダウンロードしておくと良い）
③作成したユーザーでログインし、サービス「S3」にてバケットを作成する

こちらの記事を参考にしました。
https://qiita.com/params_bird/items/dc2a3868f4a2caf0504c

## 設定コードの修正

### fogをインストール、本番環境での保存先設定

fogをインストールします。Gemfileに含めたらbundle installします。

```ruby:Gemfile
gem 'fog'
```

開発環境ではローカルファイルに画像保存で問題ありませんが、本番環境ではクラウドストレージサービス（fog）に保存するように設定します。

Carrierwaveをインストールした際に生成したuploaderファイルに以下を追加します。

```ruby:app/uploaders/avatar_uploader.rb
if Rails.env.development?
    storage :file
  elsif Rails.env.test?
    storage :file
  else
    storage :fog
  end
```

### carrierwaveの設定

私はcredentials.ymlに実際のキーなどを記載して、それを呼び出す方法をとりました！呼び出す際の設定ファイルの書き方は以下です！

```ruby:config/initializers/carrierwave.rb
if Rails.env.production?
  CarrierWave.configure do |config|
    config.fog_credentials = {
        provider: 'AWS',
        aws_access_key_id: Rails.application.credentials.aws[:access_key_id],
        aws_secret_access_key: Rails.application.credentials.aws[:secret_access_key],
        region: Rails.application.credentials.aws[:region]
    }

    config.fog_directory = Rails.application.credentials.aws[:fog_directory]
    config.cache_storage = :fog
  end
end

```

credentials.ymlは暗号化されて表示されるので、ファイルを直に弄って記載できないので、以下のコマンドでvimを起動して記載します！

```
$ EDITOR="vi" rails credentials:edit
```

そうすると、こんな感じでvimが起動され、中身が編集できるようになります。

```
aws:
  access_key_id: DRFJUJGFHJ...........（ここは実際のキーが入ります）
  secret_access_key: CEDGRGFHJ...........（ここは実際のキーが入ります）
  region: ap-northeast-1（東京リージョンなら）
  fog_directory: バケット名が入ります
```
インデントに注意して、このような形で定義すると、``Rails.application.credentials.aws[:access_key_id]``などいった形で呼び出すことができます。

これだけだと、実はまだダメです！
credential.ymlは、``master.key``が無いと開くことができないようになっており（秘密鍵と言います）、これをheroku上に登録してあげなければなりません。
以下のコマンドを打てば、登録できます！

```
$ heroku config:set RAILS_MASTER_KEY=`cat config/master.key`
```

## yarn,rubybuild

ここの設定をしていないと、デプロイコマンドを叩いた時に、``「rails assets:precompile」``の部分でエラーが出ます。yarnを使えるようにしておきます。

```
$ heroku buildpacks:add --index 1 heroku/nodejs

$ heroku buildpacks:add --index 2 heroku/ruby

```

このコマンドを打っておきましょう！

## redisの設定

### Heroku CLI で アドオン を追加

heroku CLI で heroku-redis アドオンを追加する。herokuサイト上からもGUIで追加は可能
ダッシュボード上の「Configure Add-ons」からも可能！
[![Image from Gyazo](https://i.gyazo.com/ecfdec95aea01528943cafb796e87e18.png)](https://gyazo.com/ecfdec95aea01528943cafb796e87e18)

コマンドの場合は、

```
$ heroku addons:create heroku-redis:hobby-dev 
```

ここで、herokuのサイト上でクレジットカードを登録しておかないと、追加できないことがあるので、その時はクレジットカードを登録しておいてください。（無料のサービスしか使っていなければ課金されることはありません）

### heroku CLI で 環境変数 REDIS_URL (Redis のエンドポイント)が追加されてることを確認する

```
$ heroku config

=== shakestagram Config Vars
...
REDIS_URL: redis://xxxxx@xxxxx:xxxxx
...

```


### config/environments/production.rbの設定

```ruby:config/environments/production.rb
# この一行追加
config.session_store :redis_store, servers: ENV['REDIS_URL'], expire_after: 1.day
```


## production環境でのActionMailerの設定

HerokuのアドオンSendGridを追加します！

```
$ heroku addons:create sendgrid:starter
```

ENV['SENDGRID_USERNAME'とENV['SENDGRID_PASSWORD']の環境変数が上のコマンド一つでセットされます。

```ruby:config/environments/production.rb
config.action_mailer.delivery_method = :smtp
  config.action_mailer.default_url_options = { host: 'アプリのURL' }
  config.action_mailer.perform_deliveries = true
  config.action_mailer.smtp_settings = {
      user_name: ENV['SENDGRID_USERNAME'],
      password: ENV['SENDGRID_PASSWORD'],
      domain: 'herokuapp.com',
      address: 'smtp.sendgrid.net',
      port: 587,
      authentication: :plain,
      enable_starttls_auto: true
  }
```

私は、メールの永続化にsidekiqというツールを使っているので、その設定ファイルに環境変数のURLをコピペして記載します。コピペするのは、環境変数 REDIS_URL (Redis のエンドポイント)です！
。本番環境と開発環境で分岐。

```
$ heroku config

=== shakestagram Config Vars
...
REDIS_URL: redis://xxxxx@xxxxx:xxxxx
...

```

```ruby:sidekiq.rb
if Rails.env.development?
  Sidekiq.configure_server do |config|
    config.redis = {
        url: 'redis://localhost:6379'
    }
  end

  Sidekiq.configure_client do |config|
    config.redis = {
        url: 'redis://localhost:6379'
    }
  end
else
  Sidekiq.configure_server do |config|
    config.redis = {
        url: 'redis://xxxxx@xxxxx:xxxxx'
    }
  end

  Sidekiq.configure_client do |config|
    config.redis = {
        url: 'redis://xxxxx@xxxxx:xxxxx'
    }
  end
end
```

## やっとherokuにデプロイ

変更したファイルを全て、git add、git commitしたあと、

```
$git push heroku master
```

これだけでデプロイできます。

## 本番環境でrails db:migrate


```
$ heroku run rails db:migrate
```

このようにheroku runをつけるとheroku上でrailsコマンドを打つことができます。

###  heroku run rails db:migrateが通らない場合

私の場合は、以下のコマンドを打ったあと、migrateすると通りました！

```
$ heroku addons:create heroku-postgresql
```