【Day 20】Heroku の環境整備【じゃんけんアドカレ】

[じゃんけんアドベントカレンダー](https://qiita.com/advent-calendar/2020/janken) の 20 日目です。

- 初回 ... [【Day 1】とりあえず 1 クラスに全部書く【じゃんけんアドカレ】](https://qiita.com/os1ma/items/630e2d7fb5d744bbdf8a)
- 前回 ... [【Day 19】Repository の課題を QueryService で解決【じゃんけんアドカレ】](https://qiita.com/os1ma/items/c0493ad8c26c8b401e58)

---


前回 QueryService を導入し、アプリケーション構成の整理が一段落しました。
今回は **Heroku への自動デプロイを修正したり、ロギング・モニタリング周りを無料で簡単にできる範囲で整えようと思います**。

## 自動デプロイの修正

まず、Heroku への自動デプロイの修正から始めます。

### Heroku へのデプロイのエラー

実は jOOQ を導入した時点から、Heroku へのデプロイがエラーになってしまっています。

原因は、jOOQ がコードを生成する際に MySQL が必要なためです。
(Heroku 上では JAR ファイルのビルドに必要な、最低限のタスクしか実行していなかったため、MySQL を起動していませんでした)

Heroku でのビルド時に MySQL を起動するという修正方法もありますが、**このエラーのそもそもの原因は、CI でビルドした成果物を使わず、Heroku 側でビルドした成果物をデプロイする流れになっていること**だと思います。

このように、**CI でビルドして動作確認した成果物ではない、改めてビルドした成果物をデプロイする場合、ビルド時のパラメータや環境の差異などによって、「本番では動かない」という問題が発生する可能性があります**。
本来的には、**CI でビルドした成果物をそのままデプロイする方が望ましい**です。

そこで、GitHub Actions 上でビルドしてテストした JAR ファイルをデプロイするように修正しようと思います。

### 自動デプロイセットアップ

では、**GitHub Actions 上で作成した成果物を、そのまま Heroku CLI を使ってデプロイする**という流れをセットアップしていきます。

まず、GitHub Actions 上で実行するデプロイコマンドをシェルスクリプトにまとめました。

```bash
#!/bin/bash

set -o errexit
set -o nounset
set -o pipefail
set -o xtrace

readonly SCRIPT_DIR="$(cd "$(dirname "$0")"; pwd)"
readonly PROJECT_HOME="${SCRIPT_DIR}/.."

cd "${PROJECT_HOME}"

heroku plugins:install java@3.1.1
heroku deploy:jar ./app/build/libs/*.jar --app janken-enterprise-edition
```

このくらいの内容であれば GitHub Actions の設定ファイルに直接書いても良いのですが、シェルスクリプトなどに切り出せば、何らかの理由でローカルからデプロイしたくなった際に便利です。

続いて、GitHub Actions の設定の最後に Heroku へのデプロイ処理を追加しました。

```yaml
name: main

on:
  push:
    branches:
    - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      :
    - name: Install Heroku CLI
      run: curl https://cli-assets.heroku.com/install.sh | sh
    - name: Deploy
      run: ./bin/deploy.sh
      env:
        HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
```

なお、GitHub Actions の設定ファイルは main.yaml と schedule.yaml の 2 つに分割し、main ブランチへの push 時はデプロイまで実行し、定期実行ではビルドのみが実行されるようにしています。

main.yaml と schedule.yaml の内容はほぼコピペになっていますが、[こちらの Issue](https://github.com/actions/runner/issues/646) の通り、現状ではコマンドの実行以外を共通処理として抜粋する方法がないようなので、今回はこのままにしておきます。

### その他の変更

このほかに、ClearDB の無料プランでは MySQL のバージョンが 5.5 しか使えず、使用中のバージョンの Flyway では利用できなかったため、JawsDB を使うようにするなどの変更を入れています。

参考

- [herokuでMySQL5.7以降を使うためにClearDBからJawsDBに変更する](https://qiita.com/reiya018/items/ba8ceaa732edac07e491)

## ロギング

自動デプロイのセットアップが完了したので、次はログまわりを整備しようと思います。

### デフォルトで提供されているログ機能

Heroku では特に何もセットアップせずとも、**Heroku CLI を使って以下のようなコマンドで直近のログを見ることができます**。

```bash
heroku logs --tail --app janken-enterprise-edition
```

**デフォルトでも以下のようなアクセスログが出ており、ある程度の情報を得ることができます**。

```bash
2020-12-20T09:46:56.510333+00:00 heroku[router]: at=info method=GET path="/api/health" host=xxx request_id=xxx fwd="xxx.xxx.xxx.xxx" dyno=web.1 connect=0ms service=43ms status=200 bytes=157 protocol=https
```

しかし、Heroku のドキュメントの「[ログ記録](https://devcenter.heroku.com/ja/articles/logging)」というページによると、

> Logplex は、保管用ではなく、ログメッセージを照合してルーティングするように設計されています。最新の 1,500 行の統合されたログを保持し、1 週間後に期限切れになります。

とのことで、**この方法では非常に短期間のログしか確認できませんし、エラーログが出たからアラートを上げる、といったこともできません**。

### アドオンの導入

そこで、[Papertrail](https://elements.heroku.com/addons/papertrail) というアドオンを導入します。
**Papertrail はコマンドや GUI で追加するだけでログの収集を開始してくれるので、導入が非常に簡単です**。

**エラーログが発生したら Slack にアラート通知がほしくなったりします**が、Papertrail は無料プランでも Slack に通知可能です。
具体的な設定手順は以下の記事などを参考にしてください。

- [Papertrail のログを Slack へ通知する設定方法](https://codenote.net/heroku/3869.html)

Papertrail 以外にも、Heroku のロギングに関するアドオン等はたくさんあります。
例えば以下の記事が参考になります。

- [Herokuでのログ収集と監視について調べてみた](https://blog.leko.jp/post/research-of-log-collection-and-monitoring-in-heroku/)

今回は Papertrail のみを導入して完了としますが、他にも例えばフロントエンドについては Sentry などでエラーを収集したり、マイクロサービスであれば分散トレーシングなどを導入する場合もあります。

## モニタリング

続いて、モニタリングサービスを導入しようと思います。

### メトリクス

まずは CPU 使用率やメモリ使用率などのメトリクスについてです。

Heroku のメニューにある「Metrics」の機能は有料プランでないと使えません。
アドオンを探したところ、Librato や Sumo Logic あたりが無料でもある程度使えそうでした。

Libroto はアドオンをインストールするだけである程度のダッシュボードをすぐに見ることができるものの、無料プランではデータの保持期間が 1 時間しかありません。
Sumo Logic は Libroto より多少導入の手間はありますが、データの保持期間は 7 日間あるようです。

今回は非常に簡易的に済ませてしまうため Libroto を導入しました。

### 外形監視

**実際にアプリケーションが動いているのか止まっているのかを判断するには、CPU 使用率やメモリ使用率などのメトリクスよりも外形監視が有効です**。

Heroku のアドオンでは Pingdom などが有名ですが、無料で使うことはできません。
無料でも使えるアドオンの中では New Relic が外形監視に対応していますが、無料プランでは Slack への通知まではできないようです。
**通知までできないと障害発生時に気付くことができない**ので、Heroku のアドオンとは別で通知まで無料で対応しているサービスを探し、[UptimeRobot](https://uptimerobot.com/) を導入しました。

UptimeRobot 含め、無料で使える外形監視サービスについては以下の記事が参考になります。

- [無料で使える外形監視サービス比較 2020年版](https://qiita.com/studio15/items/be8fe4f3eb649e21234a)

その他、今回検討した以外の監視としては、APM を導入したり、Spring の場合は Spring Actuator というモジュールを使うことなども考えられます。

## その他

以上で自動デプロイの修正、ロギング・モニタリングの整備といった、Heroku の環境の整備を一旦完了にしようと思います。
じゃんけんアプリケーションでは実施しませんが、本番運用するアプリケーションでは、例えば以下のような整備も必要になります。

### データバックアップ

本番運用するシステムであれば、データのバックアップはほぼ必須です。
バックアップは DB のミドルウェアが提供するツールや機能 (mysqldump など) などで実施することもできますし、DB のホスティングサービスが提供する機能 (Amazon RDS のバックアップ機能など) で実施することもできます。

### 検証環境の構築

個人で利用するシステムでもない限りは、本番環境の 1 環境だけというわけにはいきません。
本番とは別に、最低でももう 1 つ、本番と同様の構成の検証環境が必要になります。
(呼び方は「検証環境」・「開発環境」・「テスト環境」など色々あると思います)

### Infrastructure as Code

本番環境と他の環境で全く同じようにセットアップするためには、Infrastructure as Code が重要です。
Heroku も Terraform を使って設定をコードとして記述可能です。

参考

- [Using Terraform with Heroku](https://devcenter.heroku.com/articles/using-terraform-with-heroku)

### セキュリティ

今回はセキュリティについてはふれませんでした。
例えば Heroku にも、Sqreen という WAF のアドオンや、Snyk という脆弱性スキャンのアドオンなどがあります。

## 参考

この記事に書いた以外にも、Heroku の本番運用については例えば以下の記事が参考になります。

- [Herokuで本番サービスを運用する際にやっておきたいこと &amp; 構成の事例](https://engineer.crowdworks.jp/entry/2016/09/05/154304)

## 次回のテーマ

アドベントカレンダーも残り 5 記事となりました。
ここから 3 回ほどは、後回しにしていたアプリケーション設計の検討事項について考えていこうと思います。

- CLI アプリケーションにおける Controller の違和感 ([Day 6](https://qiita.com/os1ma/items/55565aa2e499aa2bc4f1#%E6%89%8B%E3%82%92%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%82%93%E3%81%A7%E3%81%98%E3%82%83%E3%82%93%E3%81%91%E3%82%93%E3%81%97%E3%81%A6%E7%B5%90%E6%9E%9C%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B%E3%81%A8%E3%81%84%E3%81%86%E5%87%A6%E7%90%86%E3%81%AE%E6%B5%81%E3%82%8C%E3%81%AE%E5%AE%9F%E7%8F%BE%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6))
- Transaction 型のキャスト ([Day 10](https://qiita.com/os1ma/items/6025f913a433a735bc43#%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E5%9E%8B%E3%81%AE%E3%82%AD%E3%83%A3%E3%82%B9%E3%83%88))
- じゃんけんするメソッドのさらなる整理 ([Day 12](https://qiita.com/os1ma/items/a65557e6fb38d2fcfec7#%E3%81%98%E3%82%83%E3%82%93%E3%81%91%E3%82%93%E3%81%99%E3%82%8B%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%8C%E3%81%82%E3%81%BE%E3%82%8A%E6%95%B4%E7%90%86%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84))

それでは、今回の記事はここまでにします。最後まで読んでくださりありがとうございました。
[じゃんけんアドベントカレンダー](https://qiita.com/advent-calendar/2020/janken) に興味を持ってくださった方は、是非購読お願いします。

## 現時点のコード

コードは GitHub の [この時点のコミット](https://github.com/os1ma/JankenEnterpriseEdition/tree/deb16a1dfa377e40686d64c045731325783b913e) を参照ください。