Azure FunctionsのDurable Functionsでのデータの永続化とシリアル化

Durable Functions ランタイムは、関数パラメーター、戻り値、およびその他の状態を task hub に自動的に永続化して、信頼性の高い実行を提供します。 ただし、永続的なストレージに永続化されるデータの量と頻度は、アプリケーションのパフォーマンスとストレージ トランザクションのコストに影響を与える可能性があります。 アプリケーションで格納されるデータの種類によっては、データ保有とプライバシー ポリシーも考慮する必要があります。

この記事では、保持されるデータ、大きなペイロードと機密データを処理する方法、サポートされている言語ごとにシリアル化をカスタマイズする方法について説明します。

この記事の内容:

タスク ハブの内容

タスク ハブには、インスタンスの現在の状態と保留中のメッセージが格納されます。

  • インスタンスの状態 には、インスタンスの現在の状態と履歴が格納されます。 オーケストレーション インスタンスの場合、この状態にはランタイム状態、オーケストレーションの履歴、入力、出力、カスタム状態が含まれます。 エンティティ インスタンスの場合、エンティティの状態が含まれます。
  • "メッセージ" には、関数の入力または出力、イベント ペイロード、ルーティングやエンド ツー エンドの関連付けなどの内部目的で使用するメタデータが格納されます。

メッセージは処理後に削除されますが、インスタンスの状態は、アプリケーションまたはオペレーターによって明示的に削除されない限り保持されます。 特に、オーケストレーションの完了後も、オーケストレーションの履歴はストレージに残ります。

状態とメッセージがオーケストレーションの進行状況を表す方法の例については、 タスク ハブの実行例を参照してください。

ストレージ内の状態とメッセージを表す場所と方法は、 ストレージ プロバイダーによって異なりますDurable Task Schedulerを使うと良いです。タスクハブの管理バックエンドを提供し、基盤となる状態ストアを管理してくれるからです。 しかし、Azure Storageは既存のワークロードや、自社ストレージリソースを管理したいアプリにとって依然として堅実な選択肢です。

ストレージ プロバイダー 状態の保存方法 推奨される使用方法
耐久性のあるタスクスケジューラ オーケストレーションとエンティティの状態は、タスクハブリソースの背後にあるマネージドスケジューラバックエンドに格納されます。 新しいDurable Functionsアプリやマネージドデプロイの優先オプションです。
Azure Storage 状態とメッセージは、Azure Storageアカウント内のキュー、テーブル、ブロブで表現されます。 既にAzure Storageに依存している既存のアプリやデプロイに適しています。

シリアル化および永続化されるデータの種類

次の一覧は、Durable Functionsの機能を使用するときにシリアル化および永続化されるさまざまな種類のデータを示しています。

  • オーケストレーター、アクティビティ、およびエンティティ関数のすべての入力と出力には、任意の ID やハンドルされない例外が含まれます。
  • オーケストレーター、アクティビティ、およびエンティティ関数の名前
  • 外部イベントの名前とペイロード
  • カスタム オーケストレーションの状態ペイロード
  • オーケストレーションの終了メッセージ
  • 持続的タイマー ペイロード
  • 持続的な HTTP 要求と応答の URL、ヘッダー、およびペイロード
  • エンティティの呼び出しとシグナル ペイロード
  • エンティティ状態ペイロード

この一覧のペイロード サイズの管理と機密性の高い項目の保護に関するガイダンスについては、次のセクションを参照してください。

Durable Functionsの入力と出力を小さく保つ

Durable Functions API との間で大きな入力と出力をやり取りすると、メモリの問題が発生する可能性があります。 入力と出力はオーケストレーション履歴にシリアル化されます。つまり、大規模なペイロードは時間の経過と同時に、無制限の履歴の増加に大きく貢献できます。 この増加は 、再生中にメモリ例外を引き起こすリスクがあります。

大きな入力と出力の影響を軽減するには、次の操作を行います。

  • サブオーケストレーターに作業を委任して、複数のオーケストレーター間で履歴のメモリ負荷を負荷分散し、個々の履歴のメモリ占有領域を小さく保ちます。
  • 外部ストレージ (Azure Blob Storage など) に大きなデータを格納し、必要に応じてアクティビティ関数内でそのデータを取得できるようにする軽量識別子を渡します。

