【デプロイ】Dockerで開発環境構築したRailsアプリをHerokuで本番公開する（M1Mac）

# 1. 事前準備
前提：DockerでRailsの環境構築を行っておく（目安：15分）

https://qiita.com/rexid/items/4ea724cef462560039da

---
**Gitの初期設定を行う**
    ※下記コマンドすべて実行
```terminal
$ cd Documents/rails_docker
$ git config --global user.name "GitHubに登録したユーザネーム"
$ git config --global user.email "GitHubに登録したメールアドレス"
$ git config --global merge.ff false
$ git config --global pull.rebase merges
```
- `git config --global merge.ff false`・・・mergeの際にファストフォワードが起こらないようにする設定
- `git config --global pull.rebase merges`・・・pullの時に常にrebaseする設定

---
**設定の確認**
```terminal
$ git config --list
```

# 2. Herokuにログイン
**Heroku新規登録** ※既に登録済みの場合はSkip

https://signup.heroku.com/jp

- クレカ登録
    - アイコンクリック→Account Settings→Billing
    - https://dashboard.heroku.com/account/billing
        - 無料枠の範囲内で行っていくが、MySQLをアドオンで追加する時にクレカを登録しておかないと追加できないため登録しておく

---
**Heroku CLIをインストール** ※インストール済みの場合はSkip
- Herokuをコマンドで操作できるようにするツール

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

