😸

Durable Task Scheduler を .NET Aspire で起動する

に公開

GA したら、それ一択になりそうな Durable Task Scheduler ですが、これにはローカル開発用の Docker イメージが用意されています。
ということは .NET Aspire で起動して簡単に使うことが出来ます。ということで試してみました。

Azure Functions プロジェクトの準備

まずは Azure Functions プロジェクトを作成します。今回は .NET 9 Isolated (.NET 8 でも可) の Azure Functions プロジェクトを作成します。プロジェクト作成時に作成するトリガーは Durable Functions Orchestration を選択しておきます。

作成したらプロジェクトの右クリックメニューから「追加」→「.NET Orchestrator サポート...」を選択して .NET Aspire のサポートを追加します。

次に、Azure Functions プロジェクトで Durable Task Scheduler を使うように設定やパッケージを追加します。現状プレビュー版の機能なのでリリース時には省きたいのですがパッケージは頑張れば含めないことはできるけど設定は host.json なので素直には省けないのがちょっと辛いところです…。まぁそこは置いておいて、以下のように host.json を編集します。

{
  ... 既存の設定 ...
  "extensions": {
    "durableTask": {
      "hubName": "%TASKHUB_NAME%",
      "storageProvider": {
        "type": "azureManaged",
        "connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
      }
    }
  }
}

そして、以下の NuGet パッケージを追加します。

  • Microsoft.Azure.Functions.Worker.Extensions.DurableTask.AzureManaged

現状は 0.4.2-alpha でした。早く v1 になってほしいですね。

.NET Aspire の設定

次に .NET Aspire の方の設定を行います。AppHost プロジェクトの AppHost.cs を開いて以下のコードを追加します。コードとしては Durable Task Scheduler のコンテナイメージを実行するようにして、ポートの設定を行い、接続文字列などを Azure Functins 側で設定しているだけです。

AppHost.cs
var builder = DistributedApplication.CreateBuilder(args);

var functionapp1 = builder.AddAzureFunctionsProject<Projects.FunctionApp1>("functionapp1");

// この if 文の中が Durable Task Scheduler の設定
if (builder.ExecutionContext.IsRunMode)
{
    var durableTaskScheduler = builder.AddContainer("durable-task-scheduler",
        "mcr.microsoft.com/dts/dts-emulator",
        "latest")
        .WithLifetime(ContainerLifetime.Persistent)
        .WithEndpoint(8080, 8080)
        .WithEndpoint(8082, 8082, scheme: "http", name: "Dashboard");
    var durableTaskSchedulerConnectionString = builder.AddParameter("durable-task-scheduler-connectionstring",
        "Endpoint=http://localhost:8080;Authentication=None");
    functionapp1.WithEnvironment("DURABLE_TASK_SCHEDULER_CONNECTION_STRING", durableTaskSchedulerConnectionString)
        .WithEnvironment("TASKHUB_NAME", "default")
        .WaitFor(durableTaskScheduler);
}

builder.Build().Run();

Azure Fuctions プロジェクトのプロファイルの設定

このまま実行すると .NET Aspire のダッシュボードに表示されるポート番号と実際の Azure Functions のポート番号が異なるという問題が起きます。これを回避するには Azure Functions のプロジェクトの Properties\launchSettings.json にあるプロファイルの名前を https にする必要があります。http のエンドポイントしか公開されないのに https という名前にするのは少し心苦しいですが、.NET Aspire の AppHost で設定するプロファイルの名前と合わせてあげる感じです。

設定すると以下のようになります。名前を変えるだけで大丈夫です。

{
  "profiles": {
    "https": {
      "commandName": "Project",
      "commandLineArgs": "--port 7034",
      "launchBrowser": false
    }
  }
}

実行して動作確認

実行すると .NET Aspire ダッシュボードで Durable Task Scheduler が起動していることが確認できます。

http://localhost:PORT/api/Function1_HttpStart にアクセスするとプロジェクトの新規作成されたときに作られたワークフローが起動します。何回か実行したあとに Durable Task Scheduler のダッシュボードにアクセスすると、実行されたワークフローの一覧や詳細情報が確認できます。

これで .NET Aspire で Durable Task Scheduler を使った Azure Functions の開発ができるようになりました。ローカル開発時に Durable Functions の動きがわかりやすく表示されるようになるので、とても捗りそうです。

リリースビルド時には Durable Task Scheduler を除外する

正式リリース後は脳死で Durable Task Scheduler を使うことができると思いますが、現状はプレビュー版の機能なのでリリースビルド時には除外したいところです。プロジェクトファイルを以下のように編集するとリリースビルドとそれ以外で Durable Task Scheduler を使うかどうかを切り替えることが出来ます。

FunctionApp1.csproj
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
    <OutputType>Exe</OutputType>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.23.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.0.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="2.0.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" Version="1.3.0" />
    <!-- リリースビルド時には Durable Task Scheudler のパッケージを追加しない -->
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask.AzureManaged" Version="0.4.2-alpha" Condition="'$(Configuration)' != 'Release'" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.3.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.0.1" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.2" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\FunctionApp1.ServiceDefaults\FunctionApp1.ServiceDefaults.csproj" />
  </ItemGroup>

  <!-- リリースビルド時とそれ以外で host.json を別のファイルにする -->
  <ItemGroup>
    <None Remove="host.release.json" />
    <None Remove="host.debug.json" />
    <None Include="host.release.json" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" Condition="'$(Configuration)' == 'Release'" Link="host.json" />
    <None Include="host.debug.json" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" Condition="'$(Configuration)' != 'Release'" Link="host.json" />
  </ItemGroup>
</Project>

host.release.jsonhost.debug.json を作成して以下のように内容を分けます。

host.release.json
{
  "version": "2.0",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      },
      "enableLiveMetricsFilters": true
    }
  }
}
host.debug.json
{
  "version": "2.0",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      },
      "enableLiveMetricsFilters": true
    }
  },
  "extensions": {
    "durableTask": {
      "hubName": "%TASKHUB_NAME%",
      "storageProvider": {
        "type": "azureManaged",
        "connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING"
      }
    }
  }
}

これでリリースビルド時には Durable Task Scheduler のパッケージが含まれず、host.json も Durable Task Scheduler の設定が含まれないようになります。他にもっといい方法があればコメントで教えてください。

まとめ

Durable Task Scheduler を .NET Aspire で起動する方法を紹介しました。これにより、ローカル開発環境で Durable Functions の動作を簡単に確認できるようになります。そのうち Durable Task Scheudler の .NET Aspire 用のホスティングパッケージが追加されると思います。そうしたら AddDurableTaskScheduler のような感じで簡単に追加できるようになると思いますが、それまでの繋ぎとして使うことができると思います。

Microsoft (有志)

Discussion