Durable Task Schedulerでは、大きなペイロードサポートを使って大きなペイロードをAzure Blob Storageにオフロードします。 新しいアプリでは、このパターンはオーケストレーションが持続的な作業間で大きなペイロードを渡す必要がある場合に推奨されます。 Azure Storageプロバイダーを使っても、以下のセクションで示したクレームチェックパターンを適用し、操作間で軽量な参照を渡すことができます。

Tip

大規模なデータを処理するためのベスト プラクティスは、外部ストレージに保持し、必要に応じてアクティビティ内でのみそのデータを具体化することです。

大きなペイロードへの参照を渡す

ストレージ提供者に合ったパターンを選びましょう。

耐久性のあるタスクスケジューラ

Durable Task Schedulerを使う場合は、大きなペイロードサポートを有効にして、ランタイムがAzure Blob Storageに大きなペイロードを書き込み、スケジューラを通じて小さな参照を送信します。 典型的な構成はスケジューラ文書に示されています:

{
  "version": "2.0",
  "extensions": {
    "durableTask": {
      "storageProvider": {
        "type": "azureManaged",
        "connectionStringName": "DTS_CONNECTION_STRING",
        "payloadStorageEnabled": true,
        "payloadStorageThresholdBytes": 262144
      },
      "hubName": "%TASKHUB_NAME%"
    }
  }
}
Azure Storage

Azure Storageプロバイダーでは、Claim Checkパターンを使ってオーケストレーション履歴を小さく抑えつつ、大きなペイロードを処理できます。 オーケストレーターはblobコンテナとblob名を含む軽量参照を渡し、必要に応じてAzure Blob Storageからペイロードを読み書きします。

以下の例は、すでにストレージアカウントにBLOBがあると仮定しています。 オーケストレーションは {"container":"large-payloads","blobName":"input/job-123.json"}のような参照から始めましょう。 必要に応じて、そのアクティビティは processed-payloads 出力コンテナを作成します。 サンプル処理ステップは入力バイトを変更せずにコピーします。それをアプリケーションロジックに置き換えてください。

Important

参照にストレージ認証情報や共有アクセス署名(SAS)を含めてはいけません。 システムはオーケストレーション履歴において参照を保持します。 これらの例は、ストレージコードを簡潔に保つために PAYLOAD_STORAGE_CONNECTION_STRING という名前のアプリ設定を使っています。 本番ワークロードでは、Microsoft Entra IDを使ってブロブデータへのアクセスを許可します。

この例にはAzure.Storage.Blobs NuGet パッケージが必要です。

using System;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Microsoft.Azure.Functions.Worker;
using Microsoft.DurableTask;

public record BlobReference(string Container, string BlobName);

public static class LargePayloadFunctions
{
    [Function("ProcessLargePayload")]
    public static async Task<BlobReference> RunOrchestrator(
        [OrchestrationTrigger] TaskOrchestrationContext context)
    {
        BlobReference inputReference = context.GetInput<BlobReference>()
            ?? throw new InvalidOperationException("A blob reference is required.");

        return await context.CallActivityAsync<BlobReference>(
            nameof(ProcessLargePayloadActivity), inputReference);
    }

    [Function(nameof(ProcessLargePayloadActivity))]
    public static async Task<BlobReference> ProcessLargePayloadActivity(
        [ActivityTrigger] BlobReference inputReference)
    {
        string connectionString =
            Environment.GetEnvironmentVariable("PAYLOAD_STORAGE_CONNECTION_STRING")
            ?? throw new InvalidOperationException("Payload storage is not configured.");
        BlobServiceClient service = new BlobServiceClient(connectionString);

        BlobClient inputBlob = service
            .GetBlobContainerClient(inputReference.Container)
            .GetBlobClient(inputReference.BlobName);
        BinaryData inputData = (await inputBlob.DownloadContentAsync()).Value.Content;

        BlobContainerClient outputContainer =
            service.GetBlobContainerClient("processed-payloads");
        await outputContainer.CreateIfNotExistsAsync();

        string outputName = $"processed/{Guid.NewGuid():N}.json";
        await outputContainer.GetBlobClient(outputName)
            .UploadAsync(inputData, overwrite: true);

        return new BlobReference(outputContainer.Name, outputName);
    }
}

