稼働中 Azure Web Apps に影響を与える操作について (再起動とか)

Azure ポータルでは Web Apps に関して色々と操作出来るが、その際の稼働中 Web Apps の挙動について説明。 なお、Functions の挙動は異なるので注意 (https://poke-dev.hatenablog.com/entry/2019/11/24/160018)。

ここでは ASP.NET MVC のプロジェクト作成時の状態を Free/Basic プランの 1 インスタンス Web Apps で確認している。 Basic についても「常時接続」設定がオフの場合は、本件の確認内容については Free と本質的に差異はないはず。

また、Basic 以上のプランであれば 2 インスタンス以上にスケールアウトすることが可能だが、スケールアウト時は仮想マシン自体が増加するので、挙動の差異は特にないはず(同じ挙動が全インスタンスで同様に行われるだけ)。

なお、セッション情報の格納先が既定の InProc となっている場合は、ワーカープロセスの再起動やリサイクルでセッション情報も失われるので、注意。

Azure Web Apps + ASP.NET の環境的なレイヤーについて

Azure Web Apps + ASP.NET は、IIS ワーカープロセスの上に、ASP.NET の AppDomain が作成されて、アプリが動作する構成をとっている。

その為、まず IIS ワーカープロセスの生成が必要で、その上で、AppDomain (= ASP.NET アプリ) を生成して初めてアプリが動作する。 操作によっては、ワーカープロセスごと再生成されたり、ワーカープロセスは変わらずに中の AppDomain だけが再生成されたりするが、それによってアプリが応答可能になるまでの待機時間が異なる(もちろん、ワーカープロセスからの再生成の方が時間がかかる)。

なお、InProc のセッション情報は AppDomain 側に用意されているようなので、ワーカープロセスの再起動だろうが AppDomain の再起動だろうが、InProc のセッション情報はリセットされる。

Web Apps のアプリ設定として「常時接続」の項目があるが、この項目がオフの場合は、アプリが 20 分間アイドル状態になった際に自動的に当該ワーカープロセスが終了させられるので注意 (恐らく、Azure ポータルで「再起動」を実行したのと同じ状態)。 稼働中のワーカープロセスがない状態でリクエストが来た場合は、ワーカープロセスの生成がまず発生するので、アプリが有効になってレスポンスが返るまでに時間がかかる。

AppDomain の切り替えの場合、ブラウザからのリクエストがエラーとなるタイミングはないようだが、ワーカープロセスの切り替えの場合はエラーとなるタイミングがある様子。

各操作時の挙動について

1 インスタンス、常時接続オンの状態で操作した際の挙動を簡単にまとめると、以下。

操作 再起動対象 備考
Azure ポータルでの「再起動」 ワーカープロセス WP 終了時、利用不可になるタイミングあり
ファイル日付更新 (デプロイ含む) AppDomain AppDomain 並行起動により、利用不可になるタイミングなし
Azure ポータルでのアプリ設定変更 ワーカープロセス WP 並行起動により、利用不可になるタイミングなし
スロットのスワップ - レスポンス準備整い次第ドメインスワップが行われるため、スワップ先が利用不可になるタイミングなし

注: 「利用不可になるタイミングなし」は、リクエスト元に 503 などアプリ管轄外のエラーが返らない事を指しているが、新しいアプリがどれぐらいでレスポンス可能になるかはアプリ次第なので、注意。 例えば、ASP.NET でプリコンパイルを行っていない場合などは、ワーカープロセスの再起動と同時にコンパイル済みのイメージも全て削除されて次回ページアクセス時に再度コンパイルが発生するため、エラーは返されないがレスポンスが返るまで非常に時間がかかる可能性がある。

各操作時の詳細な挙動は以下。

Azure ポータルでの「停止」

稼働中のワーカープロセスが落とされて、落とされた後にリクエストがあった場合も起動は行われない。 稼働中のワーカープロセスで処理中のリクエストが存在する場合は、そのリクエストが終わった後、ワーカープロセスが落とされる。

停止中にリクエストがあった場合は 1 が返されるが、停止完了した後は 2 のページが表示される。

  1. 「HTTP Error 503. The service is unavailable.」 (恐らく IIS のエラーレスポンス)
  2. 「Error 403 - This web app is stopped.」 (恐らく Azure Web Apps の既定のエラーページ)

常時接続オンの場合 常時接続オンの場合も、処理は同じ様子。

Azure ポータルでの「開始」

ワーカープロセスが起動可能状態となる(この時点で起動されるわけではない)。

初回リクエストが来た時点で、ワーカープロセスが起動されて、アプリの準備が出来次第、レスポンスが返される。

常時接続オンの場合 常時接続オンの場合は、リクエストの有無に依らず、開始実行後自動的にワーカープロセスが起動する。 また、ワーカープロセスの起動後に AppDomain も作成され、リクエストに即時応答出来る状態になる。

Azure ポータルでの「再起動」

稼働中のワーカープロセスが落とされて、Azure ポータルでの「開始」実行時と同じ状態になる。 なお、稼働中のワーカープロセスで処理中のリクエストが存在する場合は、そのリクエストの処理が終わった後、ワーカープロセスが落とされる。 再起動実行後、処理中のリクエストの完了待ちでワーカープロセスが終了できない状態で新たなリクエストが来た場合、新しいリクエストも既存のワーカープロセスでそのまま処理される様子。

再起動実行時にリクエストがあった場合、タイミングによっては以下のエラーが返されることがある。

「HTTP Error 503. The service is unavailable.」 (恐らく IIS のエラーレスポンス)

常時接続オンの場合 Azure ポータルでの「開始」と同様の動きになる様子。

bin フォルダ配下のファイル変更や、Web.config の変更 (Kudu から直接)

変更の検出は恐らくファイル更新日付。

稼働中ワーカープロセスはそのままで、中の AppDomain が変更後のアプリ用に同一ワーカープロセス内に新規作成されて、新しいリクエストを処理する。 なお、古い AppDomain がリクエストを処理中だった場合、そのリクエストの処理が完了した後に、古い AppDomain はシャットダウンされる(はず)。 古い AppDomain が処理中リクエストの完了待ちの状態で新たなリクエストが来た場合、新しいリクエストは並行して作成された新しい AppDomain で即座に処理される (Application_Start() にログをしかけて測定)。 一時的に新旧 AppDomain が共存する形となっているはずだが、画面などから確認するすべはない。

なお、完全にアンドキュメンテッドな内容だが、AppDomain リサイクル発生時に処理しているリクエストが存在した場合、処理中の全リクエストが完了して古い AppDomain がシャットダウンした直後に、新しい AppDomain が作成される(新しいリクエストがなかったとしても)。 逆に AppDomain リサイクル発生時に処理しているリクエストがない場合、新しい AppDomain が作成されるタイミングは、新しいリクエストが来たタイミングとなる。

AppDomain の切り替えに留まるからか、ブラウザから見ると新しい設定の準備が整うまで数秒程度の待ち時間は発生するが、利用不能な時間は発生しない様子。 なお、InProc のセッション情報は AppDomain 単位で持っているため、AppDomain 切り替えのタイミングでセッション情報もクリアされる。

常時接続オンの場合 常時接続オンの場合も、処理は同じ様子。

複数インスタンスが起動している場合 ストレージは全インスタンスが同じ場所を見ているため、ファイル変更は全インスタンスが同様に影響を受ける。

Visual Studio からの発行 (デプロイ)

基本的に Web.config の変更と同様の動きの様子(デプロイも結局の所は、ファイルのコピーなので)。

試してはいないが、DevOps からのデプロイ時なども同じ状態になるのではないかと思われる

Azure ポータルでのアプリ設定の変更

基本的には、Azure ポータルでの「再起動」と同じフローに見える。

ただこちらの操作の場合、既存リクエスト処理の終了待ち状態で受けた新たなリクエストは、終了待ちのワーカープロセスではなく新たなワーカープロセス上で処理されるようで、ここが「再起動」時と異なる。

また、終了待ちのワーカープロセスが終了するまで新しいワーカープロセスも起動しないようなので、新たなリクエストは終了待ちのワーカープロセスが終了するまで、待機することになる様子。

以下の説明を見る限り、一時的に重複してワーカープロセスが起動しそうな感じだが、自分が確認した限りでは、これは常時接続オンの場合の挙動だった。

常時接続オンの場合 常時接続オンの場合は、稼働中ワーカープロセスのシャットダウンを待たずに新規ワーカープロセスを並行起動して新しいリクエストを処理するため、稼働中ワーカープロセスのシャットダウン待ちの状態は発生しない。

詳細な動作はアンドキュメンテッドだが、アプリ設定変更後のリクエストも、新規ワーカープロセスがリクエスト待ち受け可能な状態になるまでは旧ワーカープロセスで受け付けてレスポンスを返し、新規ワーカープロセスがリクエスト待ち受け可能になったタイミングで新しいリクエストは全て新規ワーカープロセスで処理し、入れ替わりに旧ワーカープロセスがシャットダウンされるように見受けられる。

スロットのスワップ

スワップの注意点として、ソーススロットとターゲットスロットで行われる処理が異なる点がある。 スワップの目的は単純に 2 つのスロットを入れ替える事ではなく、「ターゲットスロットの動作を極力維持したままで、ソーススロットのアプリと入れ替える」事になる。 最終結果としてはアプリがソースとターゲットで入れ替わる事に違いはないが、この目的達成のために、入れ替わるまでの過程がそれぞれのスロットで異なる。

まず、アプリを入れ替えるために、アプリ設定などのスワップ処理が必要となるが、これらの処理は、基本的に全て「ソーススロット」で行われる。 アプリ設定などが変更されるとワーカープロセスの再起動が発生するが、これが行われるのはソーススロットのみなので、ターゲットスロットのワーカープロセスには影響が出ない。

ソーススロットにターゲットスロットの設定が適用し終えて、ソーススロットのワーカープロセスの準備が完了するまでは、ターゲットスロットで通常通りリクエストが処理される。 ソーススロットでの準備が全て完了したら、FQDN の付け替えを行う。 この時点からターゲットスロットへのリクエストは、ソーススロットで準備していたワーカープロセスで受けることになるが、既に準備は完了している状態なので、ブラウザの待ち時間などは基本的に発生しない (はず。あっても数秒程度)。

この後、ソーススロットで再度、ソーススロット用アプリの設定適用等が行われる。 スワップ完了までは、大体数分ほどかかる。

上記動作を見てもらうと分かると思うが、スワップ中は、一時的にではあるが、ターゲットスロットのアプリ設定で、ソーススロットのアプリが動作するタイミングがある。 そのため、スワップ中にソーススロット(の FQDN)に対して何かしらリクエストを行っていると、本来ターゲットスロットの接続先であるストレージや DB に対して、ソーススロットの FQDN のリクエスト処理が行われる可能性がある。 そのため、スワップ中は少なくともソーススロットに対するリクエストは禁止する必要がある。

プランの変更

Azure ポータルでの「再起動」と同様、既存のワーカープロセスが落とされて、開始可能状態となるように見える。