バックグラウンド ジョブ

ユーザー インターフェイス (UI) とは無関係に実行されるバックグラウンド タスクは、さまざまな種類のアプリケーションで重要な役割を果たしています。 その例として、バッチ ジョブ、多くの処理能力を消費するタスク、長時間実行されるプロセス (ワークフローなど) を挙げることができます。 バックグラウンド ジョブの実行は、ユーザーの介入を必要としません。アプリケーションでジョブを起動した後も、ユーザーから対話式に送られる要求を処理し続けることができます。 アプリケーションの UI に対する負荷が小さくなるので、稼働率が向上し、対話の応答時間が短縮されます。

たとえば、ユーザーからアップロードされた画像のサムネイルをアプリケーションで生成する場合、その処理をバックグラウンド ジョブとして実行し、完成したサムネイルを記憶域に保存することができます。処理が完了するまでユーザーを待たせる必要はありません。 同様に、何かを購入する状況においても、注文を処理するワークフローをバックグラウンドで開始すれば、その間もユーザーは UI を使って Web アプリを閲覧することができます。 バックグラウンド ジョブが完了した時点で、保存された注文データを更新し、注文内容を確認するメールをユーザーに送信することが可能です。

特定のタスクをバックグラウンド ジョブとして実装するかどうかの主要な判断基準は、そのタスクが、ユーザーの介入なしで実行できるかどうかと、ジョブの完了まで UI を待機状態にする必要があるかどうかです。 完了までユーザーまたは UI を待機させる必要のあるタスクは、バックグラウンド ジョブに適していない可能性があります。

バックグラウンド ジョブの種類

バックグラウンド ジョブには次のような種類があります。

  • CPU に負荷のかかるジョブ (数学的計算、構造モデル分析など)。
  • 入出力の多いジョブ (一連のストレージ トランザクションの実行、ファイルのインデックス作成など)。
  • バッチ ジョブ (夜間のデータ更新、スケジュール設定された処理など)。
  • 長時間実行されるワークフロー (受注処理、サービスやシステムのプロビジョニングなど)。
  • 機密データ処理 (より安全な場所にタスクが引き渡されて処理される)。 たとえば、機密データは、Web アプリ内で処理するより、 Gatekeeper パターン などのパターンに従い、保護された記憶域にアクセスできる分離されたバックグラウンド プロセスにデータを転送した方が賢明です。

トリガー

バックグラウンド ジョブは、さまざまな方法で開始することができます。 大きく次の 2 つのカテゴリに分けられます。

  • イベント ドリブン トリガー。 イベント (通常、ユーザーが実行する操作またはワークフロー内のステップ) に反応する形でタスクが開始されます。
  • スケジュール ドリブン トリガー。 タイマーに基づくスケジュールに従ってタスクが呼び出されます。 スケジュールに従って定期的に呼び出すことも、指定された時刻に 1 回だけ呼び出すこともできます。

イベント ドリブン トリガー

イベント ドリブン型の呼び出しでは、トリガーを使ってバックグラウンド タスクが開始されます。 イベント ドリブン トリガーの例を次に示します。

  • UI やその他のジョブによってキューにメッセージが追加されます。 メッセージに含まれているのは、実行された操作 (ユーザーによる注文など) に関するデータです。 バックグラウンド タスクは、このキューで待機し、新しいメッセージの到着を検出します。 そのメッセージが読み取られ、その中のデータが、バックグラウンド ジョブの入力として使用されます。 このパターンは、 メッセージベースの非同期通信と呼ばれます。
  • UI やその他のジョブによって記憶域に値が保存されるか値が更新されます。 バックグラウンド タスクは記憶域を監視し、変更を検出します。 そのデータが読み取られ、バックグラウンド ジョブの入力として使用されます。
  • UI やその他のジョブからエンドポイント (Web サービスとして公開されている API、HTTPS URI など) に要求が送られます。 その際バックグラウンド タスクを実行するために必要なデータは要求の一部として渡されます。 エンドポイントまたは Web サービスがバックグラウンド タスクを呼び出すときに、そのデータが入力として使用されます。

イベント ドリブンの呼び出しに適したタスクの例としては、画像処理、ワークフロー、リモート サービスへの情報送信、電子メール メッセージの送信、マルチテナント アプリケーションへの新しいユーザーのプロビジョニングが代表的です。

スケジュール ドリブン トリガー

