Azure App Serviceの単一のインスタンスで実行しているWebサイトが多数あり、それらはすべてAlways Onに設定されています。それらはすべて突然同時に再起動し、すべてがコールドリクエストにヒットすると、数分間すべてが遅くなります。
サービスによって新しいホストに移動された場合、これを期待しますが、それは起こりませんでした-私はまだ同じホスト名にいます。
再起動時のCPUとメモリの使用量は正常であり、展開などを開始しませんでした。再起動の明確な理由がわかりません。
なぜそれらがすべて再起動したかを把握できるログがどこかにありますか?または、これはApp Serviceが時々行う通常のことですか?
だから、これに対する答えは「いいえ、あなたは本当に知ることができませんなぜ、あなたはそれを推測することができますdid。」
つまり、Application Insightsのログを次のように追加できます
private void Application_End()
{
log.Warn($"The application is shutting down because of '{HostingEnvironment.ShutdownReason}'.");
TelemetryConfiguration.Active.TelemetryChannel.Flush();
// Server Channel flush is async, wait a little while and hope for the best
Thread.Sleep(TimeSpan.FromSeconds(2));
}
最終的には"The application is shutting down because of 'ConfigurationChange'."
または"The application is shutting down because of 'HostingEnvironment'."
になりますが、実際にはホストレベルで何が起こっているかはわかりません。
私が受け入れる必要があるのは、App Serviceが時々物事を再開し、なぜ私が気にかけたのかを自問することです。 App Serviceは、アプリケーションプールがウォームアップされるのを待ってからリクエストを送信するのに十分なほどスマートであると想定されています(重複したリサイクルなど)。それでも、私のアプリはリサイクル後1〜2分間CPUを処理します。
理解するのに時間がかかりましたが、犯人は、すべてのアプリにHTTPからHTTPSにリダイレクトする書き換えルールがあることです。これは、アプリケーション初期化モジュールでは機能しません。ルートにリクエストを送信し、URL書き換えモジュールから301リダイレクトを取得し、ASP.NETパイプラインがまったくヒットしません。実際に完了しました。 App Service/IISは、ワーカープロセスの準備が整ったと判断し、トラフィックを送信します。しかし、実際の最初の「実際の」リクエストは、HTTPS URLへの301リダイレクトに続いており、bam!そのユーザーはコールドスタートの苦痛を経験します。
ここで説明する書き換えルールを追加しました HTTPSを必要としないようにアプリケーション初期化モジュールを除外するため、サイトのルートに到達すると、実際にページの読み込み、したがってパイプライン全体をトリガーします。
<rewrite>
<rules>
<clear />
<rule name="Do not force HTTPS for application initialization" enabled="true" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTP_Host}" pattern="localhost" />
<add input="{HTTP_USER_AGENT}" pattern="Initialization" />
</conditions>
<action type="Rewrite" url="{URL}" />
</rule>
<rule name="Force HTTPS" enabled="true" stopProcessing="true">
<match url="(.*)" ignoreCase="false" />
<conditions>
<add input="{HTTPS}" pattern="off" />
</conditions>
<action type="Redirect" url="https://{HTTP_Host}/{R:1}" appendQueryString="true" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
これは、古いアプリをAzureに移動する日記の多くのエントリの1つです。従来のVMがほとんど再起動しないが、クラウドで私たちの勇敢な新しい世界に移行するときに、不具合を解決するためにTLCが必要になります。
-
UPDATE 10/27/2017:この執筆以来、Azureは「問題の診断と解決」の下に新しいツールを追加しました。 [Webアプリの再起動]をクリックすると、通常、ストレージの遅延またはインフラストラクチャのアップグレードが原因で理由が表示されます。ただし、Azure App Serviceに移行する場合の最善の方法は、アプリをランダムに再起動できるようにすることです。
-
UPDATE 2/11/2018:複数のレガシーシステムを中程度のApp Service Planの単一インスタンスに移行した後(十分なCPUとメモリのオーバーヘッドを伴う)、ステージングスロットからの展開がシームレスに行われるという厄介な問題がありましたが、Azureインフラストラクチャのメンテナンスのために新しいホストから起動されるたびに、2〜3分のダウンタイムですべてが混乱します。 App Serviceは、新しいホストで起動する前に、アプリから成功した応答を受信するまで待機することになっているので、なぜこれが起こっているのかを理解しようと必死でした。
これに非常にイライラして、App Serviceをエンタープライズガベージとして分類し、IaaS仮想マシンに戻る準備ができました。
それは複数の問題であることが判明し、他の人が自分の非常にレガシーなASP.NETアプリをApp Serviceに移植しているときに出くわすと思うので、ここですべてを実行すると思いました。
最初に確認することは、Application_Start
で実際の作業を実際に行っていることです。たとえば、私はNHibernateを使用していますが、これは多くのことが得意ですが、設定を読み込むのに非常に適していますので、Application_Start
の間にSessionFactory
を実際に作成して、仕事は終わりました。
前述したように、確認する2番目のことは、App Serviceのウォームアップチェックを妨げるSSLの書き換えルールがないことです。上記のように、書き換えルールからウォームアップチェックを除外できます。または、回避策を最初に書いて以来、App Serviceに HTTPSのみ フラグが追加され、web.configファイル内ではなくロードバランサーでHTTPSリダイレクトを実行できるようになりました。アプリケーションコードの上の間接層で処理されるため、考慮する必要はありません。そのため、HTTPS Onlyフラグを使用することをお勧めします。
3番目に考慮すべきことは、 App Service Local Cache Option を使用しているかどうかです。簡単に言えば、これはApp Serviceがアプリのファイルをネットワーク共有からではなく実行中のインスタンスのローカルストレージにコピーするオプションであり、アプリが気にしない場合に有効にする素晴らしいオプションですローカルファイルシステムに書き込まれた変更を失います。 I/Oパフォーマンスを高速化し(重要なことは、 App Serviceがジャガイモで実行される であるため重要です)、ネットワーク共有のメンテナンスによって引き起こされる再起動を排除します。ただし、App Serviceのインフラストラクチャのアップグレードに関しては、文書化が不十分であるため、注意が必要な特定の微妙な点があります。具体的には、ローカルキャッシュオプションは、最初の要求後に別のアプリドメインのバックグラウンドで開始され、ローカルキャッシュの準備ができたらアプリドメインに切り替えられます。つまり、App Serviceはサイトに対してウォームアップリクエストをヒットし、成功した応答を取得し、そのインスタンスへのトラフィックをポイントしますが、ローカルキャッシュはバックグラウンドでI/Oを粉砕しています。この場合、App ServiceのI/Oは恐ろしいため、停止することになります。これが発生していることがわからない場合、アプリが同じインスタンスで2回起動しているように見えるため、ログで不気味に見えます(それが原因です)。解決策は、この Jet blog post に従い、ローカルキャッシュの準備ができたときに通知する環境変数を監視するアプリケーション初期化ウォームアップページを作成することです。これにより、ローカルキャッシュが完全に準備されるまで、App Serviceに新しいインスタンスの起動を遅らせることができます。これも、データベースと通信できるようにするために使用するものです。
public class WarmupHandler : IHttpHandler
{
public bool IsReusable
{
get
{
return false;
}
}
public ISession Session
{
get;
set;
}
public void ProcessRequest(HttpContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
var request = context.Request;
var response = context.Response;
var localCacheVariable = Environment.GetEnvironmentVariable("WEBSITE_LOCAL_CACHE_OPTION");
var localCacheReadyVariable = Environment.GetEnvironmentVariable("WEBSITE_LOCALCACHE_READY");
var databaseReady = true;
try
{
using (var transaction = this.Session.BeginTransaction())
{
var query = this.Session.QueryOver<User>()
.Take(1)
.SingleOrDefault<User>();
transaction.Commit();
}
}
catch
{
databaseReady = false;
}
var result = new
{
databaseReady,
machineName = Environment.MachineName,
localCacheEnabled = "Always".Equals(localCacheVariable, StringComparison.OrdinalIgnoreCase),
localCacheReady = "True".Equals(localCacheReadyVariable, StringComparison.OrdinalIgnoreCase),
};
response.ContentType = "application/json";
var warm = result.databaseReady && (!result.localCacheEnabled || result.localCacheReady);
response.StatusCode = warm ? (int)HttpStatusCode.OK : (int)HttpStatusCode.ServiceUnavailable;
var serializer = new JsonSerializer();
serializer.Serialize(response.Output, result);
}
}
また、ルートをマップし、web.config
にアプリケーションの初期化を追加することを忘れないでください:
<applicationInitialization doAppInitAfterRestart="true">
<add initializationPage="/warmup" />
</applicationInitialization>
4番目に考慮すべきことは、App Serviceが一見ゴミのような理由でアプリを再起動する場合があることです。 fcnMode
プロパティをDisabled
に設定すると役立つようです。誰かがサーバー上の設定ファイルやコードを操作している場合、ランタイムがアプリを再起動するのを防ぎます。ステージングスロットを使用して、そのように展開を行う場合、これは煩わしいものではありません。ただし、ファイルをFTPで入力して操作し、その変更が本番環境に反映されることが予想される場合は、このオプションを使用しないでください。
<httpRuntime fcnMode="Disabled" targetFramework="4.5" />
5番目の考慮事項は、そしてこれは主にずっと私の問題でした、AlwaysOn
オプションを有効にしてステージングスロットを使用しているかどうかです。 AlwaysOn
オプションは、1分ごとにサイトにpingを送信して、IISがスピンダウンしないように暖かいことを確認します。不可解なことに、 これはありません '固定設定 なので、実稼働スロットとステージングスロットの両方でAlwaysOn
を有効にしているため、毎回それをいじる必要はありません。これにより、App Serviceインフラストラクチャのアップグレードで問題が発生します。新しいホストで起動します。インスタンスでホストされている7つのサイトがあり、それぞれに独自のステージングスロットがあり、すべてがAlwaysOn
が有効になっているとします。AppServiceは、 7つのプロダクションスロットがあり、それらが正常に応答するのを忠実に待ってからトラフィックをリダイレクトしますしかし、ステージングスロットに対してはこれを行いません。新しいインスタンスに移行しますが、ステージングスロットでAlwaysOn
が1〜2分後にキックされるので、同時に7つのサイトが同時に起動します。覚えておいてください App Serviceはじゃがいも 、だからこの追加のI/Oが同時に発生すると、実稼働スロットのパフォーマンスが破壊され、ダウンタイムとして認識されます。
解決策は、ステージングスロットでAlwaysOn
をオフにしておくことです。これにより、インフラストラクチャの更新後にこの同時I/O狂乱に悩まされることはありません。 PowerShell経由でスワップスクリプトを使用している場合、この「ステージングではオフ、実稼働ではオン」を維持するのは驚くほど冗長です。
Login-AzureRmAccount -SubscriptionId {{ YOUR_SUBSCRIPTION_ID }}
$resourceGroupName = "YOUR-RESOURCE-GROUP"
$appName = "YOUR-APP-NAME"
$slotName = "YOUR-SLOT-NAME-FOR-EXAMPLE-STAGING"
$props = @{ siteConfig = @{ alwaysOn = $true; } }
Set-AzureRmResource `
-PropertyObject $props `
-ResourceType "Microsoft.web/sites/slots" `
-ResourceGroupName $resourceGroupName `
-ResourceName "$appName/$slotName" `
-ApiVersion 2015-08-01 `
-Force
Swap-AzureRmWebAppSlot `
-SourceSlotName $slotName `
-ResourceGroupName $resourceGroupName `
-Name $appName
$props = @{ siteConfig = @{ alwaysOn = $false; } }
Set-AzureRmResource `
-PropertyObject $props `
-ResourceType "Microsoft.web/sites/slots" `
-ResourceGroupName $resourceGroupName `
-ResourceName "$appName/$slotName" `
-ApiVersion 2015-08-01 `
-Force
このスクリプトはステージングスロットをAlwaysOn
をオンにするように設定し、スワップを実行してステージングが本番になるようにし、ステージングスロットをAlwaysOn
をオフにするように設定します。インフラストラクチャのアップグレード後にアップします。
これが機能するようになったら、セキュリティの更新とハードウェア障害を処理するPaaSを用意しておくのは本当に素晴らしいことです。しかし、マーケティング資料が示唆するよりも実際に達成するのは少し難しいです。これが誰かを助けることを願っています。