.NET 8 Isolated Durable Functions が Azure で Pending/Running のまま進まない原因と解決策(Task Hub hubName 衝突)

.NET 3.1 の Durable Functions を .NET 8 Isolated に移行したら、ローカルでは動くのに Azure ではオーケストレーターが Pending / Running のまま進まない――。例外も有力なログも出ず、原因が掴めないケースがあります。この記事では、まず「止まり方」から逆算する切り分けポイントを整理し、最終的な決定打として多い Durable Task Hub 名(hubName)の衝突と、その解決手順を具体例つきで解説します。

目次

現象:Azure だけで Durable Orchestrator が進まない(Pending / Running で止まる)

今回の症状は、典型的には次のように見えます。

見えている状況よくある実態補足
Orchestrator がずっと Pendingオーケストレーションの開始要求は記録されたが、ワーカーが処理を開始できていない起動設定・ストレージ・Task Hub 競合などで起きやすい
Orchestrator が Running のまま動かないどこかで待機状態(タイマー待ち、外部イベント待ち、アクティビティ未完了など)本当に待っているのか、進行不能なのかを切り分けるのが重要
ローカルでは正常ローカルは別ストレージ(Azurite/開発用ストレージ)で動き、Azure の設定問題が露見しない「AzureWebJobsStorage が別物」になりがち
App Insights に例外が出ないそもそもログが出ていない / 重要ログのレベルが出ていない / 設定が不足Isolated は Program.cs 側のログ設定に依存しやすい

この現象は「コードのバグ」だけでなく、Azure 側の構成・ストレージ・ハブ設定・複数環境の衝突で再現しやすいのが厄介なところです。

.NET 8 Isolated の Durable Functions でハマりやすいポイント

.NET 8 Isolated(Worker)では、従来の in-process と比べて次がズレやすくなります。

  • 実行形態が別プロセスになるため、起動・DI・ログ設定の主導権が Program.cs 側に寄る
  • Functions Runtime(v4)と Worker の整合が前提。設定が少しズレると「動いているように見えるのに処理が進まない」状態を作りやすい
  • Durable はストレージ(Task Hub)に強く依存するため、ストレージの共有・衝突・権限問題が表に出やすい

このため、ローカルで動くこと自体は「コードが正しい」証拠になりづらく、Azure の運用形態(スロット・複数アプリ・同一ストレージ共有)を含めて点検するのが近道です。

最初にやるべき切り分けチェック(Azure 側で同症状が出る)

まずは、原因を“コード”に決め打ちせず、再現しやすいポイントを上から潰します。以下の表は、実務で効果が高い順に並べています。

チェック項目見る場所なぜ重要かミス例
Functions Runtime が v4(~4)Function App の設定 / 構成.NET 8 Isolated は v4 前提。ズレると起動はしても挙動が壊れることがある古いランタイムのまま移行
FUNCTIONS_WORKER_RUNTIME が dotnet-isolatedFunction App > 構成 > アプリ設定Worker が正しく起動しないと、トリガーは見えても処理が進まないdotnet のまま
AzureWebJobsStorage の接続先が想定どおりFunction App > 構成 / Key Vault 参照Durable の状態管理・キュー処理はストレージが生命線。誤ると「進まない」が起きる別環境のストレージを参照
ストレージに対する権限・ネットワーク制限Storage Account のネットワーク / アクセス制御ネットワーク制限や Private Endpoint 構成によってアクセスできず詰まる許可 IP / VNet 経路が不足
Durable Task Hub(保存先)の“衝突”host.json / アプリ設定 / スロット設定今回の決定打。同一ストレージを共有していると衝突で停止に見えるprod と staging が同じ hubName
ログ設定(Isolated)Program.cs / App Insights 設定ログが出ないと詰む。Isolated は設定が不足すると「何も出ない」状態になりやすいログレベルが高すぎる / 連携不足

ここまでのチェックで特に多いのが、ストレージ接続の取り違えと、次に紹介するTask Hub 名(hubName)の衝突です。

Durable Task Hub(hubName)とは何か:衝突すると何が起きるのか