スケジュール ドリブン型の呼び出しでは、タイマーを使ってバックグラウンド タスクが開始されます。 スケジュール ドリブン トリガーの例を次に示します。

  • 同じアプリケーション内でローカルに実行されているタイマーまたはその動作環境であるオペレーティング システムの機能として実行されているタイマーによってバックグラウンド タスクが定期的に呼び出されます。
  • Azure Logic Apps などの別のアプリケーションで実行されているタイマーから API や Web サービスに対して定期的に要求が送られます。 その API または Web サービスによってバックグラウンド タスクが呼び出されます。
  • 別のプロセスまたはアプリケーションによってタイマーが起動され、指定された時間の経過後または特定の時刻にバックグラウンド タスクが呼び出されます。

スケジュール ドリブンで呼び出すことが適しているタスクの例としては、バッチ処理ルーチン (ユーザーの直近の行動に基づいて、表示する関連製品一覧を更新するなど)、ルーチン データ処理タスク (インデックスの更新、蓄積された結果の生成など)、日次レポート用のデータ分析、データのリテンション期間のクリーンアップ、データ一貫性チェックが代表的です。

スケジュール ドリブンのタスクを単一のインスタンスとして実行する必要がある場合は、次の点に注意してください。

  • スケジューラを実行するコンピューティング インスタンス (Windows のスケジュール タスクを使用する仮想マシンなど) がスケーリングされた場合、そのスケジューラのインスタンスが複数実行され、 タスクのインスタンスも複数起動される可能性があります。 この詳細については、 べき等に関するこのブログ記事をお読みください。
  • タスクの実行時間がスケジューラのイベントの発生間隔を超えた場合、先に起動されたインスタンスが終わる前に、スケジューラによってタスクのインスタンスがもう 1 つ起動されます。

結果の返送

バックグラウンド ジョブは、その呼び出し元となった UI やプロセスとは独立したプロセスで (または場合によっては別の場所で) 非同期的に実行されます。 バックグラウンド タスクは、開始後放置できること、またその実行中に UI や呼び出し元のプロセスに影響を与えないことが理想です。 呼び出し元のプロセスはタスクの完了を待機せず、 いつタスクが完了するかを自動的には検出できません。

バックグラウンド タスクが呼び出し元のタスクと情報をやり取りし、進行状況や完了を伝えることができるようにするには、そのための機構を実装する必要があります。 いくつかの例を次に示します。

  • UI や呼び出し元のタスクからアクセスできる記憶域にステータス インジケーターの値を書き込み、呼び出し元が必要に応じてその値を監視、確認できるようにする。 その他、バックグラウンド タスクから呼び出し元に返す必要のあるデータも同じ記憶域に格納することができます。
  • 応答キューを設定し、そこで UI または呼び出し元を待機させる。 バックグラウンド タスクは、このキューにメッセージを送ることでステータスや完了を知らせることができます。 バックグラウンド タスクから呼び出し元に返す必要のあるデータもそのメッセージに格納することができます。 Azure Service Bus を使っている場合、 ReplyTo プロパティと CorrelationId プロパティを使ってこの機能を実装できます。
  • UI や呼び出し元がステータス情報を取得する目的でアクセスできる API またはエンドポイントをバックグラウンド タスク側で公開する。 その応答に、バックグラウンド タスクから呼び出し元に返す必要のあるデータを格納することができます。
  • バックグラウンド タスクから API を介して UI や呼び出し元をコールバックし、定義済みのポイントや完了時点でステータスを通知する。 コールバックのタイミングとしては、ローカルで発生するイベントや、パブリッシュとサブスクライブのメカニズムを利用することができます。 バックグラウンド タスクから呼び出し元に返す必要のあるデータは、要求またはイベントのペイロードに格納することができます。

ホスティング環境

バックグラウンド タスクは、Azure の各種プラットフォーム サービスを使ってホストすることができます。

  • Azure Web アプリと Web ジョブ。 Web ジョブを使うと、各種のスクリプトや実行可能プログラムをカスタム ジョブとして Web アプリのコンテキスト内で実行することができます。
  • Azure Functions 。 関数は、長時間実行されないバックグラウンド ジョブに使用できます。 もう 1 つのユース ケースは、ワークロードが既に App Service プランでホストされているが、十分に活用されていない場合です。
  • Azure Virtual Machines。 Windows サービスがある場合または Windows タスク スケジューラを使用する場合、バックグラウンド タスクを専用の仮想マシンにホストするのが一般的です。
  • Azure Batch。 バッチは、仮想マシンの管理されたコレクションを実行するため、大量の計算を必要とする作業をスケジュールするためのプラットフォーム サービスです。 自動的にコンピューティング リソースを拡張できます。
  • Azure Kubernetes Service (AKS)。 Azure Kubernetes Service は、Kubernetes on Azure にマネージド ホスティング環境を提供します。
  • Azure Container Apps。 Azure Container Apps を使用すると、コンテナーに基づいてサーバーレス マイクロサービスを構築できます。

