RailsアプリをHerokuにDockerとCircleCIを使ってデプロイするためのTips

HerokuにRailsアプリをデプロイしたときの備忘録です。

## Herokuの基礎知識
あまり正確ではないかもしれませんが、理解のために要所々々で他(AWS等)のアナロジーを使用します。

### [dyno](https://devcenter.heroku.com/ja/articles/dynos)
Heroku内のアプリケーションはdyno(ダイノ)というlinuxコンテナの集合で実行されるそうで、dynoにはWeb, Worker, One-offという3つのプロセスタイプが存在します。HTTPリクエストを受け付けるのはWebで、[worker](https://devcenter.heroku.com/ja/articles/background-jobs-queueing)は主にバックグラウンドジョブ、キューイングシステム、タイムドジョブ等の非同期処理に使用されます。つまりRailsアプリケーションはWebを担当します。

### [sulg](https://devcenter.heroku.com/ja/articles/slug-compiler)
デプロイされたコードをパッケージしたもの。dynoで実行される。

### [buildpack](https://devcenter.heroku.com/ja/articles/buildpacks)
> buildpack はデプロイされたコードを slug に変換する役目を担い、slug は dyno 上で実行することができます。 buildpack は一連のスクリプトで構成され、プログラミング言語に応じて、スクリプトは依存関係の取得や、生成されたアセットまたはコンパイル済みコードの出力などを行います。 この出力は ​slug コンパイラ​によって slug にアセンブルされます。

Dockerfileのようなものですね。つまりコンテナのビルドに利用されるものなので__Dockerfileと併用できません。__便利なだけに惜しい。

### [Procfile](https://devcenter.heroku.com/ja/articles/procfile)
起動時にアプリケーションが実行するコマンドの集合。ルートディレクトリに設置する。Procfileを使用しない場合はDockerfileのCMDなどが実行される。

### [add-ons](https://devcenter.heroku.com/ja/articles/add-ons)
> Heroku アドオンは、データストレージ、監視、分析、データ処理などの、アプリケーションをサポートするコンポーネントです。

AWSで例えるなら: dynoがECSでHerokuアドオンはRDSとかCloudWatchとかECSに連携するサービス

### [heroku.yml](https://devcenter.heroku.com/ja/articles/build-docker-images-heroku-yml)
Herokuアプリを定義するためのもの。アドオン、環境変数、起動時に実行するコマンドなどをまとめて定義できます。

AWSで例えるなら: ECSのタスク定義

## Heroku CLI
インストールは以下を参照。
https://devcenter.heroku.com/ja/articles/heroku-cli

CLIコマンドは以下を参照。
https://devcenter.heroku.com/ja/articles/heroku-cli-commands

## Dockerデプロイ
Dockerデプロイに関しては以下を参照。
https://devcenter.heroku.com/ja/articles/container-registry-and-runtime

## Rails x nginx
### 1. buildpackを使用する場合
公式の[buildpack](https://elements.heroku.com/buildpacks/heroku/heroku-buildpack-nginx)を使用します。buildpackを追加するコマンドは以下。

    heroku buildpacks:add heroku-community/nginx -a <アプリ名>

nginxとソケット接続するには以下を行う必要があります。
> 1. Your webserver listens to the socket at /tmp/nginx.socket.
2. You touch /tmp/app-initialized when you are ready for traffic.
3. You can start your web server with a shell command.

unicornはドキュメントのセットアップの欄に記載されているので、ここではpumaのセットアップのみ記載します。

```config/puma.rb
# 1 /tmp/nginx.socketでlisten
bind "unix:///tmp/nginx.socket"
# 2 masterプロセスがworkerプロセスを生成するときに/tmp/app-initializedを生成する
require 'fileutils'
on_worker_fork do
    FileUtils.touch('/tmp/app-initialized')
end
```

3\. Procfileの内容を以下に設定

````
web: bin/start-nginx bundle exec puma -C config/puma.rb
````

### 2. Dockerfileを使用する場合
私が調べた範囲では[Phusion Passenger](https://github.com/phusion/passenger)を利用するしかなさそうです。

参考
[passenger-dockerでRailsアプリをコンテナ化する（3. 本番モードでコンテナ化する）](https://qiita.com/NaokiIshimura/items/bfaeb07fc14a1e05102f)

### 3. リバースプロキシ(nginx)を使用しない場合
Webへのポート番号はHeroku側が勝手に割り振って値を$PORTに代入するので

```config/puma.rb
port ENV.fetch("PORT") { 3000 }
````

````
bundle exec puma -C config/puma.rb
````
とすれば外部から接続できます。

## Rails x MySQL
公式の[add-on](https://elements.heroku.com/addons/cleardb)を使用します。add-onを追加するコマンドは以下。

````
heroku addons:create cleardb:<plan> -a <アプリ名>
````
無料のプランはigniteです。

上記のコマンドを実行するとデータベースのURLが環境変数に追加されます。環境変数を表示するコマンドは以下。

````
heroku config -a <アプリ名>

CLEARDB_DATABASE_URL: mysql://<ユーザー名>:<パスワード>@<ホスト名>/<データベース名>?reconnect=true
````
$CLEARDB_DATABASE_URLの情報からconfig/database.ymlに設定した環境変数にそれぞれ値を代入します。

## CircleCIでデプロイ
公式にOrbsがあります。
https://circleci.com/developer/orbs/orb/circleci/heroku

Long-lived APIキーを生成するコマンドは以下。

````
heroku authorizations:create
````

生成したトークンをHEROKU_API_KEYとしてCircleCIの環境変数に設定することで

````
heroku container:login
````
でherokuのコンテナを操作できるようになります。

因みにDockerコマンドで直接pushする場合、イメージのタグは:latestでないとreleaseしたときに404エラーになります。


## Route53で取得したドメインを使用する
以下の記事が参考になりました。

- [Herokuでカスタムドメイン（ルートドメイン）を使うためにCloudFrontを設定する](https://sinsoku.hatenablog.com/entry/2021/02/09/014241)
- [HerokuアプリにAWS Route53 で取った独自ドメインを割り当て、 Certificate Manager + Cloudfront を使ってhttps化する](https://qiita.com/hiraokashi/items/382b43b4bcf8e84b61ae)

HerokuアプリはTerraformでも定義できます。
https://registry.terraform.io/providers/heroku/heroku/latest/docs