首页 文章

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

提问于
浏览
18

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

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

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

我可以看到任何记录,以找出他们都重新启动的原因吗?或者,这只是App Service不时做的正常事情吗?

1 回答

  • 27

    因此,似乎答案就是“不,你真的不知道为什么,你可以推断它确实存在 . ”

    我的意思是,您可以添加一些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'." ,但它并没有在主机级别上进行't really tell you what' .

    我需要接受的是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的日记中的众多条目之一 - 事实证明,当很少有重新启动的传统VM上运行某些东西时,你可以逃脱很多事情,但它需要一些TLC来解决这个问题 . 在 Cloud 中迁移到我们勇敢的新世界时扭结....

    UPDATE 10/27/2017: 自撰写本文以来,Azure已在"Diagnose and solve problems"下添加了一个新工具 . 单击"Web App Restarted",它会告诉您原因,通常是因为存储延迟或基础架构升级 . 上面的内容仍然存在,因为当转移到Azure App Service时,最好的方法是你真的只需要让你的应用程序适应随机重启 .

    UPDATE 2/11/2018: 将几个遗留系统迁移到中型应用服务计划的单个实例(具有大量CPU和内存开销)之后,我遇到了一个令人烦恼的问题,即我从暂存插槽部署无缝,但每当我被启动到由于Azure基础架构维护的新主机,一切都会因停机2-3分钟而失控 . 我正在疯狂地试图弄清楚为什么会发生这种情况,因为App Service应该等到它从你的应用程序收到成功的响应,然后才能启动你到新的主机 .

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

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

    要检查的第一件事是你实际上在 Application_Start 中做了真正的工作 . 例如,我正在使用NHibernate,虽然在很多方面很擅长加载它的配置,所以我确保在 Application_Start 期间实际创建 SessionFactory 以确保完成了艰苦的工作 .

    如上所述,要检查的第二件事是你要进行热身检查 . 如上所述,您可以从重写规则中排除热身检查 . 或者,在我最初编写该工作的时候,App Service添加了一个HTTPS Only标志,允许您在负载均衡器而不是web.config文件中执行HTTPS重定向 . 既然它必须考虑它,所以我建议使用HTTPS Only标志作为前进的方法 .

    要考虑的第三件事是你是否正在使用App Service Local Cache Option . 简而言之,这是一个选项,其中App Service将复制运行的应用程序's files to the local storage of the instances that it'而不是网络共享,如果您的应用程序不关心它是否会丢失写入本地文件系统的更改,那么这是一个很好的选择 . 它可以加快I / O性能(这很重要,因为请记住,App Service runs on potatoes)并消除由网络共享上的任何维护引起的重新启动 . 但是,那里当本地缓存准备就绪时,关于App Service 's infrastructure upgrades that is poorly documented and you need to be aware of. Specifically, the Local Cache option is initiated in the background in a separate app domain after the first request, and then you'切换到app域是一个特殊的微妙之处 . 这意味着App Service将针对您的站点发出预热请求,获得成功响应,为该实例指定流量,但是(哎呀!)现在Local Cache正在后台研磨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一次您的网站,以确保它将其旋转下来 . 令人费解的是,this isn't a sticky setting,所以你可能已经在你的制作和舞台插槽上打开了 AlwaysOn 所以你不知道发生了什么:假设你在一个实例上托管了7个站点,每个站点都有自己的临时插槽,所有启用了 AlwaysOn 的东西 . App Service会对您的7个 生产环境 插槽执行预热和应用程序初始化,并在重定向流量之前尽职地等待它们成功响应 . But it doesn't do this for the staging slots. 因此它将流量引导到新实例,但随后在分段插槽中1-2分钟后启动 AlwaysOn ,所以现在又有7个站点同时启动 . 请记住,App Service runs on potatoes,因此所有这些额外的I / O同时发生会破坏您的 生产环境 槽的性能,并将被视为停机时间 .

    解决方案是在暂存插槽上保持 AlwaysOn 关闭,以便在基础结构更新后不会被这种同时发生的I / O狂热所困扰 . 如果您通过PowerShell使用交换脚本,维护此"Off in staging, On in production"令人惊讶的冗长:

    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确实很不错 . 但在实践中实现比营销材料可能提出的要困难一些 . 希望这有助于某人 .

相关问题