Herokuのクセとうまく付き合う
こんにちは。
ここ数年、Heroku + Javaでの開発プロジェクトによく参画しています。
いくつかのプロジェクトを通し、Herokuならではクセに直面してきました。
弊社の @yonyonsaeki の記事と合わせて、これからHerokuプロジェクトをはじめられる方に向けて、私が知らずにハマったクセ(もしくは地雷)についてご紹介していきます
Web Dynoは60秒以内に起動しないといけない
Web Dynoは起動後の60秒以内で$PORT
をlistenしなくてはなりません。
60秒を過ぎてもlisten状態にならない場合、R10 - Boot timeoutとなり、Dynoはクラッシュします。
Web Dynoは30秒以内にレスポンスを返さないとダメ
Web DynoはHTTPリクエストを受信してから30秒以内にレスポンスを返さなければなりません。
30秒を経過すると、Heroku routerによりHTTPコネクションが切断されてしまいます。
レスポンスに時間のかかるAPIなどは注意しましょう。
Dynoは1日ごとに再起動する
Web Dynoは、およそ1日に一回必ず再起動されます。
期限の長いセッション情報などをメモリに持っておくと、再起動でリセットされてしまうためやめておきましょう。
再起動時にダウンタイムはありません。これは、実行中のDynoの他に新しいDynoが新規で起動し、新しいDynoがlisten状態になるまでHeroku routerは古い実行中のDynoにリクエストをルーティングするためです。
2019/12/16 追記
ダウンタイムをなくすためにはPreboot
機能の有効化が必要でした。
有効化するには「Heroku の Preboot 機能を深掘りした - Feedforce Developer Blog」を参照してください。
@masutaka さん、ありがとうございます!
ファイルを保存してはいけない
DynoはDockerのコンテナの様なものです。
Dyno内のファイルシステムに作成したファイルは、再起動すると消えてしまいます。
また、Dyno間でファイルを共有できません。複数のWeb Dynoなどを立ち上げていると、web1にはファイルがあるけどweb2にはファイルがない、なんてことが発生します。
ストレージが必要な時はBucketeerの様なHeroku AddonsのData Storesを検討してみましょう。
ログは標準出力に出す
ログはファイルに出力せずに、全て標準出力へ出しましょう。
Heroku CLIでheroku logs --tail --app <App名>
と実行すると、リアルタイムのログを見ることができます。
HerokuのダッシュボードからもMore -> View logs
で参照できます。
さらに、Papertrailの様なLoggingのHeroku Addonsを利用してみましょう。検索機能がとても便利です。
スケジューラで起動したジョブは、次のスケジュール時間までに終了しないと強制終了される
Heroku Schedulerを利用すると、cron的にコマンドを実行できます。コマンドはOne-off Dynoで実行されます。
スケジューラで実行したOne-off Dynoは、次のスケジュール時刻までに実行しないと強制終了されます。コマンドの最後でDBにコミットする様なケースでは、ロングランして強制終了->ロールバック->次のスケジュールでコマンド実行->ロングラン->強制終了...のループが繰り返されます(やらかしてとても困った)。
他の環境変数を参照する環境変数は設定できない
heroku config:set VAR=value
の様に、Appに対して環境変数を定義できます。
この時にFOO=$BAR
の様な他の環境変数を参照する定義はできません。
他の環境変数の参照が必要な場合は.profile
を追加してみてください。
以下はREDIS_URL
をコピーしたSPRING_REDIS_URL
を定義する.profile
の例です。
export SPRING_REDIS_URL="$REDIS_URL"
Slugサイズは500MBまで
AppはSlugというものにパッケージされます。Slugには構築したアプリの他に依存関係のモジュールなどが含まれます。
DynoはSlugを展開し、起動されます。
このSlugには500MBのサイズ上限があります。
サイズの大きなファイルを利用する場合などは注意してください。
また、Gitのコミット数が多くなり未参照のオブジェクトが増えたり、依存関係のキャッシュで圧迫される様な場合、Heroku CLIのrepoプラグインを利用してゴミ掃除をしてみましょう。
そもそもSlugに含めたくないファイル・フォルダがある場合(Dynoでアプリを実行する際に不要なファイル)は.slugignore
に指定することで、Slugから除外することができるので活用しましょう。
ただし、除外されるタイミングはビルドの前です。ビルド後に生成されるファイルをこの方法では削除できないため注意しましょう。
アクセス元のIPアドレスは X-Forwarded-For ヘッダからとる
Web Dynoから接続元のIPアドレスを取得する時はX-Forwarded-For
ヘッダから取得しましょう。
Web DynoはHeroku Routerと呼ばれるリバースプロキシの後ろで実行されています。
そのため、REMOTE_ADDR
はHeroku RouterのIPアドレスとなります。
DynoのIPアドレスは固定できない
HerokuはAWS上に構築されており、DynoのIPアドレスはAWSのアドレス範囲のどれかになります。
外部システムとの連携において、IPアドレスの固定化が要求として上がるケースがあります。
HerokuからのアウトバウンドのIPアドレスを固定したい場合は、ProximoなどのAddon利用を検討してください。
※Heroku Private Spacesを利用している場合は例外です。固定のIPアドレスが割り当てられます。