並列活動が複数の大きな結果を生み出した場合、参照リストを返し、そのリストを最終的な集約活動に渡します。 集約活動はペイロードを読み込み結合し、最後に1つの出力ブロブを書き込むべきです。 大きな結果をオーケストレーターで読み込んだり連結したりしないでください。

機密データを操作する

Durable Functions API との間の入力と出力 (例外を含む) は、選択した storage プロバイダーに永続的に保持されます。 これらの入力、出力、または例外に機密データ (シークレット、接続文字列、個人を特定できる情報など) が含まれている場合は、ストレージ プロバイダーのリソースへの読み取りアクセス権を持つすべてのユーザーがそれらを取得できます。

機密データを安全に処理するには、アクティビティ関数内のデータをAzure Key Vaultまたは環境変数からフェッチし、オーケストレーターまたはエンティティとの間でそのデータを直接またはやり取りしないようにします。 この方法は、機密データがストレージ リソースに漏えいするのを防ぐのに役立ちます。

同様に、ストレージ リソースへの書き込みアクセスは厳密に制御する必要があります。ストレージ内の改ざんされたデータによってオーケストレーションの動作が変更される可能性があります。 タスク ハブ ストレージのセキュリティ保護の詳細については、「タスク ハブ ストレージ のセキュリティ保護」を参照してください

Tip

このガイダンスは、 CallHttp オーケストレーター API にも適用され、要求と応答のペイロードがストレージに保持されます。 ターゲット HTTP エンドポイントで認証が必要な場合は、アクティビティ内に HTTP 呼び出しを実装するか、 CallHttp によって提供される組み込みのマネージド ID サポートを使用します。これは、資格情報をストレージに保持しません。

Note

ログへの読み取りアクセス権を持つすべてのユーザー (Application Insights など) がそれらのシークレットを取得する可能性がある場合は、シークレットを含むデータをログに記録しないようにします。

静止時の暗号化

Azure Storage プロバイダーを使用すると、すべてのデータが保存時に自動的に暗号化されます。 ただし、ストレージ アカウントへのアクセス権を持つユーザーは、暗号化されていない形式でデータを読み取ることができます。 機密データのより強力な保護が必要な場合は、事前に暗号化された形式でデータが永続化されるように、最初に独自の暗号化キーを使用してデータを暗号化することを検討してください。

または、.NETユーザーには、自動暗号化を提供するカスタム シリアル化プロバイダーを実装するオプションがあります。 暗号化を使用したカスタムシリアル化の例は、このGitHubサンプルで確認できます。

Note

アプリケーション レベルの暗号化を実装する場合は、オーケストレーションとエンティティが無期限に存在する可能性があることに注意してください。 オーケストレーションまたはエンティティはキーのローテーション ポリシーよりも長く実行される可能性があるため、暗号化キーをローテーションするときにこれが重要になります。 キーのローテーションが行われると、データの暗号化に使用されたキーが、次にオーケストレーションまたはエンティティを実行したときに復号化できなくなる可能性があります。 そのため、カスタム暗号化は、オーケストレーションとエンティティが比較的短時間実行されることが予想される場合にのみ推奨されます。

タスク ハブ ストレージをセキュリティで保護する

タスク ハブをホストするストレージ バックエンドは、重要な信頼境界です。 Durable Task Framework は、オーケストレーションの再生とメッセージ処理中にストレージから読み取ったデータを信頼します。 タスク ハブ ストレージへの書き込みアクセス権を持つすべてのユーザーは、オーケストレーション状態、保留中のメッセージ、または格納されているペイロードを改ざんできます。 これにより、アプリケーションの動作が変更されたり、意図しないアクションがトリガーされたり、関数アプリのコンテキスト内でリモート でコードが実行されたりする可能性があります。

Important

タスク ハブのストレージ資格情報を公開したり、信頼されていない関係者に書き込みアクセス権を付与したりしないでください。 タスク ハブ ストレージへの書き込みアクセスは、任意のコード実行のトリガーなど、アプリケーションの動作を変更するために使用できます。

共有責任

ストレージ バックエンドのセキュリティ保護は、アプリケーションの状態またはコードを格納するデータベースのセキュリティ保護と同じです。 Durable Task Framework は、保存されたデータに対して整合性検証を実行しないため、未承認の変更を防ぐためにストレージ層のアクセス制御に依存します。