以降のセクションでこれらの方法について詳しく説明すると共に、適切な方法を選ぶうえでの注意事項を取り上げます。

Azure Web アプリと Web ジョブ

Azure Web ジョブを使うと、Azure Web アプリ内でカスタム ジョブをバックグラウンド タスクとして実行できます。 Web ジョブは、Web アプリのコンテキスト内で継続するプロセスとして実行されます。 または、Azure Logic Apps や外的要因 (ストレージ BLOB やメッセージ キューに対する変更など) からのトリガー イベントに反応する形で実行されます。 ジョブは、起動と停止をオンデマンドで行うことができ、正規の手順でシャットダウンすることができます。 継続的に実行される Web ジョブは、エラーが発生した場合、自動的に再起動されます。 再試行とエラーのアクションは、設定で変更することができます。

Web ジョブを構成する際の注意事項を次に示します。

  • イベント ドリブン トリガーを使ってジョブを起動する場合は、 [連続的に実行] として構成する必要があります。 スクリプトまたはプログラムは、"site/wwwroot/app_data/jobs/continuous" という名前のフォルダーに格納されます。
  • スケジュール ドリブン トリガーを使ってジョブを起動する場合は、 [スケジュールに従って実行] として構成する必要があります。 スクリプトまたはプログラムは、"site/wwwroot/app_data/jobs/triggered" という名前のフォルダーに格納されます。
  • ジョブを構成するときに [オンデマンドで実行] オプションを選択した場合、 [スケジュールに従って実行] オプションと同じコードが起動時に実行されます。

Azure Web ジョブは、Web アプリのサンドボックス内で実行されます。 つまり、環境変数にアクセスして、接続文字列などの情報を Web アプリとの間で共有することができます。 Web ジョブは、それを実行しているコンピューターの一意の識別子にアクセスすることができます。 Azure Storage のキューや BLOB、テーブルにアクセスしてアプリケーション データを取得したり、Service Bus にアクセスしてメッセージングや通信を行ったりするには、 AzureWebJobsStorage という接続文字列を使用します。 ジョブのアクション ログ ファイルにアクセスするには、 AzureWebJobsDashboard という接続文字列を使用します。

Azure の Web ジョブの特徴を次に示します。

  • セキュリティ:Web ジョブの保護には、Web アプリのデプロイ資格情報が使用されます。
  • サポートされるファイルの種類: Web ジョブは、コマンド スクリプト (.cmd)、バッチ ファイル (.bat)、PowerShell スクリプト (.ps1)、bash シェル スクリプト (.sh)、PHP スクリプト (.php)、Python スクリプト (.py)、JavaScript コード (.js)、実行可能プログラム (.exe.jarなど) を使って定義することができます。
  • デプロイ: スクリプトと実行可能ファイルは、 Azure portalVisual Studio、 または Azure WebJobs SDK を使ってデプロイできます。スクリプトと実行可能ファイルを次の場所に直接コピーしてデプロイすることもできます。
    • トリガーによって実行する場合: site/wwwroot/app_data/jobs/triggered/{job name}
    • 連続実行の場合: site/wwwroot/app_data/jobs/continuous/{job name}
  • ログ:Console.Out は INFO として処理 (マーク) されます。 Console.Error は ERROR として処理 (マーク) されます。 監視情報と診断情報には、Azure ポータルからアクセスできます。 ログ ファイルは、サイトから直接ダウンロードすることができます。 これらの情報は次の場所に保存されます。
    • トリガーによって実行する場合:Vfs/data/jobs/triggered/jobName
    • 連続実行の場合:Vfs/data/jobs/continuous/jobName
  • 構成:Web ジョブの構成には、ポータル、REST API、PowerShell を使用できます。 ジョブの構成情報は、ジョブ スクリプトと同じルート ディレクトリにある settings.job という名前の構成ファイルで指定できます。 例:
    • { "stopping_wait_time":60 }
    • { "is_singleton": true }

考慮事項

  • 既定では、Web アプリをスケーリングすると Web ジョブもスケーリングされます。 ただし、 is_singleton 構成プロパティを true に設定すれば、単一インスタンスで実行されるようにジョブを構成することが可能です。 単一インスタンスの Web ジョブは、スケーリングが不要なタスク (インデックスの再構築、データ分析など、同時に複数のインスタンスで実行することが望ましくないタスク) に適しています。
  • 長時間実行されるジョブやリソースの消費量が大きい Web ジョブは、Web アプリのパフォーマンスへの影響を最小限に抑えるために、新しい App Service プランで空の Azure Web App インスタンスを作成し、そこでホストすることを検討してください。

