ランタイムの原則
この記事の英語版に更新があります。ご覧の翻訳には含まれていない変更点があるかもしれません。
最終更新日 2022年01月27日(木)
Table of Contents
Heroku はランタイムを提供し、アプリケーションの実行とスケールを管理します。しかし、アプリケーションが適切に管理されるようにするため、いくつかのランタイム原則に従わなければなりません。
これらのランタイム原則は、Heroku にデプロイするときのいくつかのベストプラクティスのカテゴリの 1 つです。アプリケーション設計の原則をすべて見直して、Heroku でのアプリの適正な開発について十分に理解しましょう。
プロセスモデル
アプリケーションをモノリシックエンティティとして実行すべきではありません。代わりに、1 つ以上の軽量プロセスとして実行しましょう。開発中に、これはコマンドラインを介して起動される 1 つのプロセスになりえます。本番環境で、高度なアプリが、ゼロ以上の実行中プロセスにインスタンス化された数多くのプロセスタイプを使用する場合があります。
Heroku でのプロセスは、サービスデーモンを実行するためにunix プロセスモデルのストロングキューを使用します。このモデルを使用して、各種の作業をプロセスタイプに割り当てることにより、多様なワークロードに対処するアプリを設計できます。一般的なプロセスタイプとして、着信する HTTP リクエストを扱う web
と、バックグラウンドジョブを実行する worker
があります。これらの様々なプロセスタイプが指定されているプロファイルでは、アプリの実行モデルが定義されています。
Heroku dyno は、アプリケーションにより定義されたプロセスの実行を担う、仮想化されたコンテナです。1 つの dyno があるプロセスタイプの 1 つのインスタンスを実行 (それ自体が今度はいくつかのサブプロセスを生成して管理) します。表面的に、それぞれのアプリの dyno が 1 つのルートプロセスを管理する、1 対 1 の関係と考えるのが便利です。
ステートレス
プロセスがステートレスで、シェアナッシングです。存続しなければならないあらゆるデータをステートフルなバッキングサービス (一般的にはデータベース) に保管しなければなりません。
アプリは、メモリまたはディスクにキャッシュされるものすべてが将来のリクエストあるいはジョブで利用できると想定しません。数多くのプロセスが分散環境で実行されているので、将来のリクエストに対処する様々なプロセスがそれぞれ異なる物理的場所にあり、そこに当初のメモリやファイルスペースにアクセスできなくなる可能性は大いにあります。プロセスを 1 つしか実行していないときでも、再起動 (コードデプロイ、設定変更、あるいは実行環境がプロセスを別の物理的場所へリロケーションすることによりトリガされる) によって、通常はすべてのローカル (たとえばメモリやファイルシステム) ステートが消去されます。
このプロセスモデルは、スケールアウトする段階になると、非常に有効です。シェアナッシング、横方向に分割可能な性質は、さらなる同時並列性の追加が単純で信頼できる操作であることを意味します。
フォアグラウンド実行
プロセスが PID ファイルをデーモン化したり書き込みしてはなりません。代わりに、Dyno Manager や、Heroku Local のようなツールを開発環境で使用して、出力ストリームを管理し、クラッシュしたプロセスに対応し、ユーザーが始めた再起動やシャットダウンに対処します。
Web サーバー
Heroku の外部では、Web アプリが Web サーバーコンテナの内側で実行されることがあります。たとえば、PHP アプリが Apache HTTPD 内部でモジュールとして実行されたり、Java アプリが Tomcat 内部で実行されたりします。
Heroku では、アプリは完全に自己完結型です。Web サーバーによる実行環境へのランタイムインジェクションを使用して Web 対応サービスをつくり出すのではありません。各 Web プロセスがポートに結合して、そのポートで着信するリクエストをリスンするだけです。結合する相手のポートは、Heroku により PORT
環境変数として割り当てられます。
これは、一般には依存関係宣言を使用してアプリに Web サーバーライブラリ (Tornado for Python、Unicorn for Ruby、Jetty for Java およびその他の JVM ベース言語) を追加することで実装されます。これは、全体がユーザー空間、つまりアプリのコード内で行われます。Heroku との契約は、リクエストに対処するためにポートと結合するプロセスのために結びます。ここで Heroku のルーターは、HTTP リクエストを正しいポート上のプロセスに転送する働きをします。
廃棄可能性
アプリのプロセスは廃棄可能と考えるべきであり、即座に開始や停止できます。これにより速い弾性スケーリング、コードまたは設定変更の迅速なデプロイ、堅牢な本番環境デプロイが可能になります。
プロセスが起動時間の短縮を図り、リクエストを数秒内で受信する態勢ができなければなりません。逆に、プロセスが SIGTERM
シグナルを受け取ったときはグレースフルシャットダウンする必要があります。Web プロセスの場合、グレースフルシャットダウンはサービスポートでのリスンを止める (それにより新たなリクエストが拒否される) ことで行われ、現在のリクエストが終結可能になって、終了します。
このモデルで暗黙なのは HTTP リクエストが短いこと (数秒以内)、あるいは長いポーリングの場合、クライアントは接続が失われたときシームレスに再接続しようとすることです。
ビルド、リリース、実行
コードベースが 3 つの段階 (ビルド、リリース、実行) を経て実行中アプリケーションに変わります。これらの段階間を厳密に切り分けることにより、一貫して予測可能なリリースパイプラインが可能になります。
ビルド段階では、依存関係をフェッチしてベンダー化し、バイナリとアセットをコンパイルすることで、コードリポジトリをスラグと呼ばれる実行可能バンドルに変換します。リリース段階では、ビルドを使って、デプロイの現在の設定と結び付けます。結果となるリリースにはビルドと設定の両方が含まれ、すぐに実行する準備ができます。
実行段階 (「ランタイム」とも呼ぶ) では、アプリを実行環境で実行します。実行段階を可能な限りわずかな可動部に保つことで、アプリが実行できなくなる問題が最小限になります。
Heroku のしっかり構築されたビルドプロセスにより拡張性の機会 (カスタムビルドパック内)) が公開され、設定変更 (heroku config:set
) を速く伝播できるようになり、強力なプラットフォーム管理機能 (不測のリリースを即座にロールバックできるなど) が可能になります。3 つのデプロイ段階の間の厳密な分離は、必ずしもアプリのアーキテクチャ内でマニフェスト化する原則ではありませんが、アプリのコードベースを実行アプリに変換するプロセスを理解する際の重要概念です。