Durable Functions は、オーケストレーションの状態(実行履歴、待機中のイベント、アクティビティの完了通知など)をTask Hubと呼ばれる論理単位で管理します。Azure Storage Provider を使う場合、hubName を元にして、ストレージ内にキューやテーブル(または同等のデータ構造)が作られ、そこでオーケストレーターとアクティビティのやり取りが行われます。

重要なのは、hubName は「同じストレージ上での Durable の“部屋番号”」のようなものだという点です。部屋番号が同じなら、違うアプリでも同じ部屋を共有してしまいます。

hubName が衝突しやすい典型パターン

パターンなぜ起きるか症状(例)
複数の Function App が同一ストレージを共有コストや管理簡略化で AzureWebJobsStorage を共通化している片方が拾う/拾わない、Pending のままに見える、キューが増える
スロット(staging/production)で同一ストレージ+同一 hubNameスロット設定を分けていない / hubName を固定で書いているデプロイ直後だけ不安定、staging で開始した実行が prod に混ざる
移行前のアプリ(.NET 3.1)と移行後(.NET 8)が同居旧アプリ停止前に新アプリを同じストレージで動かしてしまう処理が進まない、見えないところで競合、監視が読めない
環境(dev/test/prod)の hubName を分けていないhost.json を共通化し、環境差分を作っていないテストの実行が本番の状態に干渉、不可解な “止まり”

衝突が起きると、Durable が内部で使う制御用のキューや履歴の保存先を複数のホストが奪い合う形になり、結果として「開始はされたのに、その後が進まない」「監視上は Running/Pending だが、処理しているワーカーが実質いない」といった見え方になります。

さらに厄介なのは、競合の仕方によっては例外が表に出ないことです。ログ設定が薄いと「何も起きていない」ように見えてしまいます。

今回の決定打:hubName の衝突(同名 Task Hub 競合)

最終的に判明した原因は、Durable Task Hub 名(hubName)が他のアプリ/環境と衝突していたことでした。

特に、次の条件が揃うと再現しやすくなります。

  • AzureWebJobsStorage が複数の Function App(または複数スロット)で共通
  • hubName を明示していない(=既定の hubName を共有してしまう)、または同じ hubName を使い回している
  • 旧アプリと新アプリが同時に存在する、あるいは staging と production を同時に回している

この状態だと、オーケストレーションの開始は記録されても、その後の処理が別ホスト側に流れたり、競合で捌けず、結果として Pending/Running のままに見えるケースが出ます。

対処:hubName を「環境ごと / アプリごと」に一意にする

解決策はシンプルで、hubName を衝突しない値に変えることです。やり方は大きく2つあります。

方法1:host.json で hubName を指定する

最も分かりやすいのは host.json で指定する方法です。

{
  "extensions": {
    "durableTask": {
      "hubName": "myappProdHub"
    }
  }
}

ポイントは、「アプリ名 + 環境名」などでユニークにすることです。例えば次のような命名規則にしておくと、運用が楽になります。

用途例意図
開発myappDevHub開発用の実行が本番に混ざらない
検証myappStgHubスロット/ステージングの干渉を防ぐ
本番myappProdHub本番専用のタスクハブを固定

注意:hubName はストレージ側の命名制約の影響を受けます。記号を混ぜるとストレージ資源名に使えず問題になる場合があるため、迷ったら 英数字のみ(先頭は英字)の命名に寄せるのが安全です。

方法2:Azure のアプリ設定で hubName を指定する(スロット差分にも強い)

Azure 側の構成で切り替えたい場合(特にスロットごとに hubName を変えたい場合)は、アプリ設定を使うのが便利です。

設定キー:

AzureFunctionsJobHost__extensions__durableTask__hubName

設定値(例):

myappProdHub

この方法なら、同じ成果物(同じビルド)を dev/staging/prod にデプロイしても、環境ごとに hubName を変えるだけで衝突を回避できます。スロット運用をしている場合は、該当のアプリ設定をスロット設定として扱うのが定石です。

実施手順(失敗しない順番)

