有没有办法确定Azure App Service重新启动的原因?

时间:2017-07-10 21:10:39

标签: azure azure-web-sites azure-web-app-service

我有一堆网站在Azure App Service的单个实例上运行,并且它们都设置为Always On。它们都突然重新启动,导致一切都变慢了几分钟,因为一切都发出了冷的请求。

如果服务将我转移到新主机上,我会期待这一点,但这没有发生 - 我仍然使用相同的主机名。

重启时CPU和内存使用情况正常,我没有启动任何部署或类似的事情。我没有看到重启的明显原因。

在任何地方我都可以看到为什么它们全部重新启动?或者,这只是App Service不时做的正常事情吗?

2 个答案:

答案 0 :(得分:34)

所以,似乎答案就是这样;不,你不能真正知道为什么,你可以推断它做了。 "

我的意思是,您可以添加一些像

这样的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应该足够聪明,等待应用程序池在向其发送请求之前进行预热(如重叠的回收)。然而,我的应用程序在回收后会在CPU处理1-2分钟。

我花了一段时间才弄明白,但罪魁祸首是我的所有应用都有重写规则从HTTP重定向到HTTPS。这不适用于Application Initialization模块:它向root发送请求,并且它从URL Rewrite模块获得301重定向,并且ASP.NET管道根本没有被击中,这是一项艰苦的工作实际上并没有完成。 App Service / IIS然后认为工作进程已准备就绪,然后向其发送流量。但第一个"真实"请求实际上遵循301重定向到HTTPS URL,并且bam!那个用户遇到了冷启动的痛苦。

I added a rewrite rule described here免除Application Initialization模块需要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日记中的众多条目之一 - 事实证明,当传统虚拟机上运行某些东西时,您可以逃脱很多事情。很少重启,但在迁移到云中我们勇敢的新世界时,它需要一些TLC来解决问题....

-

更新10/27/2017 :自撰写本文以来,Azure已在&#34;诊断和解决问题&#34;下添加了一个新工具。单击&#34; Web App重新启动&#34;,它会告诉您原因,通常是因为存储延迟或基础架构升级。上面的内容仍然存在,因为当转移到Azure App Service时,最好的方法是你真的只需要让你的应用程序适应随机重启。

-

更新2/11/2018 :将多个旧系统迁移到中型应用服务计划的单个实例(具有大量CPU和内存开销)后,我遇到了一个棘手的问题来自暂存插槽的部署可以无缝进行,但是每当我因为Azure基础架构维护而被引导到新主机时,一切都会因停机2-3分钟而失控。我正在疯狂地试图弄清楚为什么会发生这种情况,因为App Service应该等到它从你的应用程序收到成功的响应,然后才能启动你到新的主机。

我对此感到非常沮丧,因此我准备将App Service归类为企业垃圾并返回IaaS虚拟机。

事实证明这是多个问题,我怀疑其他人会在将他们自己的野兽遗留的ASP.NET应用程序移植到App Service时遇到它们,所以我想我会在这里完成它们。

要检查的第一件事是,您实际上正在Application_Start中进行实际工作。例如,我使用的是NHibernate,虽然在很多方面很擅长加载其配置,所以我确保在SessionFactory期间实际创建Application_Start以确保努力工作。

如上所述,要检查的第二件事是您没有SSL的重写规则干扰App Service的热身检查。如上所述,您可以从重写规则中排除预热检查。或者,在我最初编写该工作的时候,App Service添加了一个HTTPS Only标志,允许您在负载均衡器而不是web.config文件中进行HTTPS重定向。由于它是在应用程序代码之上的间接层处理的,因此您不必考虑它,因此我建议使用HTTPS Only标记作为方法。