Azure Functions

Web ジョブに似たオプションは Azure Functions です。 このサービスはサーバーレスであり、短時間実行されるイベントドリブン トリガーに最も適しています。 関数は、設定時に実行するように構成されているときに、スケジュールされたジョブをタイマー トリガーを使用して実行するためにも使用できます。

Azure Functions は、長時間実行される大規模なタスクには推奨されません。予期しないタイムアウトの問題が発生する可能性があるためです。 ただし、ホスティング プランによっては、スケジュールドリブン トリガーについて検討できます。

考慮事項

バックグラウンド タスクがイベントに応答して短時間実行されると予想される場合は、従量課金プランでのタスクの実行を検討してください。 実行時間は最大時間まで構成できます。 関数の実行時間が長いほどコストが高くなります。 また、CPU を集中的に使用するジョブはメモリの消費が多く、コストが高くなる可能性があります。 サービスのために追加のトリガーをタスクの一部として使用する場合、それらは個別に課金されます。

短期間のタスクが多数あるが、連続して実行されることが予想される場合は、Premium プランの方が適しています。 このプランでは、より多くのメモリと CPU が必要になるため、コストが高くなります。 利点は、仮想ネットワーク統合などの機能を使用できることです。

専用プランは、ワークロードが既に実行されている場合にバックグラウンド ジョブに最適です。 VM を十分に活用していない場合は、同じ VM 上で実行して、コンピューティング コストを共有できます。

詳細については、次の記事を参照してください。

Azure Virtual Machines

バックグラウンド タスクの実装形態によっては、Azure Web Apps にデプロイできなかったり、便利ではなかったりすることもあります。 Windows のサービスや、サード パーティのユーティリティ、サード パーティの実行可能プログラムがその代表的な例です。 また、アプリケーションのホスティング環境とは異なる実行環境向けに作成されたプログラムも同様です。 たとえば、Unix や Linux のプログラムを Windows アプリケーションまたは .NET アプリケーションから実行するケースなどが該当します。 Azure Virtual Machines には、さまざまなオペレーティング システムが用意されているので、そこから目的のオペレーティング システムを選んで、仮想マシン上で必要なサービスまたは実行可能ファイルを運用することができます。

どのようなときに Virtual Machines を使うかについては、 Azure App Services、Cloud Services、Virtual Machines の比較に関するページを参照してください。 Virtual Machines の選択肢については、 Azure の Windows 仮想マシンのサイズに関するページを参照してください。 Virtual Machines で利用できるオペレーティング システムと既製イメージの詳細については、 Azure Virtual Machines Marketplaceを参照してください。

バックグラウンド タスクを別の仮想マシンで開始するには、いくつかの方法があります。

  • タスクの公開エンドポイントに要求を送り、アプリケーションからオンデマンドで直接タスクを実行する。 これにより、タスクに必要なデータが渡され、 タスクは、このエンドポイントによって呼び出されます。
  • 選択したオペレーティング システムに用意されているスケジューラやタイマーを使い、スケジュールに基づいて動作するようにタスクを構成する。 たとえば Windows では、Windows タスク スケジューラを使ってスクリプトやタスクを実行できるほか、 仮想マシンに SQL Server がインストールされていれば、SQL Server エージェントを使ってスクリプトやタスクを実行することができます。
  • Azure Logic Apps を使用し、タスクが待機しているキューにメッセージを追加するか、タスクが公開している API に要求を送ることによってタスクを開始する。

バックグラウンド タスクを開始する方法の詳細については、冒頭のセクション「 トリガー 」を参照してください。

考慮事項

Azure 仮想マシンにバックグラウンド タスクをデプロイするかどうかは、次の点を考慮して判断してください。

  • 独立した Azure 仮想マシンでバックグラウンド タスクをホスティングすれば、運用の幅が広がり、開始と実行、スケジューリング、リソース割り当てを細かく制御することができます。 ただし、バックグラウンド タスクを実行するためだけに仮想マシンをデプロイしなければならない場合、ランタイム コストが大きくなります。
  • Azure Resource Manager コマンドレットを使って仮想マシンを管理したり、仮想マシンの基本的なステータスを監視したりすることはできますが、Azure portal にはタスクを監視する機能も、障害の発生したタスクを自動的に再起動する機能もありません。 コンピューティング ノード内のプロセスやスレッドを制御する機構も存在しません。 通常、仮想マシンを使うためには、タスクのインストルメンテーションや仮想マシン内のオペレーティング システムからデータを収集するメカニズムを別途導入する必要があります。 1 つの方法として、 Azure 用の System Center 管理パックを使うことが解決策として考えられます。
  • HTTP エンドポイントを介して公開される監視プローブの作成を検討してください。 正常性チェックの実行、運用情報や統計情報の収集、エラー情報の照合を監視プローブのコードから実行して管理アプリケーションに返すようにします。 詳細については、「正常性エンドポイントの監視パターン」を参照してください。