「設定したのに直らない」を避けるため、手順を順番通りに進めます。

  1. どのストレージを使っているか確定する Azure Portal で Function App の構成を開き、AzureWebJobsStorage がどのストレージを指しているかを確認します。Key Vault 参照の場合も、最終的にどのストレージになるかを把握します。
  2. 同じストレージを参照している Function App / スロットがないか洗い出す 「本番とステージングが同じ」「旧アプリが残っている」など、衝突の温床を見つけます。特に Durable は “共有しても動くことがある” ため、気づきにくいです。
  3. hubName を決める(アプリ名 + 環境名で固定) 例:myappProdHub / myappStgHub / myappDevHub
  4. host.json または アプリ設定で hubName を設定する スロット差分が必要ならアプリ設定がおすすめです。
  5. Function App を再起動し、再度オーケストレーションを開始する 設定反映を確実にするため、変更後は再起動を挟みます。

直ったかを確認するチェックポイント

hubName を変更すると、新しい Task Hub として別の保存領域が使われるため、確認観点も整理しておくと安心です。

確認ポイント期待する状態補足
新しいインスタンスが Pending のまま止まらない開始後すぐに Running へ遷移し、アクティビティが呼ばれるまずは最短ルートの簡単なオーケストレーションで試す
ストレージ内に hubName に紐づく資源が作られる新しい hubName の領域が生成されるStorage Explorer 等で確認(削除は慎重に)
App Insights に実行ログが出る開始・アクティビティ実行・完了が追えるログが出ない場合は Program.cs 側も見直す

なお、hubName を変えると以前の hubName 側に残っているインスタンスは新しいハブからは見えません。本番稼働中のアプリで変更する場合は、実行中オーケストレーションの扱い(完了を待つのか、停止して切り替えるのか)を必ず検討してください。

ログが出ないときの最低限の見直し(Isolated の落とし穴)

「例外が出ない」のではなく「見える場所に出していない」ケースも多いです。Isolated では Program.cs でログを明示しておくと、切り分けが一気に楽になります。

例:コンソールログの最低限設定(概念例)

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureLogging(logging =>
{
logging.SetMinimumLevel(LogLevel.Information);
logging.AddConsole();
})
.Build();

host.Run();

App Insights を使う場合も、拡張の導入状況・設定値(接続文字列など)・ログレベルを合わせて点検してください。Durable の内部挙動は、情報が取れないと推測ゲームになりやすいので、最初にログの通り道を整えるのが効きます。

再発防止:hubName 衝突を設計で潰す

今回の問題は「設定ミス」というより、運用が大きくなるほど自然に踏みやすい地雷です。再発防止としては、次のいずれか(または両方)をルール化するのがおすすめです。

環境ごとにストレージを分ける

  • dev/test/prod で Storage Account を分離
  • 権限・ネットワーク・コストの境界が明確になり、事故が減る

ストレージを共有するなら hubName を必ず分ける(スロットも含む)

  • 同一ストレージを共有する設計は珍しくないが、Durable では hubName が衝突すると影響が大きい
  • アプリ設定 AzureFunctionsJobHost__extensions__durableTask__hubName を環境ごとに設定し、スロット設定にする
運用形態推奨理由
単一環境・単一アプリhubName 明示(固定)将来の拡張で衝突を避けられる
スロット運用(stg/prod)アプリ設定で hubName を分離(スロット設定)成果物は同じでも環境差分を安全に出せる
複数アプリでストレージ共有アプリごとに hubName を分離同居させるなら“部屋番号”を必ず分ける

まとめ:Pending / Running で止まったら、まず Task Hub の衝突を疑う

.NET 8 Isolated への移行後に、Azure で Durable Orchestrator が Pending / Running のまま進まない場合、原因はコード以外(設定やストレージ)に潜んでいることが少なくありません。

  • Runtime v4 と dotnet-isolated の整合を確認する
  • AzureWebJobsStorage が正しいか、アクセスできるかを確認する
  • そして同一ストレージで hubName が衝突していないかを最優先で疑う

hubName を環境ごと・アプリごとに一意にするだけで、動かなかったオーケストレーションが一気に進み始めることがあります。ローカルで動くのに Azure で止まる系のトラブルでは、まず「共有しているもの」を洗い出し、Task Hub を分離するのが最短ルートです。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次