リリースフェーズ
この記事の英語版に更新があります。ご覧の翻訳には含まれていない変更点があるかもしれません。
最終更新日 2020年02月11日(火)
Table of Contents
リリースフェーズを使用すると、アプリの新しいリリースがデプロイされる前に特定のタスクを実行できます。リリースフェーズは、次のようなタスクに役立ちます。
- ご使用のアプリの slug から CDN または S3 バケットへの CSS、JS、その他のアセットの送信
- キャッシュストアの準備または無効化
- データベーススキーマの移行の実行
リリースフェーズタスクが失敗した場合は、新しいリリースがデプロイされず、現在のリリースは影響を受けません。
リリースフェーズを使用するとき、特にデータベース移行を実行している場合は、念頭に置いておくべき設計に関する考慮事項がいくつかあります。リリースフェーズには 1 時間のタイムアウトがあり、この制限を延長することはできません。
リリースフェーズタスクの指定
リリースフェーズ中に実行するタスクを指定するには、アプリの Procfile で release
プロセスタイプを定義します。 Heroku に Docker イメージをデプロイしている場合は、Container Registry でのリリースフェーズの使用についての詳細を参照してください。
この Procfile の例では、release
は Django データベースの移行を実行します。
release: python manage.py migrate
web: gunicorn myproject.wsgi --log-file -
この例では、release
は、複数の異なるコマンドが含まれているスクリプトを実行します。
release: ./release-tasks.sh
web: gunicorn myproject.wsgi --log-file -
release
コマンドが実行されるタイミング
新しいリリースが作成されると、そのリリースがアドオンの環境設定への変更によって発生したものでない限り、常に release
コマンドが One-off dyno で実行されます。新しいリリースは、次のすべてのイベントによって作成されます。
- アプリのビルドの成功
- 環境設定の値の変更 (環境設定がアドオンに関連付けられている場合は除く)
- パイプラインのステージ変更
- ロールバック
- Platform API によるリリース
- 新しいアドオンのプロビジョニング
アプリの dyno は、リリースフェーズが正常に完了するまで新しいリリースで起動されません。
heroku ps
コマンドを使用すると、release
コマンドが実行されていることを確認できます。
heroku ps:type release=<type>
を使用して dyno タイプを設定できます。ただし、最初のリリースフェーズが実行される前にタイプを設定することはできません。
release コマンドの失敗
release
コマンドが 0 以外の終了ステータスで終了するか、または Dyno Manager によってシャットダウンされた場合、そのリリースは失敗します。この場合、そのリリースはアプリの Dyno formation にデプロイされません。リリースフェーズが失敗した場合は、メール通知を受信します。
リリースが環境設定の値への変更によってトリガーされた場合は、release
コマンドが失敗した場合でも、その環境設定の値は変更されたままになります。
ビルドが成功しても、それに関連付けられたリリースは失敗する可能性があります。これにより、ビルドキャッシュは消去されません。
失敗した release
コマンドには通常、アプリのコードの修正が必要になります。必要な変更を行った後、新しいコードをプッシュして新しいリリースをトリガーします。
場合によっては、リリースの失敗がアプリのコードとは関連がないことがあります。たとえば、リリースフェーズ中に外部サービスが使用できなくなることがあります。 このような場合は、releases-retry CLI プラグインを使用すると、アプリ上の新しいビルドをトリガーすることなく失敗したリリースを再試行できます。
リリースステータスとログのチェック
リリース (失敗したリリースと、長時間実行の release
コマンドのために保留中になっているリリースを含む) のステータスをチェックするには、heroku releases
を実行します。
$ heroku releases
=== limitless-savannah-19617 Releases - Current: v52
v53 Deploy ad7c527 release command failed jbyrum@heroku.com
v52 Deploy b41eb7c jbyrum@heroku.com
v51 Deploy 38352d3 jbyrum@heroku.com
...
Use the heroku releases:output
command to see the output of a particular release
command:
$ heroku releases:output RELEASE_NUMBER
--- Migrating db ---
INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO [alembic.runtime.migration] Will assume transactional DDL.
リリースログも Heroku ダッシュボードから取得できます。
リリースステータスのプログラムでのチェック
リリースのステータスをプログラムでチェックするには、特定のリリース用に Platform API を curl
するか、またはすべてのリリースを一覧表示できます (詳細は Platform API のドキュメントを参照)。次の例では、status
キーに failed
の値が含まれています。
$ curl -n https://api.heroku.com/apps/<app_id_or_name>/releases/ \
-H "Accept: application/vnd.heroku+json; version=3"
{
"app":{
"id":"a933b6af-b2e3-4a03-91a9-1c758110a553",
"name":"limitless-savannah-19617"
},
"created_at":"2016-07-29T22:43:20Z",
"description":"Deploy ad7c527",
"status":"failed",
"id":"735a9e8c-ef49-4047-8079-984d40a84051",
"slug":{
"id":"a49ed40f-801a-45ff-8b90-758c5a33637f"
},
"updated_at":"2016-07-29T22:43:20Z",
"user":{
"email":"jbyrum@heroku.com",
"id":"cbcd34ce-c556-4289-8bc7-2dc160649fb7"
},
"version":53,
"current":true,
"output_stream_url":"https://release-output.heroku.com/streams/a9/..."
}
リリースの出力は output_stream_url
属性の下に表示されており、この URL で GET
リクエストを作成することによってプログラムで取得できます。
この出力はチャンクされたエンコーディングで送信され、コマンドが完了すると接続は閉じられます。
特定の URL に対してデータが送信された後にクライアントが接続した場合、そのデータはコマンドの最初からバッファリングされます。 出力は、コマンドの進行中と、完了後のいつでもストリーミングできます (後者の場合は、すべての出力が即座に送信される)。
release コマンドのキャンセル
release
コマンドをキャンセルするには、最初に、そのコマンドを実行している One-off dyno を識別します。
$ heroku ps
=== release (Free): bundle exec rake db:migrate
release.5129: up 2016/02/01 12:17:45 (~ 2s ago)
次に、heroku ps:stop
コマンドにその One-off dyno の名前を指定します。
$ heroku ps:stop release.5129
レビューアプリと postdeploy スクリプト
レビューアプリは、完全かつ廃棄可能な Heroku アプリで、いずれかの GitHub プルリクエストのコードを実行します。レビューアプリは、1 回限りの設定タスクを実行するために使用される postdeploy スクリプトをサポートしています。
次のタイムラインは、レビューアプリでリリースフェーズと postdeploy スクリプトの両方を使用している場合の操作の順序と推奨されるタスクの分割を示しています。
新しいプルリクエストごとに、レビューアプリの作成が開始されます。
- ビルドが成功した後、
release
コマンドが実行されます。これは、次のことに推奨されます。- データベーススキーマの設定と移行
- CDN のアップロード
- キャッシュの無効化とウォームアップ
- リリースフェーズが失敗した場合は、レビューアプリの作成も失敗します。
リリースフェーズの成功後:
- postdeploy スクリプトが実行されます。これは、次のような 1 回限りのタスクに推奨されます。
- OAuth クライアントと DNS の設定
- レビューアプリのテストデータベースへのシードまたはテストデータのロード
プルリクエストへの以降のいずれかの変更の場合:
release
コマンドが再び実行されます。- postdeploy スクリプトは再び実行されません。postdeploy は、レビューアプリの作成時に 1 回のみ実行されます。
このセクションの推奨事項は、postdeploy スクリプトを使用する Deploy to Heroku ボタンアプリにも適用されます。
設計に関する考慮事項
失敗したリリースが最新リリースのままになっている時間を最小限に抑える
ビルドは成功したが、リリースフェーズが失敗した場合、ビルトされた slug は、アドオン環境設定の変更によってトリガーされるそれ以降のすべてのリリースで使用されます。これらのリリースではリリースフェーズが実行されないため、これはアプリケーションコードが、依存しているリリースフェーズタスク (データベース移行など) が実行されずにデプロイされることを示します。これを回避するために、リリースフェーズのすべての失敗を (たとえば、一時的に以前のリリースにロールバックして) できるだけ早く解決することをお勧めします。
ファイルシステムの永続性が必要なアセットコンパイルまたはその他のタスクには推奨されない
リリースフェーズ中のファイルシステムの変更はアプリの Dyno formation にデプロイされない (dyno のファイルシステムは一時的である) ため、リリースフェーズはファイルシステムの永続性が必要なタスクに適していません。 アセットコンパイルは、ビルド中に実行されます。リリースフェーズは、コンパイルされたアセットを CDN にアップロードするために使用できます。
次の考慮事項は、主に手動のデータベース移行スクリプトを作成する場合に関連しています。 ORM (ActiveRecord など) を使用している場合は、これらの考慮事項が適用されない可能性があります。 データベースのベストプラクティスについての詳細を参照してください。
データベース移行にトランザクションを使用する
データベース移行を実行する場合は、常にトランザクションを使用する必要があります。 トランザクションによって、変更をデータベースにコミットする前にすべての移行操作が成功していることが保証されます。 これにより、リリースフェーズ中に部分的な移行が失敗する可能性が最小限に抑えられます。 リリースフェーズ中にデータベース移行が失敗した (つまり、移行コマンドが 0 以外のステータスで終了した) 場合、新しいリリースはデプロイされません。 トランザクションが使用されなかった場合、データベースが部分的に移行された状態のままになる可能性があります。 スキーマ/データを修正するには、リリースフェーズではなく、heroku run
を使用することをお勧めします。
移行を実行する前に、データベースがすでに移行されているかどうかをチェックする
新しいリリースは、環境設定の設定やアプリへの新しいアドオンの追加など、多くのアクションによって作成されます。 データベース移行スクリプトは、新しい移行を実行する前に、データベースがすでに移行されているかどうかをチェックする必要があります (たとえば、テーブル/列は存在するか、存在しない場合は追加する)。 これにより、新しいリリース (新しい環境設定から作成されたリリースなど) によってデータベース移行が再実行されることがなくなります。
移行を実行する前に、データベースに対するアドバイザリロックを取得する
Heroku リリースは並列に動作できますが、これは、リリースフェーズがデータベース移行を実行している場合に問題になることがあります。 多くの一般的なリレーショナルデータベース (Postgres や MySQL など) は、並列移行を防止するために使用できるアドバイザリロック機能を提供しています。 アドバイザリロックは、アプリケーションで適用されるデータベースロックです。取得されていると、テーブルが書き込みに対してロックされないため、アプリケーションは引き続き正常に動作します。
Postgres では、移行を実行する前に、アドバイザリロックを取得できます。 次の例では、キー migration
を使用してアドバイザリロックを取得しようとしています。
SELECT pg_try_advisory_lock(migration);
ロックに成功した場合、Postgres は t
を返します。 これで、安全に移行を実行できるようになります。 失敗した場合は、f
が返され、移行に失敗する可能性があります。
アドバイザリロックがトランザクション内で取得された場合は、そのトランザクションがコミットされると、ロックは自動的に解放されます。 また、次を呼び出してロックを解放することもできます。
SELECT pg_advisory_unlock_all();
または
SELECT pg_advisory_unlock(key);
既知の問題
- release コマンドが実行されたとき、アドオンが完全にプロビジョニングされていないために失敗する場合があります。たとえば、DB 移行を実行しようとするときに DB アドオンのプロビジョニングが完了していない場合です。 Heroku Postgres と Heroku Redis はどちらも、この問題の影響を受けません。