詳細については、次を参照してください。

Azure Batch

数十、数百または数千の VM 間で高パフォーマンス コンピューティング (HPC) ワークロードを 大規模に並列実行する必要がある場合は、Azure Batch の使用を検討してください。

バッチ サービスにより VM がプロビジョニングされ、VM にタスクが割り当てられ、タスクが実行され、進行状況が監視されます。 バッチは、ワークロードに応じて VM を自動的にスケール アウトできます。 また、バッチは、ジョブのスケジューリングも提供します。 Azure Batch は、Linux と Windows VM の両方をサポートします。

考慮事項

Batch は、本質的に並列ワークロードの実行に適しています。 最終的に低減されたステップで並列計算を実行するか、ノード間でメッセージの受け渡しを必要とする並列タスクの場合は、 Message Passing Interface (MPI) アプリケーション を実行することもできます。

Azure Batch のジョブは、ノード (VM) のプールで実行されます。 1 つの方法は、必要なときのみプールを割り当て、ジョブの完了後に削除することです。 この場合、ノードがアイドル状態にならず、使用率が最大限化されますが、ノードが割り当てられるまで、ジョブは待機する必要があります。 または、前もってプールを作成することができます。 この方法では、ジョブが開始されるまでの時間を短縮できますが、ノードがアイドル状態のままになる可能性があります。 詳細については、 プールとコンピューティング ノードの有効期間を参照してください。

詳細については、次を参照してください。

Azure Kubernetes Service

Azure Kubernetes Service (AKS) を使用すると、ホストされている Kubernetes 環境を管理できます。これによって、コンテナー化されたアプリケーションを簡単にデプロイおよび管理できるようになります。

コンテナーは、バック グラウンド ジョブを実行するために役立ちます。 次の利点があります。

  • コンテナーは、高密度のホストをサポートします。 各 VM に複数のコンテナーを配置して、コンテナー内のバック グラウンド タスクを分離できます。
  • コンテナー オーケストレーターは、内部負荷分散、内部ネットワークと他の構成タスクの構成を処理します。
  • コンテナーは、必要に応じて起動/停止できます。
  • Azure Container Registry を使用して、Azure の境界内にコンテナーを登録することができます。 これは、セキュリティ、プライバシー、および近接の面でメリットがあります。

考慮事項

  • コンテナー オーケストレーターの使用方法を理解する必要がある。 DevOps チームのスキル セットによって、これが問題になる、またはならない場合があります。

詳細については、次を参照してください。

Azure Container Apps

Azure Container Apps を使用すると、コンテナーに基づいてサーバーレス マイクロサービスを構築できます。 Container Apps の特徴は次のとおりです。

考慮事項

Azure Container Apps では、基になる Kubernetes API への直接アクセスは提供されません。 Kubernetes API とコントロール プレーンにアクセスする必要がある場合は、 Azure Kubernetes Serviceを使用してください。 ただし、Kubernetes スタイルのアプリケーションを構築したいが、すべてのネイティブ Kubernetes API とクラスター管理への直接アクセスは必要ない場合は、Container Apps により、ベスト プラクティスに基づくフル マネージド エクスペリエンスが提供されます。 このような理由から、多くのチームが、Azure Container Apps を使用してコンテナー マイクロサービスの構築を開始することを選択する場合があります。

詳細については、次を参照してください。

クイックスタートを使用して、初めてのコンテナー アプリの作成を始めることができます。

パーティション分割

