リリースフェーズ
この記事の英語版に更新があります。ご覧の翻訳には含まれていない変更点があるかもしれません。
最終更新日 2024年04月01日(月)
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
一方、この例では、release
はいくつかの異なるコマンドが含まれているスクリプトを実行します。
release: ./release-tasks.sh
web: gunicorn myproject.wsgi
release
コマンドが実行されるタイミング
新しいリリースが作成されると、そのリリースがアドオンの環境設定への変更によって発生したものでない限り、常に release
コマンドが One-off dyno で実行されます。新しいリリースは、次のすべてのイベントによって作成されます。
- アプリのビルドの成功
- 環境設定の値の変更 (環境設定がアドオンに関連付けられている場合は除く)
- パイプラインのステージ変更
- ロールバック
- Platform API によるリリース
- 新しいアドオンのプロビジョニング
アプリの dyno は、リリースフェーズが正常に完了するまで新しいリリースで起動されません。
heroku ps
コマンドを使用して、release
コマンドが実行されていることを確認します。
dyno タイプは `heroku ps:type release={type} を使用して設定できますが、リリースフェーズが初めて実行された後に限られます。
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
...
特定の release
コマンドの出力を表示するには、heroku releases:output
コマンドを使用します。
$ 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 にクエリを実行して特定のリリースを見つけるか、すべてのリリースを一覧表示します (詳細は Platform API のドキュメントを参照)。curl
を使用する次の例では、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 (Eco): 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」 (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);
ラップされたリリースフェーズタスクで終了ステータスが正しくロールアップされるようにする
リリースフェーズで複数のタスクが 1 つのファイル (release-tasks.sh
など) にラップされている場合、それらの各タスクが自分の終了ステータスを取得するようにします。取得されたステータスが 0
(ゼロ) でない場合、ラッパーはそれを適切に伝える役割を担います。ラップされたコマンドの終了ステータスがどれも取得されないと、それらの結果が受け入れられ、release.sh
を正常に呼び出したというだけの理由でリリースフェーズは成功したと報告されます。
この動作の単純な例は、次のようになります。
# release-tasks.sh
## Step to execute
bundle exec rails db:migrate
# check for a good exit
if [ $? -ne 0 ]
then
# something went wrong; convey that and exit
exit 1
fi
# other code, potentially
既知の問題
- リリースコマンドが実行されるときにアドオンが完全にプロビジョニングされていないため、リリースコマンドが失敗する可能性があります。たとえば、データベース移行がトリガーされたが、データベースアドオンのプロビジョニングが完了していない場合があります。この問題は Heroku Postgres や Heroku Data for Redis に影響しません。