Heroku の仕組み
この記事の英語版に更新があります。ご覧の翻訳には含まれていない変更点があるかもしれません。
最終更新日 2023年03月08日(水)
Table of Contents
ここでは、Heroku の仕組みに関する高度で技術的な内容を説明します。 Heroku プラットフォームでアプリケーションを作成、設定、デプロイ、実行するときに必要となる多くの概念をまとめています。
「スターターガイド」のいずれかのチュートリアルを実施すると、 このドキュメントの概念をより具体的に理解できます。
このドキュメントは上から順に読み進めてください。このドキュメントでは、流れに沿って説明するために、プラットフォームに関する概念を少しずつ紹介し、段階を追って改訂していきます。
最後のセクションでは、すべての定義を Heroku のデプロイ時とランタイムの視点に分けてまとめています。
アプリケーションを定義する
Heroku では、Rugy、Node.js、Java、Python、Clojure、Scala、Go、PHP で作成されたアプリケーションのデプロイ、実行、管理を行うことができます。
アプリケーションは、上記の言語のいずれかで作成されたソースコード、場合によってはフレームワーク、そしてアプリケーションをビルドして実行するために必要な追加の依存関係をビルドシステムに指示するいくつかの依存関係の説明の集まりです。
用語 (暫定): アプリケーションは、ソースコードと依存関係の説明で構成されています。
依存関係のメカニズムは言語によって異なります。Ruby では Gemfile
、Python では requirements.txt
、Node.js では package.json
、Java では pom.xml
などです。
アプリケーションのソースコードと依存関係ファイルで、Heroku プラットフォームでアプリケーションをビルドして実行可能なものを作成するために必要な情報を提供する必要があります。
実行内容を明確にする
アプリケーションを Heroku で実行するために、多くの変更を行う必要はありません。必要なのは、アプリケーションのどの部分が実行可能かをプラットフォームに伝えることだけです。
一部の定着しているフレームワークを使用している場合は、Heroku によって実行可能部分が認識されます。 たとえば、Ruby on Rails では通常 rails server
、Django では python <app>/manage.py runserver
、Node.js では package.json
内の main
です。
用語: Procfile で、プロセスタイプ - 実行する名前付きコマンドのリストを定義します。
ほかのアプリケーションについては、実行可能な内容を明示的に宣言する必要がある場合があります。 実行可能な内容は、Procfile という、ソースコードに付属するテキストファイルで宣言します。 各行で、プロセスタイプ - ビルドされたアプリケーションに対して実行できる名前付きコマンドを宣言します。 たとえば、Procfile の内容は次のようになります。
web: java -jar lib/foobar.jar $PORT
queue: java -jar lib/queue-processor.jar
このファイルでは、web
プロセスタイプを宣言し、動作するために実行する必要があるコマンド (この場合は java -jar lib/foobar.jar $PORT
) を宣言しています。 また、queue
プロセスタイプと、それに対応するコマンドも宣言されています。
これで、前回のアプリケーションの定義を改訂して、この 1 つの Procfile を追加できます。
用語: アプリケーションは、ソースコード、依存関係の説明、Procfile で構成されています。
Heroku は多言語プラットフォームです。すべての言語にわたって同様の方法で、つまり依存関係と Procfile を利用して、アプリケーションのビルド、実行、スケールを行うことができます。 Procfile によりアプリケーションのアーキテクチャの側面が公開され (上記の例では、アプリケーションへの 2 つのエントリポイントがある)、このアーキテクチャにより、たとえば各部分を個別にスケールすることなどができます。 Heroku で実行するアプリケーションに役立つアーキテクチャの原則に関する優れたガイドについては、「Architecting Applications for Heroku」(Heroku のアーキテクチャ設計) を参照してください。
アプリケーションをデプロイする
Git は、多くの開発者がソースコードの管理やバージョン管理に使用している、強力な分散型バージョン管理システムです。 Heroku プラットフォームでは、アプリケーションをデプロイする主な手段として Git を使用しています (API 経由など、ほかにもソースコードを Heroku に転送する方法があります)。
Heroku でアプリケーションを作成すると、新しい Git リモート (通常は heroku
という名前) がアプリケーションのローカル Git レポジトリに関連付けられます。
結果として、コードのデプロイには git push
を使用しますが、デプロイ先は heroku
リモートになります。
$ git push heroku main
用語: アプリケーションのデプロイとは、Git か GitHub を使用して、または API 経由で、Heroku にアプリケーションを送信することです。
アプリケーションをデプロイする方法は、ほかにもたくさんあります。 たとえば、新しい各プルリクエストが独自の新しいアプリケーションに関連付けられるように、GitHub インテグレーションを有効にすることができます。これにより、あらゆる種類の継続的なインテグレーションシナリオが実現されます。また、Heroku API を使用して、アプリのビルドとリリースを行うこともできます。
デプロイとは、つまり、アプリケーションをローカルシステムから Heroku に移動することです。Heroku にはアプリケーションをデプロイできる方法が複数用意されています。
アプリケーションをビルドする
Heroku プラットフォームにアプリケーションのソースが渡されると、ソースアプリケーションのビルドが開始されます。 ビルドのメカニズムは一般的に言語固有ですが、通常は指定された依存関係の取得と必要なアセットの作成といった、同じパターンで実施されます (スタイルシートの処理のようにシンプルな場合も、コードのコンパイルのように複雑な場合も)。
上級: slug のコンパイルプロセスの背後には buildpack が存在します。 buildpack では、アプリケーション、アプリケーションの依存関係、および言語ランタイムから slug が生成されます。 buildpack はオープンソースで、これを使うことにより、Heroku をほかの言語やフレームワークに拡張することができます。
たとえば、ビルドシステムに Rails アプリケーションが渡されると、Gemfile で指定されているすべての依存関係が取得され、アセットパイプラインに基づいてファイルが生成されます。 Java アプリケーションの場合は、Maven を使用してバイナリライブラリの依存関係が取得され、バイナリライブラリと一緒にソースコードがコンパイルされて、実行する JAR ファイルが生成されます。
アプリケーションのソースコードは、取得された依存関係、ビルドフェーズの出力 (生成されたアセットやコンパイルされたコードなど)、および言語やフレームワークと共に、slug にアセンブルされます。
用語: slug は、ソース、取得された依存関係、言語ランタイム、およびビルドシステムのコンパイル / 生成後の出力 (実行できる状態) のバンドルです。
slug は、アプリケーションの実行中に発生する処理の基本的な側面です。slug には、コンパイルおよびアセンブルされて実行できる状態になったアプリケーションが、実行する処理の指示 (Procfile) と共に格納されます。
dyno でアプリケーションを実行する
Heroku では、Procfile で指定したコマンドを実行することで、用意した slug (実際には、slug と未定義のいくつかの項目 (環境設定やアドオン) を拡張するリリース) があらかじめロードされた dyno 上でアプリケーションが実行されます。
実行中の dyno は、ファイルシステム内にアプリケーションの slug を格納する、軽量で安全性が高い、仮想化された Unix コンテナのようなものです。
用語: dyno は、アプリケーションの実行に必要な環境を提供する、分離、仮想化された Unix コンテナです。
一般的に、初めてアプリケーションをデプロイする場合は、1 つの Web dyno が自動的に実行されます。 つまり、dyno が起動され、slug がロードされて、Procfile 内で Web プロセスタイプに関連付けているコマンドが実行されます。
いつでも、実行する dyno の数を制御できます。 前に説明した Procfile を例にとると、次のようにして、5 つの dyno (web プロセスタイプが 3 つ、queue プロセスタイプが 2 つ) を起動できます。
$ heroku ps:scale web=3 queue=2
アプリケーションの新しいバージョンをデプロイすると、現在実行中の dyno がすべて強制終了され、既存の Dyno formation を維持したまま、起動する (新しいリリースを使用した) 新しい dyno に置き換えられます。
用語: アプリケーションの Dyno formation とは現在実行中の dyno の合計数のことで、スケール済みのさまざまなプロセスタイプに分配されます。
実行中の状況を把握するには、どの dyno でどのプロセスタイプが実行されているかを確認するだけで済みます。
$ heroku ps
== web: 'java lib/foobar.jar $PORT'
web.1: up 2013/02/07 18:59:17 (~ 13m ago)
web.2: up 2013/02/07 18:52:08 (~ 20m ago)
web.3: up 2013/02/07 18:31:14 (~ 41m ago)
== queue: `java lib/queue-processor.jar`
queue.1: up 2013/02/07 18:40:48 (~ 32m ago)
queue.2: up 2013/02/07 18:40:48 (~ 32m ago)
dyno はアプリケーションをスケールする重要な手段でもあります。 この例では、アプリケーションが正しく設計されているため、web と queue の Worker dyno を個別にスケールできます。
環境設定
アプリケーションの設定は、すべて、環境 (ステージング環境、本番環境、開発環境など) によって変わる可能性があります。設定には、データベース、資格情報、アプリケーションに特定の情報を提供する環境変数などのバッキングサービスなどがあります。
Heroku では、カスタマイズ可能な設定を使用してアプリケーションを実行できます。この設定は、アプリケーションコードの外に置かれ、コードとは関係なく変更できます。
アプリケーションの設定は、環境設定に保存されます。 たとえば、アプリケーションの暗号化鍵を設定する方法は、次のとおりです。
$ heroku config:set ENCRYPTION_KEY=my_secret_launch_codes
Adding config vars and restarting demoapp... done, v14
ENCRYPTION_KEY: my_secret_launch_codes
用語: 環境設定 には、ソースコードとは関係なく変更できる、カスタマイズ可能な設定データが格納されます。この設定は、環境変数を通じて、実行中のアプリケーションに公開されます。
ランタイムではすべての環境設定が環境変数として公開されるため、プログラムで簡単に抽出できます。 上記の環境設定を使用してデプロイされた Ruby アプリケーションは、ENV["ENCRYPTION_KEY"]
を呼び出すことで暗号化鍵にアクセスできます。
アプリケーション内のすべての dyno が、ランタイムで同じ一連の環境設定にアクセスできる必要があります。
リリース
この記事の前半で、dyno でアプリケーションを実行するときには、Heroku プラットフォームで dyno に最新の slug がロードされると説明しました。 この説明は改訂する必要があります。実際には、slug と、アプリケーションに割り当てた環境設定がロードされます。 slug と設定の組み合わせを「リリース」といいます。
用語 (暫定): リリースは、slug と環境設定を記録する追加専用の台帳です。
すべてのリリースが追加専用の台帳に自動的に保持されるため、アプリケーションや複数のリリースの管理が簡単になります。リリースのデプロイに関する追跡記録を確認するには、heroku releases
コマンドを使用します。
$ heroku releases
== demoapp Releases
v103 Deploy 582fc95 jon@heroku.com 2013/01/31 12:15:35
v102 Deploy 990d916 jon@heroku.com 2013/01/31 12:01:12
デプロイメッセージの横にある番号 (たとえば、582fc95
) は、Heroku にデプロイしたリポジトリのコミットハッシュに一致します。
アプリケーションの新しいバージョンをデプロイするたびに、新しい slug が作成され、リリースが生成されます。
Heroku にはアプリケーションの以前のリリースが保管されているので、簡単にロールバックして、以前のリリースをデプロイすることができます。
$ heroku releases:rollback v102
Rolling back demoapp... done, v102
$ heroku releases
== demoapp Releases
v104 Rollback to v102 jon@heroku.com 2013/01/31 14:11:33 (~15s ago)
v103 Deploy 582fc95 jon@heroku.com 2013/01/31 12:15:35
v102 Deploy 990d916 jon@heroku.com 2013/01/31 12:01:12
ソースの変更か設定の変更かに関係なく、アプリケーションに対して重要な変更を行うと、新しいリリースが作成されます。
リリースは、つまり、Heroku でアプリケーションのソースに関係なくアプリケーションの設定 (環境設定) を変更できる仕組みの背後にあるメカニズムで、リリースによってソースと設定がまとめてバインドされます。 アプリケーションに関連付けられている一連の環境設定を変更するたびに、新しいリリースが生成されます。
Dyno Manager
Heroku プラットフォームの一部である Dyno Manager により、dyno の継続的な実行が維持されます。 たとえば、dyno は最低でも 1 日に 1 回、または実行中のアプリケーションの障害 (メモリー不足の例外など) や、dyno を物理的に新しい場所に移動する必要があるような基盤のハードウェアの問題が Dyno Manager で検知されるたびに、再起動されます。
用語: Heroku プラットフォームの Dyno Manager は、Heroku で実行中のすべてのアプリケーションの dyno を管理します。
dyno の再起動は、定期的に透過的かつ自動的に行われ、ログに記録されます。
用語: eco
dyno タイプを使用するアプリケーションは、スリープするか、ゼロにスケールされます。スリープ中のアプリケーションが HTTP トラフィックを受信するとスリープは解除されますが、数秒の遅延が発生します。
ほかのいずれかの dyno タイプを使用すると、スリープしないようにすることができます。
Heroku でアプリケーションの管理と実行が行われるため、オペレーティングシステムやほかの内部システムの設定を管理する必要はありません。 One-off dyno は、入力 / 出力をローカルのターミナルに接続して実行できます。 One-off dyno は、データベースの設定など、共有リソースの状態を変更する管理タスクを (多くの場合、スケジューラーを通じて定期的に) 実行するために使用することもできます。
用語: One-off dyno は、入力 / 出力をローカルのターミナルに接続して実行できる一時的な dyno です。 これらには最新のリリースがロードされます。
One-off dyno を作成して接続する最も簡単な方法は、次の通りです。
$ heroku run bash
Running `bash` attached to terminal... up, run.8963
~ $ ls
この方法により、新しい dyno が迅速に起動され、リリースがロードされた後、bash
コマンドが実行されて、Unix シェルを使用できるようになります (前にも説明しましたが、dyno は、事実上、分離、仮想化された Unix コンテナです)。 セッションを終了するか、アイドル状態が一定の時間続くと、dyno は削除されます。
ある dyno で行ったファイルシステムへの変更は、ほかの dyno には反映されません。また、この変更は別のデプロイや dyno の再起動では無効になります。 より適切でスケーラブルな方法は、データベースやキューなどの共有リソースを使用することです。
用語: 各 dyno には、最新リリースの新しいコピーが含まれる、独自の一時的なファイルシステムがあります。このファイルシステムは一時的なスクラッチパッドとして使用できますが、ファイルシステムへの変更はほかの dyno に反映されません。
dyno のファイルシステムの一時的という特性は、上記のコマンドで確認できます。 heroku run bash
(dyno 上の Unix シェル) を実行して One-off dyno を作成し、その dyno でファイルを作成してから、セッションを終了すると、その変更は消滅します。 同じアプリケーション内の dyno であっても、すべての dyno は分離されており、セッションが終了すると、dyno は強制終了されます。新しい dyno は、ほかの dyno の状態を継承するのではなく、常に slug から作成されます。
アドオン
アプリケーションは、通常、アドオンを利用して、データベース、キューイングとキャッシングのシステム、ストレージ、メールサービスなどのバッキングサービスを提供します。 アドオンは、Heroku やサードパーティからサービスとして提供され、アドオンの大規模なマーケットプレイスがあるため、そこから選択することができます。
Heroku では、アドオンは接続されたリソースとして扱われます。アドオンのプロビジョニングとは、アドオンマーケットプレイスからアドオンを選択して、自分のアプリケーションに接続することです。
たとえば、Heroku Data for Redis のバッキングストアアドオンをアプリケーションに追加する方法は、次のとおりです。
$ heroku addons:create heroku-redis:mini
dyno ではファイルの状態が共有されないため、一種のストレージを提供するアドオンは、通常、アプリケーション内の dyno 間の通信手段として使用されます。たとえば、Redis または Postgres をキューのバッキングメカニズムとして使用することができ、web プロセスタイプの dyno ではそのキューにジョブリクエストをプッシュでき、queue プロセスタイプの dyno ではそのキューからジョブリクエストをプルできます。
アドオンサービスプロバイダーはサービスを担当し、アプリケーションへのインターフェースは、多くの場合、環境設定を通じて提供されます。この例では、アドオンをプロビジョニングすると、REDIS_URL
が自動的にアプリケーションに追加されます。 たとえば、次のように、URL を通じてサービスに接続するコードを書くことができます。
uri = URI.parse(ENV["REDIS_URL"])
REDIS = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)
用語: Add-on は、簡単にアプリケーションに接続できて機能を拡張する、サードパーティの専門付加価値クラウドサービスです。
アドオンは環境設定のようにアプリケーションに関連付けられるため、前回のリリースの定義は改訂する必要があります。 アプリケーションのリリースは、slug と環境設定だけではありません。slug、環境設定、およびプロビジョニングされた一連のアドオンです。
用語: リリース は、slug、環境設定、およびアドオンを記録する追加専用の台帳です。 Heroku では、作成されるリリースの追加専用の台帳が管理されます。
環境設定のように、アドオンの追加、削除、または変更を行うたびに、新しいリリースが作成されます。
ログと監視
Heroku では、ログはタイムスタンプ付きイベントのストリームとして扱われ、すべての dyno で実行中のすべてのプロセスから生成されたログと、Heroku プラットフォームのコンポーネントから生成されたログのストリームが Logplex (高パフォーマンスのリアルタイムログ配信システム) に順にまとめられます。
すべてのプラットフォームコンポーネントと dyno のログを簡単に確認できます。
$ heroku logs
2013-02-11T15:19:10+00:00 heroku[router]: at=info method=GET path=/articles/custom-domains host=mydemoapp.heroku.com fwd=74.58.173.188 dyno=web.1 queue=0 wait=0ms connect=0ms service=1452ms status=200 bytes=5783
2013-02-11T15:19:10+00:00 app[web.2]: Started GET "/" for 1.169.38.175 at 2013-02-11 15:19:10 +0000
2013-02-11T15:19:10+00:00 app[web.1]: Started GET "/" for 2.161.132.15 at 2013-02-11 15:20:10 +0000
上記の例では、タイムスタンプ付きの 3 つのログエントリが表示されています。1 つ目は Heroku のルーターから、残りの 2 つは web プロセスタイプを実行中の 2 つの dyno からのログエントリです。
用語: Logplex には、アプリのすべての実行中の dyno からのログエントリと、ルーターなどのほかのコンポーネントからのログエントリが自動的に順にまとめられるので、アクティビティを 1 か所で確認することができます。
1 つの dyno からログに入り、チャネルを開いたままにして、以降のイベントをリッスンすることもできます。
$ heroku logs --ps web.1 --tail
2013-02-11T15:19:10+00:00 app[web.1]: Started GET "/" for 1.169.38.175 at 2013-02-11 15:19:10 +0000
単にパフォーマンス上の理由で、Logplex では、限られたバッファのログエントリのみ保管されています。 ログエントリ、および実行時のメール通知などのアクションイベントを保持するには、ログドレイン (Logplex からの出力を受信するための API) と連動する Logging Add-on を使用します。
HTTP ルーティング
Dyno formation に応じて、dyno の一部は web
プロセスタイプに関連付けられたコマンドを実行し、また一部は、ほかのプロセスタイプに関連付けられたほかのコマンドを実行します。
web
という名前のプロセスタイプを実行する dyno は、ある意味でほかのすべての dyno とは異なり、HTTP トラフィックを受信します。 Heroku の HTTP ルーター は、受信リクエストを、実行中のすべての Web dyno 上のアプリケーションに分散します。
そのため、Web トラフィックを処理するためにアプリの処理能力をスケールするには、Web dyno の数をスケールします。
$ heroku ps:scale web+5
Web dyno 全体での HTTP リクエストのロードバランシングには、ランダム選択アルゴリズムが使用され、このルーティングで HTTP と HTTPS の両方のトラフィックが処理されます。 複数の同時接続と、タイムアウト処理もサポートされています。
まとめ
ここまでで説明した概念は、アプリケーションの開発とデプロイに関するものと、デプロイ後の Heroku プラットフォームとアプリケーションのランタイム処理に関するものの 2 種類に大別されます。
次の 2 つのセクションでは、プラットフォームのメインコンポーネントを 2 種類に分けて振り返ります。
デプロイ
- アプリケーションは、ソースコード、依存関係の説明、Procfile で構成されています。
- Procfile で、プロセスタイプ - 実行する名前付きコマンドのリストを定義します。
- アプリケーションのデプロイとは、Git か GitHub を使用して、または API 経由で、Heroku にアプリケーションを送信することです。
- slug のコンパイルプロセスの背後には buildpack が存在します。buildpack では、アプリケーション、アプリケーションの依存関係、および言語ランタイムから slug が生成されます。
- slug は、ソース、取得された依存関係、言語ランタイム、およびビルドシステムのコンパイル / 生成後の出力 (実行できる状態) のバンドルです。
- 環境設定 には、ソースコードとは関係なく変更できる、カスタマイズ可能な設定データが格納されます。この設定は、環境変数を通じて、実行中のアプリケーションに公開されます。
- Add-on は、簡単にアプリケーションに接続できて機能を拡張する、サードパーティの専門付加価値クラウドサービスです。
- リリースは、slug (アプリケーション)、環境設定、およびアドオンの組み合わせです。 Heroku では、作成されるリリースの追加専用の台帳が管理されます。
ランタイム
- dyno は、アプリケーションの実行に必要な環境を提供する、分離、仮想化された Unix コンテナです。
- アプリケーションの Dyno formation とは現在実行中の dyno の合計数のことで、スケール済みのさまざまなプロセスタイプに分配されます。
- Dyno Manager は、Heroku で実行中のすべてのアプリケーションの dyno を管理します。
eco
dyno タイプを使用するアプリケーションは、アイドル状態が 30 分続くとスリープします。別の dyno タイプにスケールすると、スリープしないようにできます。- One-off dyno は、入力 / 出力をローカルのターミナルに接続して実行する一時的な dyno です。この dyno には最新のリリースがロードされます。
- 各 dyno には、最新リリースの新しいコピーが含まれる、独自の一時的なファイルシステムがあります。このファイルシステムは一時的なスクラッチパッドとして使用できますが、ファイルシステムへの変更はほかの dyno に反映されません。
- Logplex には、アプリのすべての実行中の dyno からのログエントリと、ルーターなどのほかのコンポーネントからのログエントリが自動的に順にまとめられるので、アクティビティを 1 か所で確認することができます。
- アプリケーションのスケールとは、各プロセスタイプの dyno の数を変更することです。
次のステップ
- 「スターターガイド」のいずれかのチュートリアルを実施して、このドキュメントの概念をより具体的に理解します。
- 「Architecting Applications for Heroku」(Heroku のアーキテクチャ設計) を参照して、Heroku のアーキテクチャを活用したスケーラブルなアプリを構築する方法を理解します。