バックグラウンド タスクを既存のコンピューティング インスタンスでホストすることにした場合、そのコンピューティング インスタンスとバックグラウンド タスクそのもののパフォーマンス特性に波及する影響を考慮に入れる必要があります。 以下、既存のコンピューティング インスタンスを間借りする形でタスクを配置するか、独立したコンピューティング インスタンスを専用に用意して配置するかを決めるうえでの判断材料としてください。

  • 稼働率:バックグラウンド タスクに、アプリケーションの他の領域 (特に、ユーザーとの対話を直接つかさどる UI などの領域) と同じレベルの稼働率は必要ありません。 バックグラウンド タスクは、処理をキューに追加することができるため、遅延や接続失敗時の再試行など、稼働率を左右する要因は、ある程度許容することができます。 ただし、要求が溢れてしまうとキューがブロックされてアプリケーション全体に影響が生じるので、ある程度の処理能力は必要です。

  • スケーラビリティ:通常、バックグラウンド タスクは、アプリケーションの UI など対話的な操作をつかさどる領域とは、スケーラビリティの要件が異なります。 UI のスケーリングは需要のピークに対応できるように行う必要があるのに対し、未処理のバックグラウンド タスクは混雑時を避け、より少ないコンピューティング インスタンスで実行することができます。

  • 回復性:バックグラウンド タスクだけをホストするコンピューティング インスタンスは、障害が発生しても、それらのタスクの要求をキューに追加 (つまりタスクが回復するまで延期) すれば、アプリケーション全体に致命的な影響は生じません。 適切な時間内にコンピューティング インスタンスやタスクが再起動されれば、アプリケーションのユーザーが影響を被ることはありません。

  • セキュリティ:バックグラウンド タスクは、アプリケーションの UI などとは、セキュリティ上の要件や制限が異なります。 独立したコンピューティング インスタンスを使うことによって、そのタスクに異なるセキュリティ環境を適用することができます。 また、セキュリティと独立性を高めるために、Gatekeeper などのパターンを使って、バックグラウンド コンピューティング インスタンスと UI とを分離させることもできます。

  • パフォーマンス: バックグラウンド タスクに用いるコンピューティング インスタンスのタイプは、具体的なタスクのパフォーマンス要件に合わせて選ぶことができます。 タスクに求められる処理能力が UI ほど高くない場合は、より安価なコンピューティング オプションを選ぶことができます。逆に、UI よりも高い処理能力と多くのリソースが必要である場合は、コンピューティング インスタンスを増設することができます。

  • 管理性:バックグラウンド タスクは、開発とデプロイの頻度が、アプリケーションのメイン コードや UI とは異なる場合があります。 独立したコンピューティング インスタンスにデプロイすることで、更新とバージョン管理を単純化することができます。

  • コスト: バックグラウンド タスクを実行するためのコンピューティング インスタンスを追加した場合、ホスティングにかかるコストが増えます。 処理能力の向上とコストの増大のトレードオフを慎重に検討する必要があります。

詳細については、「リーダー選定パターン」および「競合コンシューマー パターン」を参照してください。

競合

バックグラウンド ジョブのインスタンスが複数存在する場合、データベースや記憶域などのリソースやサービスを奪い合う状況が生じる可能性があります。 このとき同時アクセスによってリソースの競合が生じ、サービスの稼働状態や記憶域におけるデータの整合性に矛盾が生じる場合があります。 リソースの競合は、排他的ロックを使って解決できます。 排他的ロックは、リソースを奪い合っている複数のタスクのインスタンスが同時にサービスにアクセスできないように (データを破損させないように) する手法です。

競合を解決するもう 1 つのアプローチとして、バックグラウンド タスクをシングルトンとして定義し、インスタンスを 1 つしか実行できないようにする方法もあります。 しかし、この方法では、複数インスタンス構成の利点である信頼性とパフォーマンスが活かされません。 特に、複数のバックグラウンド タスクを常時塞いでしまうほどに UI の処理能力が高い場合はなおさらです。

バックグラウンド タスクを自動的に再起動できるようにし、さらに需要のピークに対処できるだけの処理能力を与えることが必須となります。 たとえば、十分なリソースを備えたコンピューティング インスタンスを割り当てるか、要求をいったんキューに格納しておき後で需要が落ち着いたときに処理するメカニズムを実装するか、その両方の手段を組み合わせることが考えられます。

調整

バックグラウンド タスクは、複合的な成り立ちを有している場合があり、1 つの結果を得たり、すべての要件を満たしたりするためには、ばらばらに存在するいくつものタスクが必要になることがあります。 そのような場合の常套手段は、より小さなステップ (サブタスク) にタスクを分割し、複数のコンシューマーによって実行できるようにすることです。 複数のステップでジョブが構成されていれば、個々のステップを他のジョブで再利用できるため、効率がよく柔軟性も向上します。 ステップの追加と削除、順序変更も容易に行うことができます。

