Durable Functionsの実行可能な実行モデルでは、オーケストレーションが決定論的である必要があり、更新プログラムを展開するときに考慮すべき追加の課題が生まれます。 アクティビティ関数のシグネチャまたはオーケストレーター ロジックに対する変更がデプロイに含まれている場合、実行中のオーケストレーション インスタンスが失敗します。 この状況は、数時間または数日間にわたる作業を表す可能性がある、長時間実行されているオーケストレーションのインスタンスの場合に特に問題があります。
これらのエラーが発生しないようにするには、いくつかのオプションがあります。
- 実行中のオーケストレーション インスタンスがすべて完了するまでデプロイを遅らせます。
- オーケストレーションのバージョン管理を使用して、異なるバージョンのオーケストレーションを共存させることができます (推奨)。
- 実行中のオーケストレーション インスタンスで既存バージョンの関数が使用されていることを確認します。
次のグラフは、Durable Functionsのダウンタイムゼロのデプロイを実現するための 4 つの主要な戦略を比較しています。
| 戦略 | 使用する場合 | 長所 | 短所 |
|---|---|---|---|
| オーケストレーションのバージョン管理 | 重大な変更が加わるアプリケーション。特に、異なるオーケストレーション バージョンの同時実行をサポートする必要があるアプリケーション。 | 重大な変更を伴うダウンタイムなしのデプロイを有効にします。 最小限の構成を必要とする組み込み機能。 |
バージョンの互換性のために、慎重にオーケストレーター コードを変更する必要があります。 |
| 名前ベースのバージョン管理 | 破壊的変更が頻繁に発生しないアプリケーション。 | 実装が簡単。 | メモリ内の関数アプリのサイズと関数の数が増える。 コードの重複。 |
| スロットでの状態チェック | 24 時間以上続く長時間のオーケストレーションまたは頻繁に重複するオーケストレーションが存在しないシステム。 | シンプルなコード ベース。 追加の関数アプリ管理が不要。 |
追加のストレージ アカウントまたはタスク ハブの管理が必要である。 オーケストレーションが実行されていない時間帯が必要である。 |
| アプリケーション ルーティング | 24 時間以上続くオーケストレーションや、頻繁に重複するオーケストレーションの期間など、オーケストレーションが実行されていない期間がないシステム。 | 破壊的変更を伴うオーケストレーションが継続的に実行されている新しいバージョンのシステムを処理する。 | インテリジェントなアプリケーション ルーターが必要。 サブスクリプションで許可されている関数アプリの最大数を超える可能性。 既定値は、100 です。 |
このドキュメントの残りの部分では、これらの戦略について詳しく説明します。
Note
これらのダウンタイムなしのデプロイ戦略の説明では、Durable Functionsに既定のAzure Storage プロバイダーを使用していることを前提としています。 既定のAzure Storage プロバイダー以外のストレージ プロバイダーを使用している場合は、ガイダンスが適切でない場合があります。 さまざまなストレージ プロバイダー オプションとその比較方法の詳細については、Durable Functions ストレージ プロバイダードキュメントを参照してください。
オーケストレーションのバージョン管理
オーケストレーションのバージョン管理機能を使用すると、展開中のダウンタイムを回避しながら、オーケストレーションに重大な変更を加えることができます。 この組み込み機能により、さまざまなバージョンのオーケストレーションが共存し、競合することなく同時に実行できます。
オーケストレーションのバージョニングでは:
- 各オーケストレーション インスタンスは、作成時に永続的に関連付けられたバージョンを取得します。
- 新しいオーケストレーター バージョンを実行しているワーカーは、古いバージョンのインスタンスの実行を続行できます。
- 以前のバージョンのオーケストレーションを実行しているワーカー は、 新しいバージョンのインスタンスを実行できません。
- オーケストレーター関数は、それに応じてバージョンとブランチの実行を調べることができます。
この方法により、異なるバージョンのアプリケーションを実行しているワーカーが安全に共存できるローリング アップグレードが容易になります。 ダウンタイムなしのデプロイを維持しながら、破壊的変更をサポートする必要があるアプリケーションに推奨される戦略です。 オーケストレーションのバージョン管理機能は バックエンドに依存しないため、Durable Function アプリが使用しているストレージ バックエンドに関係なく、それを活用できます。 詳細な構成と実装のガイダンスについては、「 オーケストレーションのバージョン管理」を参照してください。
名前ベースのバージョン管理
関数の新しいバージョンを定義し、関数アプリでは古いバージョンのままにします。 図を見るとわかるように、関数のバージョンが名前の一部になります。 以前のバージョンの関数が保持されるため、実行中のオーケストレーション インスタンスは引き続きそれらを参照できます。 一方で、新しいオーケストレーション インスタンスに対する要求では最新バージョンが呼び出され、オーケストレーション クライアント関数はアプリ設定から参照できます。
この戦略では、すべての関数をコピーし、他の関数への参照を更新する必要があります。 スクリプトを記述することで簡単にできます。 次に示すのは、移行スクリプトを使用したサンプル プロジェクトです。
Note
この戦略では、デプロイ スロットを使用して、デプロイ時のダウンタイムが回避されます。 新しいデプロイ スロットを作成して使用する方法の詳細については、「Azure Functions デプロイ スロット」を参照してください。
スロットでの状態チェック
現在のバージョンの関数アプリが運用スロットで実行されている間に、関数アプリの新しいバージョンをステージング スロットにデプロイします。 運用スロットとステージング スロットをスワップする前に、実行中のオーケストレーション インスタンスがあるかどうかを確認します。 すべてのオーケストレーション インスタンスが完了したら、スワップを実行できます。 この戦略は、実行中のオーケストレーション インスタンスがなくなる期間を予測できる場合に有効です。 これは、オーケストレーションが長時間実行されない場合や、オーケストレーションの実行で重複が頻度に発生しない場合に、最適な方法です。
関数アプリの構成
次の手順でこのシナリオを設定します。
ステージングと運用のために、関数アプリにデプロイ スロットを追加します。
スロットごとに、共有ストレージ アカウントの接続に対して AzureWebJobsStorage アプリケーション設定を設定します。 このストレージ アカウント接続は、Azure Functions ランタイムによって、functions のアクセス キーを安全に格納するために使用されます。 最高レベルのセキュリティを実現するには、ストレージ アカウントに対してマネージド ID 接続を使う必要があります。
スロットごとに、新しいアプリ設定を作成します (例:
DurableManagementStorage)。 その値を、さまざまなストレージ アカウントのconnection stringに設定します。 これらのストレージ アカウントは、信頼性の高い実行のためにDurable Functions拡張機能によって使用されます。 スロットごとに個別のストレージ アカウントを使用します。 この設定をデプロイ スロットの設定としてマークしないでください。 やはり、マネージド ID ベースの接続が最も安全です。関数アプリの host.json ファイルの durableTask セクションで、ステップ 3 で作成したアプリ設定の名前として
connectionStringName(Durable 2.x) またはazureStorageConnectionStringName(Durable 1.x) を指定します。
次の図では、デプロイ スロットとストレージ アカウントの説明した構成を示します。 このような事前にデプロイされている可能性のあるシナリオでは、バージョン 2 の関数アプリが運用スロットで実行されている間、バージョン 1 はステージング スロットに残っています。
host.json の例
次の JSON フラグメントは、host.json ファイルのconnection string設定の例です。
Functions 2.0
{
"version": 2.0,
"extensions": {
"durableTask": {
"hubName": "MyTaskHub",
"storageProvider": {
"connectionStringName": "DurableManagementStorage"
}
}
}
}
関数 1.x
{
"durableTask": {
"azureStorageConnectionStringName": "DurableManagementStorage"
}
}
CI/CD パイプラインの構成
関数アプリに保留中または実行中のオーケストレーション インスタンスがない場合にのみデプロイするように、CI/CD パイプラインを構成します。 Azure Pipelinesを使用している場合は、次の例のように、これらの条件をチェックする関数を作成できます。
[FunctionName("StatusCheck")]
public static async Task<IActionResult> StatusCheck(
[HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestMessage req,
[DurableClient] IDurableOrchestrationClient client,
ILogger log)
{
var runtimeStatus = new List<OrchestrationRuntimeStatus>();
runtimeStatus.Add(OrchestrationRuntimeStatus.Pending);
runtimeStatus.Add(OrchestrationRuntimeStatus.Running);
var result = await client.ListInstancesAsync(new OrchestrationStatusQueryCondition() { RuntimeStatus = runtimeStatus }, CancellationToken.None);
return (ActionResult)new OkObjectResult(new { HasRunning = result.DurableOrchestrationState.Any() });
}
次に、オーケストレーションが実行されなくなるまで待機するように、ステージング ゲートを構成します。 詳細については、「ゲートを使用してデプロイの制御をリリースする」を参照してください
Azure Pipelinesは、デプロイが開始される前に、関数アプリでオーケストレーション インスタンスが実行されているのを確認します。
ここで、新しいバージョンの関数アプリを、ステージング スロットにデプロイする必要があります。
最後に、スロットをスワップします。
デプロイ スロット設定としてマークされていないアプリケーション設定もスワップされるため、バージョン 2 のアプリではストレージ アカウント A への参照が維持されます。オーケストレーションの状態がストレージ アカウントで追跡されるため、バージョン 2 のアプリで実行中のすべてのオーケストレーションは、中断されることなく、新しいスロットで引き続き実行されます。
両方のスロットに同じストレージ アカウントを使用するには、タスク ハブの名前を変更します。 この場合は、ユーザーがスロットの状態とアプリの HubName の設定を管理する必要があります。 詳細については、Durable Functions の
アプリケーション ルーティング
この戦略は最も複雑です。 しかし、オーケストレーションの実行と実行の間に時間がない関数アプリに使用できます。
この戦略では、Durable Functionsの前にアプリケーション ルーターを作成する必要があります。 このルーターは、Durable Functionsを使用して実装できます。 ルーターは次の役割を担います。
- 関数アプリをデプロイします。
- Durable Functionsのバージョンを管理します。
- オーケストレーション要求を関数アプリにルーティングします。
初めてオーケストレーション要求を受信したとき、ルーターでは次のタスクが実行されます。
- Azureに新しい関数アプリを作成します。
- Azureの新しい関数アプリに関数アプリのコードをデプロイします。
- オーケストレーション要求を新しいアプリに転送します。
ルーターは、アプリのコードのバージョンが、Azure内の関数アプリにデプロイされる状態を管理します。
ルーターでは、要求と共に送信されたバージョンに基づいて、デプロイとオーケストレーション要求が適切な関数アプリに転送されます。 パッチ バージョンは無視されます。
破壊的変更が含まれないアプリの新しいバージョンをデプロイするときは、パッチ バージョンをインクリメントできます。 ルーターでは、既存の関数アプリに対してデプロイが行われ、コードの古いバージョンと新しいバージョンに対する要求は、同じ関数アプリにルーティングされます。
破壊的変更が含まれるアプリの新しいバージョンをデプロイするときは、メジャー バージョンまたはマイナー バージョンをインクリメントできます。 その後、アプリケーション ルーターは、Azureに新しい関数アプリを作成し、それにデプロイし、アプリの新しいバージョンの要求をそれにルーティングします。 次の図では、アプリの 1.0.1 バージョンで実行中のオーケストレーションは実行し続けますが、1.1.0 バージョンに対する要求は新しい関数アプリにルーティングされます。
ルーターでは、1.0.1 バージョンでのオーケストレーションの状態が監視され、すべてのオーケストレーションが完了した後でアプリが削除されます。
追跡ストアの設定
各関数アプリでは、個別のスケジュール キュー (可能な場合は異なるストレージ アカウントの) を使用する必要があります。 アプリケーションのすべてのバージョンですべてのオーケストレーション インスタンスのクエリを実行したい場合は、関数アプリ間でインスタンス テーブルと履歴テーブルを共有できます。
trackingStoreConnectionStringName ファイルで trackingStoreNamePrefix と の設定を構成し、すべてのテーブルで同じ値が使用されるようにすることで、テーブルを共有できます。
詳細については、「Azure での Durable Functions のインスタンス管理」を参照してください。