.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-isolated | Function 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 を変えるだけで衝突を回避できます。スロット運用をしている場合は、該当のアプリ設定をスロット設定として扱うのが定石です。
実施手順(失敗しない順番)
「設定したのに直らない」を避けるため、手順を順番通りに進めます。
- どのストレージを使っているか確定する Azure Portal で Function App の構成を開き、
AzureWebJobsStorageがどのストレージを指しているかを確認します。Key Vault 参照の場合も、最終的にどのストレージになるかを把握します。 - 同じストレージを参照している Function App / スロットがないか洗い出す 「本番とステージングが同じ」「旧アプリが残っている」など、衝突の温床を見つけます。特に Durable は “共有しても動くことがある” ため、気づきにくいです。
- hubName を決める(アプリ名 + 環境名で固定) 例:
myappProdHub/myappStgHub/myappDevHub - host.json または アプリ設定で hubName を設定する スロット差分が必要ならアプリ設定がおすすめです。
- 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 を分離するのが最短ルートです。

コメント