Backend セキュリティ責任 Guidance
耐久性のあるタスクスケジューラ Microsoftは基盤となるストレージのバックエンドを管理しています。 アイデンティティ、タスクハブへのアクセス、アプリレベルのセキュリティを管理します。 新しいDurable Functionsアプリの推奨デフォルト。
Azure Storageおよびその他のBYOプロバイダー ストレージアカウントやデータベースとそのセキュリティ管理を管理します。 既にAzure Storageに依存している既存のワークロードやデプロイには適しています。

Note

信頼されていないテナント間で 1 つのタスク ハブを共有しないでください。 タスク ハブはユーザー間のアクセス境界を強制しないため、タスク ハブに対して読み取りまたは書き込みを行うことができるテナントは、その中のすべてのオーケストレーションとエンティティに影響を与える可能性があります。 同様に、セキュリティ境界と同じバックエンド内の個別のタスク ハブに依存しないでください。 Durable Task Scheduler は個々のタスク ハブを対象とした RBAC をサポートしますが、IP 許可リストやプライベート エンドポイントなどのネットワーク制御はスケジューラ レベルでのみ適用されるため、スケジューラ内のタスク ハブはセキュリティ分離境界ではありません。 BYO ストレージ プロバイダーについても同様です。ストレージ アカウントまたはデータベースにアクセスできるテナントは、そのバックエンド上のすべてのタスク ハブにアクセスできます。 テナント間でセキュリティを分離する必要がある場合は、テナントごとに個別のインフラストラクチャ (BYO プロバイダー用の個別のストレージ アカウントまたはデータベース、または個別の Durable Task Scheduler インスタンス) をプロビジョニングします。

ストレージのセキュリティ強化チェックリスト

タスク ハブ ストレージを保護するには、次のベスト プラクティスを適用します。

  • 選ぶバックエンドにはアイデンティティベースの接続を使いましょう。

    • Durable Task Schedulerでは、スケジューラやタスクハブにマネージドIDとRBACを頼りにできます。
    • Azure Storageや他のBYOプロバイダーでは、可能な限り接続文字列よりもマネージドIDを優先してください。

    Durable Functions 用マネージド ID を構成する を参照してください。

  • 最小特権 RBAC ロールを適用します。 必要な最小限のアクセス許可のみを付与します。 不要なユーザーやサービスに広範なストレージアクセスを付与するのは避けましょう。

  • プライベートエンドポイントやサービスエンドポイントを使ってストレージアカウントやスケジューラ展開へのネットワークアクセスを制限しましょう。 この制限により、タスクハブデータへの不正なネットワークレベルアクセスを防ぐことができます。

  • ストレージ アクセスを監視するには、ストレージ アカウントで Azure Monitor のリソース ログ、特に StorageWrite ログ カテゴリを有効にします。 これらのログを、Log Analyticsなどの監視対象ストレージ アカウントの外部の宛先にルーティングして、改ざんできないようにします。 ストレージ ログを参照してください。

  • 接続文字列を使用する場合は、資格情報を定期的にローテーションします。 ストレージ アカウント キーは、他の高い特権の資格情報と同じ注意を払って扱います。

  • マネージド ストレージ バックエンドについて考えてみましょうDurable Task Schedulerは認証、RBAC、ネットワーク隔離などのストレージセキュリティを自動的に管理し、Azure Storageは明示的なストレージ制御を提供します。

シリアル化と逆シリアル化をカスタマイズする

シリアル化のカスタマイズ オプションは言語によって異なります。 [言語] タブを選択すると、使用可能なオプションが表示されます。

.NET Isolated と System.Text.Json

.NET隔離ワーカープロセスで動作するDurable Functions、Azure Functionsアプリ全体でグローバルに設定された同じオブジェクトシリアライザーを使用します(WorkerOptions参照)。 このシリアライザーはNewtonsoft.JsonではなくデフォルトでSystem.Text.Jsonされています。 WorkerOptions.Serializerの変更は推移的にDurable Functionsに適用されます。

.NET での JSON シリアル化の組み込みサポートの詳細については、.NET の 概要ドキュメントで JSON シリアル化と逆シリアル化を参照してください。

次のステップ