複数のタスクやステップを調整するのは容易ではありませんが、その解決策として 3 つのパターンがよく知られているので、実装するうえでの参考にしてください。

  • 再利用可能な複数のステップにタスクを分解する。 アプリケーションは、その処理の対象となる情報に対し、複雑度の異なる多様なタスクを実行しなければならない場合があります。 その処理を一体型のモジュールとして実行するのが、柔軟性は低いものの実装方法としては簡単です。 しかしそのアプリケーション内のどこか他の箇所で同じ処理の一部分が必要になったとき、この方法では、コードのリファクタリングや最適化、再利用の可能性が制限されます。 詳細については、「パイプとフィルターのパターン」を参照してください。

  • タスクのステップの実行を管理する。 アプリケーションで実行するタスクが、多数のステップから成り立っていて、その中でリモート サービスを呼び出したり、リモート リソースにアクセスしたりする場合があります。 それぞれのステップは互いに独立しているかもしれませんが、それらを指揮するのは、タスクを実装するアプリケーションのロジックです。 詳細については、「Scheduler Agent Supervisor パターン」を参照してください。

  • タスクのステップの障害復旧を管理する。 一連のステップによって実行される作業は、最終的にそれらが集まって整合の取れた操作となるものであり、そのいずれかのステップで障害が発生した場合は、アプリケーション側で元に戻す必要があります。 詳細については、「補正トランザクション パターン」を参照してください。

回復性に関する考慮事項

バックグラウンド タスクが Reliable Services をアプリケーションに提供するためには、その回復力が重要となります。 バックグラウンド タスクを計画、設計する際は、次の点を考慮してください。

  • バックグラウンド タスクは、再起動を正規の手順で処理できることが必要です。データが破損したり、アプリケーションに不整合をもたらしたりすることは許されません。 長時間実行されるタスクや複数のステップから成るタスクでは、"チェック ポイント" の使用を検討してください。ジョブの状態を永続的ストレージに保存するか、または (妥当である場合) メッセージとしてキューに保存します。 たとえば、状態情報をメッセージとしてキューに永続化し、タスクの進行状況に合わせてその状態情報を漸増的に更新します。そうすれば、良好な状態であることが確認できている最新のチェックポイントからタスクを処理することができ、最初からやり直す必要はありません。 Azure Service Bus キューを使っている場合は、メッセージ セッションを使って同じシナリオを実現できます。 SetState メソッドと GetState メソッドを使って、アプリケーションの処理状態を保存したり取得したりすることが可能です。 信頼性のあるマルチステップのプロセスとワークフローを設計する方法の詳細については、「Scheduler Agent Supervisor パターン」を参照してください。

  • バックグラウンド タスクとのやり取りにキューを使っている場合、普段よりも高い負荷がアプリケーションにかかっているときにはキューがバッファーとして働き、タスクに送られた要求を溜めておくことができます。 負荷が低くなったときにタスクの処理が UI に追い付くことができます。 このことはまた、再起動しても UI がブロックされないことを意味します。 詳細については、「キュー ベースの負荷平準化パターン」を参照してください。 タスクによって重要度に差がある場合は、 Priority Queue パターン を用い、重要度の高いタスクが低いタスクよりも先に実行されるようにすることをお勧めします。

  • メッセージによって開始されるバックグラウンド タスクまたはメッセージを処理するバックグラウンド タスクは、さまざまな不整合、たとえば不適切な順序で到着するメッセージ、頻繁にエラーが起こるメッセージ (" 有害なメッセージ")、複数回配信されるメッセージに対処できるように設計する必要があります。 以下、具体例に沿って説明します。

    • 既存のデータ値に基づいてデータを変更する (たとえば既存の値に加算する) など、特定の順序で処理する必要のあるメッセージが、送信されたときと同じ順序で到着するとは限りません。 また、バックグラウンド タスクの各インスタンスにかかる負荷の変動によって、メッセージを処理するインスタンスが変わったり、メッセージの処理の順番が入れ替わったりする可能性もあります。 特定の順序で処理する必要のあるメッセージは、バックグラウンド タスクが常に正しい順序で処理できるように、シーケンス番号やキーなど、何らかのインジケーターを含んでいる必要があります。 Azure Service Bus を使っている場合、メッセージ セッションを使って配信の順序を保証することができます。 ただし通常は、メッセージの順序が問題とならないようにプロセスを設計した方が効率的です。

    • バックグラウンド タスクは通常、キュー内のメッセージを読み出し (このとき、対象のメッセージは、一時的に他のメッセージ コンシューマーからは見えなくなります)、 正常に処理した後でそのメッセージを削除します。 メッセージの処理中にバックグラウンド タスクで障害が発生した場合、読み出しのタイムアウト期間が経過するとそのメッセージが再びキューに現れ、 別のタスク インスタンスによって (または同じインスタンスの次回の処理サイクルで) 処理されることになります。 同じメッセージに起因するエラーが繰り返しコンシューマーで発生すると、やがてキューがいっぱいになり、タスクやキュー、最終的にはアプリケーションそのものまでブロックされてしまいます。 そのため、有害なメッセージを検出してキューから除去することが不可欠となります。 Azure Service Bus を使っている場合、エラーの原因となるメッセージを自動的に、または手動で、対応する 配信不能キュー に移動することができます。

    • キューは Least Once のメカニズムで配信が保証されますが、同じメッセージが複数回配信される可能性もあります。 また、メッセージの処理後、キューから削除される前にバックグラウンド タスクで障害が発生した場合、そのメッセージは再び処理の対象となります。 バックグラウンド タスクは、1 と 1 のかけ算のように、同じメッセージを繰り返し処理しても、エラーを招いたり、アプリケーションのデータに不整合を来さないことが必要です。 中には、自然にそのような性質を備えている操作もあります (格納された値を特定の新しい値に設定するなど)。 ただし、格納されている既存の値に対して値を加算する際、格納されている値が、メッセージ送信時と同じであることをチェックしなかった場合は、不整合の原因となります。 Azure Service Bus のキューは、重複するメッセージを自動的に削除するように構成できます。 少なくとも 1 回のメッセージ配信に関する課題の詳細については、べき等メッセージ処理に関するガイダンスを参照してください。

    • Azure Queue Storage や Azure Service Bus キューなどの一部のメッセージング システムでは、メッセージがキューから読み取られた回数を示すデキュー カウント プロパティがサポートされています。 反復するメッセージや有害なメッセージの処理には、このプロパティを利用できます。 詳細については、 非同期メッセージングの基本Idempotency パターンに関するページを参照してください。