要考虑的第三件事是你是否正在使用App Service Local Cache Option。简而言之,这是App Service将您的应用程序文件复制到其运行的实例的本地存储而不是网络共享的选项,并且是一个很好的选项,如果如果您的应用程序丢失了写入本地文件系统的更改,那么您的应用程序并不在意。它可以加快I / O性能(这很重要,因为请记住App Service runs on potatoes)并消除由网络共享上的任何维护引起的重新启动。但是,对于App Service的基础架构升级存在一个特定的微妙之处,这些升级的文档很少,您需要注意。具体来说,本地缓存选项在第一个请求之后在单独的应用程序域中在后台启动,然后在本地缓存准备就绪时切换到应用程序域。这意味着App Service将针对您的站点发出预热请求,获得成功响应,将流量指向该实例,但是(哎呀!)现在Local Cache正在后台研磨I / O,如果您有很多站点在这个例子中,你已经停止了,因为App Service I / O是可怕的。如果你不知道这种情况正在发生,它在日志中看起来很怪异,因为它好像你的应用程序在同一个实例上启动了两次(因为它是)。解决方案是遵循此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>

要考虑的第四件事是,有时App Service会因为看似垃圾的原因重新启动你的应用程序。似乎将fcnMode属性设置为Disabled可能有所帮助;如果有人在服务器上处理配置文件或代码,它会阻止运行时重新启动应用程序。如果您正在使用转储插槽并以这种方式进行部署,那么这不应该让您感到烦恼。但是,如果您希望能够在文件中使用FTP并对文件进行处理并看到生产中反映的更改,那么请不要使用此选项:

     <httpRuntime fcnMode="Disabled" targetFramework="4.5" />

要考虑的第五件事,,这主要是我的问题,是否使用启用了AlwaysOn选项的临时插槽。 AlwaysOn选项的工作方式是每隔一分钟左右ping一次您的网站,以确保它的温暖,以便IIS不会将其分解。令人费解的是this isn't a sticky setting,所以你可能已经在你的制作和舞台插槽上打开AlwaysOn,所以你不必每次都搞乱它。这会在将您引导至新主机时导致App Service基础架构升级出现问题。这里发生了什么:让我们假设您在一个实例上托管了7个站点,每个站点都有自己的临时插槽,启用了AlwaysOn的所有内容。 App Service会对您的7个生产插槽执行预热和应用程序初始化,并在重定向流量之前尽职地等待它们成功响应。 但它不会为暂存插槽执行此操作。因此它将流量引导到新实例,但随后AlwaysOn在暂存时段后1-2分钟启动,所以现在你又有7个网站同时启动了。请记住,App Service runs on potatoes,因此所有这些额外的I / O同时发生会破坏您的生产槽的性能,并将被视为停机时间。

解决方案是在您的暂存插槽上保持AlwaysOn关闭,这样您就不会在基础架构更新后被这种同时发生的I / O狂热所困扰。如果您通过PowerShell使用交换脚本,请在暂存中保持这个&#34;关闭,在生产中保持&#34;令人惊讶的是,这是令人惊讶的:

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确实很不错。但在实践中实现的难度比营销材料所暗示的要难得多。希望这有助于某人。

答案 1 :(得分:0)

如果您的服务因 OutOfMemoryExceptions 而重新启动,Application_End 可能因应用程序崩溃而无法运行。

我们将 ASP.NET 4.8 MVC 5 应用程序移至 Azure 应用程序服务(带有 Windows 容器),并且在上线后面临 OOM。应用程序崩溃非常严重,以至于 Application_End 事件无法记录任何消息。我们确实收到了 AppInsights 能够在重启前分派的间歇性 OOME。

我们的工程师一直在寻找增加网站的内存(因为我们在以前的环境中确实使用了很多),但找不到任何可用的参考。我们终于被微软支持人员救了,他们建议使用这个应用程序设置(在配置下添加)来增加内存:

WEBSITE_MEMORY_LIMIT_MB = 3072

他们将此引用添加到 Azure 文档中: https://github.com/MicrosoftDocs/azure-docs/issues/13263#issuecomment-655051828

现在我们的应用程序正在愉快地运行,在高峰时间提交大约 4200M。我的服务计划有32G,有2个app服务,一共5个槽位,其中一个配置使用5120M。仍有大约 40% 的内存可用于启动暂存槽。