:::note warn
HerokuCLIインストール時にエラーが発生した場合は[こちら](https://qiita.com/rexid/items/d2fc0178bb0bad975aba)も参考に
:::

---
**Herokuにログイン**
- rails_docker（任意で作成したアプリケーション名の）ディレクトリで行う
```terminal
$ cd ~/Documents/rails_docker/
$ heroku login
```
- 上記コマンドが実行されたらEnterキーを一度押す
    - ブラウザに遷移するのでログインボタンを押下
    ![スクリーンショット 2022-09-23 13.00.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2689074/bcb91da2-7ca6-3acb-ad33-8a0bc2a9d418.png)

```terminal:terminal
(base) sample@SampleMBP rails_docker % heroku login
›   Warning: Our terms of service have changed: https://dashboard.heroku.com/terms-of-service
heroku: Press any key to open up the browser to login or q to exit:
Opening browser to https://cli-auth.heroku.com/auth/cli/browser/e99d7720-5888-4587-85a2-bb1c591b335e?requestor=SFMyNTY.g2gDbQAAAAs2MC43OC43LjE3M24GAKC0MQmDAWIAAVGA.rmy7BA2sWUS2qp2udTXooa-GOABuSW_ZdvzkwGJs3IM
Logging in... done
Logged in as 自分のメールアドレスが表示される
```

---

**Herokuコンテナレジストリにログイン**
- `レジストリ`・・・Dockerのイメージ置き場
    - ここにログインすることで、コンテナレジストリにイメージをUP出来るようになる
```terminal
$ heroku container:login
```
`Login Succeeded`と出ていればOK

# 3. Herokuアプリを作成
- 世界中の他のアプリ名と重複できないため、アプリ名は任意で命名する
    - 今回は`rails-docker-gucci`という名に:sunglasses:
```terminal
$ heroku create アプリ名
```

```terminal:terminal
(base) sample@SampleMBP rails_docker % heroku create rails-docker-gucci
Creating ⬢ rails-docker-gucci... done
https://rails-docker-gucci.herokuapp.com/ | https://git.heroku.com/rails-docker-gucci.git
```

# 4. DBの追加 ・ 設定

**追加**

- Herokuではデータベースをアドオンという形で作成する
    - MySQLの場合、アドオンはいくつか存在するが、`cleardb:ignite`というプランだけ無料
    - 無料なのはMySQLバージョン5系
        - 	本来はローカルのSQL（Dockerfileに記載されている）とバージョンを揃えた方が良いが、今回はバージョン違いで困ることがないのでそのまま実行

```terminal
$ heroku addons:create cleardb:ignite -a アプリ名
```
- `-a`はアプリの略

```terminal:terminal
(base) sample@SampleMBP rails_docker % heroku addons:create cleardb:ignite -a rails-docker-gucci
Creating cleardb:ignite on ⬢ rails-docker-gucci... free
Created cleardb-asymmetrical-43068 as CLEARDB_DATABASE_URL
Use heroku addons:docs cleardb to view documentation
```

---

**設定**
- railsの接続先を変えておく
    - productionのデータベースの接続先情報は環境変数にして渡してあげる
    - 環境変数にすることで、ソースコードにデータベースの接続先情報を載せなくて済む＆接続先を環境毎にも切り替えられる

```yml:修正前database.yml
production:
  <<: *default
  database: app_production
  username: app
  password: <%= ENV['APP_DATABASE_PASSWORD'] %>
```

```yml:修正後database.yml
production:
  <<: *default
  database: <%= ENV['APP_DATABASE'] %>
  username: <%= ENV['APP_DATABASE_USERNAME'] %>
  password: <%= ENV['APP_DATABASE_PASSWORD'] %>
  host: <%= ENV['APP_DATABASE_HOST'] %>
```

---

**データベースの接続先情報の確認**
```terminal
$ heroku config -a アプリ名
```

```terminal:terminal
(base) sample@SampleMBP rails_docker % heroku config -a rails-docker-gucci
=== rails-docker-gucci Config Vars
CLEARDB_DATABASE_URL: mysql://b659fc19e4d6cb:3adea8dc@us-cdbr-east-06.cleardb.net/heroku_6a7fdff24c98449?reconnect=true
```

- 仕組み
    - `mysql://ユーザ名:パスワード@ホスト名/データベース名?reconnect=true`

---

**確認した接続先情報を環境変数に設定していく**

`heroku config:add`と打つことで、環境変数を登録できる

```terminal:例
$ heroku config:add APP_DATABASE='heroku_6a7fdff24c98449' -a rails-docker-gucci
$ heroku config:add APP_DATABASE_USERNAME='b659fc19e4d6cb' -a rails-docker-gucci
$ heroku config:add APP_DATABASE_PASSWORD='3adea8dc' -a rails-docker-gucci
$ heroku config:add APP_DATABASE_HOST='us-cdbr-east-06.cleardb.net' -a rails-docker-gucci
```

---

**環境変数が設定できているか確認**

```terminal
$ heroku config -a アプリ名
```

```terminal:terminal
(base) sample@SampleMBP rails_docker % heroku config -a rails-docker-gucci
=== rails-docker-gucci Config Vars
APP_DATABASE:          heroku_6a7fdff24c98449
APP_DATABASE_HOST:     us-cdbr-east-06.cleardb.net
APP_DATABASE_PASSWORD: 3adea8dc
APP_DATABASE_USERNAME: b659fc19e4d6cb
CLEARDB_DATABASE_URL:  mysql://b659fc19e4d6cb:3adea8dc@us-cdbr-east-06.cleardb.net/heroku_6a7fdff24c98449?reconnect=true
```

# 5. Dockerfileを本番環境用に修正
**アプリケーションディレクトリ直下に`start.sh`というファイルを作成**
```terminal
$ cd ~/Documents/rails_docker/
$ touch start.sh
```

---
**Dockerfileを修正**
- 環境変数設定
- start.shの設定を記述

```Dockerfile:Dockerfile
# Rubyの2.7.5を使用
FROM ruby:2.7.5

# 環境変数設定
ENV RAILS_ENV=production

# 必要なライブラリをインストール
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
  # nodejsとyarnをインストールするために必要なライブラリのアップデート関連
  && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
  && apt-get update -qq \
  && apt-get install -y nodejs yarn # nodejsとyarnをインストールしている

# 今回は作業ディレクトリを/appとする
WORKDIR /app

# ローカルの./src（railsのソースコードを入れる場所）以下のソースコードを/app配下にコピー
COPY ./src /app

# RubyのGemfileを一括インストール
RUN bundle config --local set path 'vendor/bundle' \
  && bundle install

# ローカルのstart.shをDocker側にコピー
COPY start.sh /start.sh
# 実行権限を付与
RUN chmod 744 /start.sh
# start.shを実行
CMD ["sh", "/start.sh"]
```

---
**start.shに記載**
```start.sh
#!/bin/sh

if [ "${RAILS_ENV}" = "production" ]
then
  bundle exec rails assets:precompile
fi

bundle exec rails s -p ${PORT:-3000} -b 0.0.0.0
```

**解説**
- `#!/bin/sh`・・・`start.sh`はシェルスクリプトなので1行目に記載する
- `if [ "${RAILS_ENV}" = "production" ]`・・・${RAILS_ENV}がproductionの時（本番環境の時だけ実行したい）
- `bundle exec rails assets:precompile`を実行（ローカルでは必要ないが、本番ではこのコマンドを実行する必要がある）
    - ※ rails特有の事情・・・cssやjsのファイルをひとまとめにしてからhtmlで一発で読み込めるようにしている
        - そのための仕組みが`assets:precompile`で、ローカルと本番だとここの挙動が違い、本番ではこのコマンドを実行しなければならない
- `bundle exec rails s`・・・Railsサーバを起動
- `${PORT:-3000}`・・・環境変数でPORTの値が入っていたらそれを使い、何も設定がなかった時は3000番ポートを使用
- `-b 0.0.0.0`・・・バインドでIPアドレスを設定。`0.0.0.0`はどこからのIPアドレスでもOK

:::note info
このように、環境特有の処理を入れる時はDockerfileに設定を書き、シェルスクリプトを作成し条件分岐させる
:::

---

**環境変数を追加**
- `assets:precompile`が本番で起動するようになる
```terminal
$ heroku config:add RAILS_SERVE_STATIC_FILES='true' -a アプリ名
```

---
**Boot Timeoutの秒数を増やす**
- Herokuをデプロイした時にポート番号をサーバがバインドする
    - その時にデフォルトで60秒で設定されている
        - 無料枠だとマシンパワーが弱く、60秒だとバインドできずにエラーとなることがよくある

https://tools.heroku.support/limits/boot_timeout
![スクリーンショット 2022-09-23 15.50.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2689074/87e978d0-5668-a0b0-ab96-70d21dcb6fe5.png)
`Change Boot Timeout`ボタン押下

---
**ローカルのwebサーバを削除しておく**

:::note warn
ローカルでwebサーバが起動しているとHerokuでwebサーバが起動しないことがあるため
:::

```terminal
$ docker-compose down
$ rm src/tmp/pids/server.pid
```
`docker-compose down`を行えば`src/tmp/pids/server.pid`も削除されるはずだが、これが残っているとHerokuでエラーとなってしまうため念のため実行する

# 6. Dockerイメージをビルド ・ リリース
**Dockerイメージをビルドし、コンテナレジストリに上げたイメージからHeroku上にDockerコンテナをリリース**

```terminal
$ docker buildx build . --platform linux/amd64 -t 任意のタグ名/アプリ名
$ docker tag タグ名/アプリ名:latest registry.heroku.com/アプリ名/web
$ docker push registry.heroku.com/アプリ名/web
$ heroku container:release web -a アプリ名
```

```terminal:例
$ docker buildx build . --platform linux/amd64 -t gucci/rails-docker-gucci:latest
$ docker tag gucci/rails-docker-gucci:latest registry.heroku.com/rails-docker-gucci/web
$ docker push registry.heroku.com/rails-docker-gucci/web
$ heroku container:release web -a rails-docker-gucci
```

---

**マイグレーション**
```terminal
$ heroku run bundle exec rake db:migrate RAILS_ENV=production -a アプリ名
```

```terminal:例
(base) sample@SampleMBP rails_docker % heroku run bundle exec rake db:migrate RAILS_ENV=production -a rails-docker-gucci
Running bundle exec rake db:migrate RAILS_ENV=production on ⬢ rails-docker-gucci... up, run.5914 (Free)
```

---
**リリースしたHerokuのアプリを開く**
```terminal
$ heroku open -a アプリ名
```

### 🎉🎉🎉エラーが出ていなければデプロイ成功！！🎉🎉🎉


---

:::note
**herokuでエラーになった時の対策**
:::

- 環境変数を設定
```terminal
$ heroku config:add RAILS_LOG_TO_STDOUT='true' -a アプリ名
```

- herokuのログを出力
    - FATALなどの情報を見てググり、エラーを解決していく
```terminal
$ heroku logs -t -a アプリ名
```

# 7. おまけ（機能追加する際の手順）
**開発用サーバを立ち上げる**
```terminal
$ docker-compose up -d
```

---

**Controller作成**
```terminal
$ docker-compose exec web bundle exec rails g controller homes
```

---

**ルーティングを設定**
```config/routes.rb
Rails.application.routes.draw do
  root 'homes#top'
end
```

---

**コントローラにアクションを定義**
```app/controller/homes_controller.rb
class HomesController < ApplicationController
  def top
  end
end
```

---

**Viewファイル作成**
- 綺麗なトップページにしたいので今回はVanta.jsを使用　※任意

https://www.vantajs.com/?effect=birds#(alignment:26,backgroundAlpha:1,backgroundColor:140882,birdSize:1.2000000000000002,cohesion:17,color1:9511997,color2:1819130,colorMode:varianceGradient,gyroControls:!f,minHeight:200,minWidth:200,mouseControls:!t,quantity:5,scale:1,scaleMobile:1,separation:31,speedLimit:7,touchControls:!t,wingSpan:33)

```erb:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>App</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>
```
```erb:app/views/homes/top.html.erb
<div id="bird">
  <div class="main-visual">
    <h1>Congratulations on your deployment!!</h1>
    <h2>created user : Ryo.S</h2>
  </div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r121/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.birds.min.js"></script>
<script>
  VANTA.BIRDS({
    el: "#bird",
    mouseControls: true,
    touchControls: true,
    gyroControls: false,
    minHeight: 970.00,
    minWidth: 200.00,
    scale: 1.00,
    scaleMobile: 1.00,
    backgroundColor: 0x22652,
    color1: 0x91243d,
    color2: 0x1bc1fa,
    birdSize: 1.20,
    wingSpan: 33.00,
    speedLimit: 7.00,
    separation: 31.00,
    alignment: 26.00,
    cohesion: 17.00
  })
</script>
```

```css:assets/stylesheets/homes.scss
body {
  margin: 0;
}
.main-visual {
  text-align: center;
  color: transparent;
	background: linear-gradient(45deg, #B67B03 0%, #DAAF08 45%, #FEE9A0 70%, #DAAF08 85%, #B67B03 90% 100%);
	-webkit-background-clip: text;
  width: auto;
  padding-top: 100px;
  padding-right: 30px;
  padding-left: 30px;
  font-size: 35px;
  font-style: italic;
  font-family: Times New Roman, "ＭＳ Ｐゴシック";
}
```

---
**ブラウザで動作確認（ローカル）**
- http://localhost:3000/

---
**Herokuに反映させる**
```terminal:例
$ docker-compose down
$ rm src/tmp/pids/server.pid
$ docker buildx build . --platform linux/amd64 -t gucci/rails-docker-gucci:latest
$ docker tag gucci/rails-docker-gucci:latest registry.heroku.com/rails-docker-gucci/web
$ docker push registry.heroku.com/rails-docker-gucci/web
$ heroku container:release web -a rails-docker-gucci
$ heroku open -a rails-docker-gucci
```


# その他よく使うコマンド
- アプリケーション起動コマンド
```terminal
$ heroku ps:scale web=1 -a アプリ名
```

---

- アプリケーション停止コマンド
```terminal
$ heroku ps:scale web=0 -a アプリ名
```

---
- 起動停止確認コマンド
    - `up`なら起動中
    - `No dynos on`が表示されていたら停止中
```terminal
$ heroku ps -a アプリ名
```

---
- お掃除コマンド
```terminal
$ docker system prune
```

## NextStep
https://qiita.com/rexid/items/0265e16f11cba57a07cc