App Service プラン再起動時の挙動を探る

# はじめに
[Microsoft Azure Tech Advent Calendar 2020](https://qiita.com/advent-calendar/2020/microsoft-azure-tech) の 8 日目の記事です。本稿では、[App Service プラン](https://docs.microsoft.com/ja-jp/azure/app-service/overview-hosting-plans) 再起動時の挙動を詳しく見ていきます。
App Service は、Web アプリケーションを動作させることができるプラットフォームであり、App Service プランは App Service をホストしている仮想マシンです。今回の記事では App Service の使用方法には触れていませんので、使用方法の詳細は [App Service の概要](https://docs.microsoft.com/ja-jp/azure/app-service/overview) よりご確認ください。

# 概要
App Service には、Windows ベースの環境、Linux ベースの環境、そして、カスタムコンテナ－があります。本稿では、**Windows ベースの App Service** を扱います。

前提として Windows ベースの App Service にアプリケーションを 1 つデプロイすると、App Service プラン内に 2 つの IIS プロセスが起動します。これは、Azure ポータルの App Service - "プロセス エクスプローラ" から確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104140/5998705a-20f9-4827-55f3-eabbdd8e0100.png)
上記では、"インスタンス ID: 0e360a" が App Service プランのインスタンスID (頭 6 桁) を示しており、"PID 19752" が App Service プラン内に起動している Web アプリケーションの IIS プロセス、そして "PID 12952" が [KUDU](https://github.com/projectkudu/kudu/wiki) の IIS プロセスとなります。


App Service の再起動には、App Service プラン内の IIS プロセスを再起動するものと App Service プランを再起動するものの 2 種類があります。
具体的にいくつか例を挙げると...
１．IIS プロセスを再起動するもの
　(1) Azure ポータルからの再起動
　(2) Advanced Application restart
　(3) Azure ポータルからの "アプリケーション設定" の更新
　(4) web.config の更新
　(5) Proactive Auto-Heal
　(6) Custom Auto-Heal
　(7) 正常性チェック パス

２．App Service プランを再起動するもの
　(1) App Service プランの再起動 ( [REST API](https://docs.microsoft.com/en-us/rest/api/appservice/appserviceplans/rebootworker) ) 
　(2) Azure プラットフォームのメンテナンス

"１．IIS プロセスを再起動するもの" の操作では、App Service プラン内の IIS プロセスが再起動されるため、実行することで、IIS のプロセスのプロセス ID が変更されることを確認できます。"２．App Service プランを再起動するもの" については、IIS プロセスよりも下位レイヤーとなる App Service プランの再起動となり、１よりも影響範囲が大きくなります。
本稿では、REST API を使用して App Service プランを再起動させ、再起動時の挙動がどのようになるのか詳細を見ていきます。

なお、App Service は、管理と高スケーラビリティ確保のため、単一の仮想マシンではなく、内部的には複数の仮想マシンから構成されています。このため、内部のそれぞれの仮想マシンに対してメンテナンスが行われていますが、メンテナンスの内容によっては、App Service プランが再起動されないものもあります。メンテナンスの内容については、[Azure App Service での OS とランタイムのパッチ適用](https://docs.microsoft.com/ja-jp/azure/app-service/overview-patch-os-runtime) に記載されているようなことが行われます。


# 検証
App Service にシンプルな Web アプリケーションをデプロイし、クライアント (JMeter) からアクセス負荷をかけ続けた状態で、App Service プランを再起動させ、再起動時の挙動を観測します。

## 事前準備
アプリケーションの実装やその他の影響を受けないよう新規に Web アプリ、App Service プラン を作成します。
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104140/4ec507ea-1b80-b622-5eea-d0715d04e680.png)
また、振り分けを均等にするために "構成" - "全般設定" から "ARR アフィニティ" を "オフ" にします。
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104140/09cdfd45-77e7-b85d-e04a-33548614d900.png)
そして、Visual Studio のテンプレートから作成した Web アプリケーションから Application Insights のサンプリングを無効化したものをデプロイします。(規定では Application Insights のサンプリングが有効化されているため、全てのログが Application Insights に蓄積されないようになっています。) 
具体的には、[ASP.NET Web サイトに Application Insights を構成する](https://docs.microsoft.com/ja-jp/azure/azure-monitor/app/asp-net#create-a-basic-aspnet-web-app) の手順に沿って作成したアプリケーションを使用しています。私が検証に使用したプログラムは [コチラ](https://github.com/makokui/ASPNET48-SampleWebApplication) です。
アプリケーションをデプロイした App Service にアクセスすると、以下の画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104140/1255f04a-f590-1a0e-4c5e-af34a6243868.png)

検証の前に、JMeter から App Service に数分間負荷をかけて、Application Insights のサンプリングが無効化されているかを確認します。

●負荷をかけた際の 1 秒間毎に集計したリクエスト数のグラフ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104140/2d00873a-83d6-e787-5bef-f74fe17ceabc.png)

●このときの Application Insights のサンプリング レート
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104140/c49fdfcb-d122-c820-3c53-ee87952aa152.png)
RetainedPercentage が 100 なので、[サンプリングが動作しているかどうかを把握する](https://docs.microsoft.com/ja-jp/azure/azure-monitor/app/sampling#knowing-whether-sampling-is-in-operation) より、サンプリングが動作していないことが確認できました。


## 検証実施
では、JMeter から負荷をかけ続けた状態で、App Service プランを再起動してみます。

再起動には [REST API](https://docs.microsoft.com/en-us/rest/api/appservice/appserviceplans/rebootworker) を使用しますが、パラメーターに、App Service プラン名、リソースグループ名、サブスクリプション ID、ワーカー名が必要となります。App Service プラン名、リソースグループ名、サブスクリプション ID は、Azure ポータルから App Service プランの "プロパティ" - "リソースID" より確認することができます。
リソース ID 例：
　/subscriptions/{サブスクリプション ID}/resourceGroups/{リソースグループ名}
/providers/Microsoft.Web/serverfarms/{App Service プラン名}

ワーカー名については、いくつか確認方法がありますが、Azure ポータルの App Service - "高度なツール" から Kudu サイトを開き、Environment の System Info より取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104140/7781a9c7-4ba6-93de-0e46-36da95f08ca8.png)
"System info" にある "Machine name" がワーカー名です。
補足になりますが、"Short instance id" が先で確認した Azure ポータルの App Service - "プロセス エクスプローラ" に表示される "インスタンス ID" (頭 6 桁) となり、"instance id" が完全な "インスタンス ID" になります。

これらのパラメータ―を使用して、[REST API](https://docs.microsoft.com/en-us/rest/api/appservice/appserviceplans/rebootworker) の "Try it" から、App Service プランを再起動をします。
REST API の実行例：
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104140/354ec988-6f7c-f1be-6904-ef0f65f0b5e5.png)
Response Code に 200 が返却されていれば正常に実行されています。


App Service プランの再起動後、先ほどと同じように Application Insights からリクエスト状況を確認します。今回は、App Service プランのインスタンスごとのレスポンスコード、応答時間を含めてグラフ化します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104140/e3e12c87-7df0-b61b-0ab3-1c0f3447c8b7.png)
グラフからは同じインスタンスが再起動しているのではなく、再起動によって別のインスタンスが起動していることがわかります。"RD0003FF753898" が再起動前のインスタンス、"RD0003FF47D77F" が再起動実行後に起動したインスタンスのようです。
また、クライアントからのリクエストはすべて 200 応答で返却されており、わずかに応答に 1 秒以上要したリクエストがあるものの、ほぼすべてのリクエストは 250 ミリ秒以内に返却されています。

Application Insights のログからもう少し詳しく見てみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104140/acad338b-bd0f-7ca2-f71b-0d3913361f1c.png)
再起動前のインスタンス (RD0003FF753898) が最後にリクエストを返却した時刻は、9 時 36 分 51.565 秒。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104140/32953d13-1641-d531-a29a-ab0bb0df56bb.png)
再起動後のインスタンス (RD0003FF47D77F) が最初にリクエストを返却した時刻は、9 時 36 分 21.457 秒。
url が "http://localhost" のリクエストは起動時に内部的に発行されるものであるため、除外します。