スケーリングとパフォーマンスの考慮事項

バックグラウンド タスクは、システムに負荷がかかっている状況下で、アプリケーションをブロックしないこと、また操作に遅延が生じることによる不整合を起こさないことが重要です。そのような事態を引き起こさないだけのパフォーマンスをバックグラウンド タスクには確保する必要があります。 通常、バックグラウンド タスクをホストするコンピューティング インスタンスをスケーリングすることによってパフォーマンスは向上します。 バックグラウンド タスクを計画、設計する際は、スケーラビリティとパフォーマンスに関して次の点を考慮してください。

  • Azure は自動スケールをサポートしています。Web Apps と Virtual Machines でホストされたデプロイに対し、そのときどきの需要と負荷に応じて、または、あらかじめ決められたスケジュールに基づいてスケールアウトし、不要になったら再びスケールインする、といったことが可能です。 この機能を利用することで、運用コストを最小限に抑えつつ、アプリケーション全体の性能を適切な水準に保つことができます。

  • バックグラウンド タスクに求められるパフォーマンス要件がアプリケーションの他の領域 (たとえば UI や、データ アクセス レイヤーなどのコンポーネント) と異なる場合、バックグラウンド タスクをまとめて別個のコンピューティング サービスでホスティングすれば、UI とバックグラウンド タスクを独立にスケーリングしながら負荷に対応することができます。 バックグラウンド タスク同士でパフォーマンス要件が大きく異なる場合は、それらを分けて、それぞれのタイプを別々にスケーリングすることをお勧めします。 ただし、その場合、実行時のコストが増加する可能性があります。

  • 単純にコンピューティング リソースをスケーリングするだけでは、負荷がかかった状況下でのパフォーマンス低下を防ぎきれない場合もあります。 処理チェーン全体の 1 か所がボトルネックになってしまうことを防ぐためには、ストレージ キューやその他のリソースをスケーリングすることも、場合によっては必要です。 それ以外にも、アプリケーションとバックグラウンド タスクで使われているストレージや各種サービスの最大スループットなどの制限も考慮してください。

  • バックグラウンド タスクの設計は、スケーリングを想定して行う必要があります。 たとえば、バックグラウンド タスクは、キューで待機したり適切なキューにメッセージを送信したりするために、使用中のストレージ キューの数を動的に検出できなければなりません。

  • 既定では、Web ジョブは、それが関連付けられている Azure Web Apps インスタンスに合わせてスケーリングされます。 ただし、Web ジョブを単一のインスタンスとしてのみ実行する必要がある場合は、 { "is_singleton": true } という JSON データを含んだ Settings.job ファイルを作成してください。 これで、関連付けられている Web アプリのインスタンスが複数存在していても、Web ジョブ インスタンスを強制的に 1 つだけ実行させることができます。 スケジュール設定されたジョブで、常に単一インスタンスとして実行する必要がある場合などにこの手法を活用できます。

次のステップ