しつこいですが、念のため、サンプリングが行われていないことも確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104140/ef7c400a-508b-c375-c5f1-62c56b8e1c14.png)
RetainedPercentage が 100 なので、ログの欠損はありません。


別の観点から App Service のメトリックから状態を確認します。
App Service プランのインスタンスが変更されているので、"分割を適用する" よりインスタンス毎に集計します。

● 1 分間単位に集計したインスタンス別リクエスト数のメトリックグラフ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104140/946b6a89-5167-beb1-331f-aab5a9b256c9.png)
1 分毎に丸められていますが、先ほどの Application Insights とほぼ同等の傾向を確認できます。

● 1 分間単位に集計したインスタンス別平均 CPU のメトリックグラフ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104140/91496900-707d-79a1-516c-807d1d267299.png)
再起動前のインスタンスの CPU メトリックは 9 時 36 分までとなり、再起動後のインスタンスの CPU メトリックは 9 時 37 分から記録され始めています。再起動後のインスタンスが最初にリクエストを返却した時刻が 9 時 36 分 18.801 秒なので、実際にはもう少しは早くインスタンスが起動しているはずです。
しかしながら、[こちらの図](https://docs.microsoft.com/ja-jp/azure/azure-monitor/platform/data-platform-metrics#what-can-you-do-with-azure-monitor-metrics) にあるようにインスタンス内のエージェントが Metric DB に対してメトリックデータを送信しており、Azure ポータルは Metric DB 内に蓄積されたメトリックデータを参照する仕組みとなっているため、一部のメトリックに関してはインスタンス起動後やや遅れてから収集されているように見えます。


#最後に
App Service プランが再起動された場合にも再起動前に新しいインスタンスが起動することで、システムを停止することなく、クライアントからのリクエストが振り分けられる挙動になっています。

しかしながら、今回の検証で使用した Web アプリケーションは非常にシンプルな ASP.NET アプリケーションであり、アプリケーションの起動やクライアントからのリクエスト応答にほとんど時間がかからない仕組みであるため、Java などの起動に時間のかかるアプリケーションや、クライアントからの応答に非常に時間のかかるアプリケーションの場合には事前に検証されることをお勧めします。
また、[Azure の冗長性のタスクを完了する](https://docs.microsoft.com/ja-jp/azure/architecture/reliability/architect#complete-azure-redundancy-tasks) に記載されているように App Service プランのインスタンス 1台では冗長性要件を満たしておらず、シングルポイントになり得ます。インスタンスが 1 台であってもシステムの安定稼働を保証するものではありません。
記事の内容は投稿時点のものであり、将来的に機能変更される可能性があります。


#参考
・[Jmeter のインストールから負荷テストまで](https://qiita.com/shotets/items/d553d7be0d407a9a9a53)
・[Kusto の概要](https://docs.microsoft.com/ja-jp/azure/data-explorer/kusto/concepts/)