azure - 是否可以确定为什么重新启动Azure App Service?

标签 azure azure-web-app-service

我有一堆网站都在Azure App Service的单个实例上运行,并且它们都设置为Always On。它们突然都在同一时间重新启动,由于一切都达到了冷的要求,因此一切都变慢了几分钟。

如果该服务将我转移到了新主机上,我会期望这样做,但是那没有发生-我仍然使用相同的主机名。

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

我可以在任何地方看到任何日志记录,以弄清它们为什么都重新启动了?还是这只是App Service不时做的正常事情?

最佳答案

因此,答案似乎是“不,您真的不知道为什么,您可以推断出确实如此。”
我的意思是,您可以添加一些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。这不适用于“应用程序初始化”模块:它将请求发送到根,所有请求都从URL Rewrite模块获得301重定向,而ASP.NET管道根本没有命中,辛苦了。实际完成了。然后,App Service / IIS认为工作进程已准备就绪,然后向其发送流量。但是第一个“真实”请求实际上是在301重定向到HTTPS URL之后发出的,这真是糟糕!用户遇到了冷启动的痛苦。
I added a rewrite rule described here使应用程序初始化模块免于需要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来解决迁移到云中我们勇敢的新世界时会扭结……。
-
更新2017年10月27日:自撰写本文以来,Azure已在“诊断和解决问题”下添加了一个新工具。单击“重新启动Web App”,它会告诉您原因,通常是由于存储延迟或基础架构升级所致。上面的内容仍然存在,因为当转移到Azure App Service时,最好的方法是,您实际上只需要诱使您的应用程序适应随机重启。
-
更新2/11/2018:在将多个旧系统迁移到中型App Service Plan的单个实例(具有大量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传入文件并欺骗它,并且看到更改反射(reflect)在生产中,那么不要使用此选项:
     <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使用交换脚本,那么执行此操作“在登台中关闭,在生产中打开”是非常冗长的操作:
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确实很棒。但是在实践中要比营销 Material 所建议的要困难一些。希望这对某人有帮助。
-
更新2020年7月17日:在上面的内容中,我谈到如果您正在使用暂存插槽,则需要对“AlwaysOn”进行欺骗,因为它将与插槽交换,并且在所有插槽上使用它可能会导致性能问题。在我不清楚的某个位置they seem to have fixed this so that "AlwaysOn" isn't swapped。我的脚本实际上仍在使用AlwaysOn进行轻描淡写,但实际上,它现在最终成为空手。因此,建议始终将AlwaysOn保留为暂存插槽的建议仍然存在,但是您不必再在脚本中做些麻烦了。

关于azure - 是否可以确定为什么重新启动Azure App Service?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45021644/

相关文章:

azure - 使用自定义 nuget.config 的 webapp 的 nuget 包恢复失败

azure - 将子域映射到虚拟目录 Azure WebApps

asp.net - 如何调试 Azure 500 内部服务器错误

c# - 如何使用 C# 将多重身份验证登录到 Dynamics 365

azure - JWT 响应中缺少刷新 token

c# - 如何为移动应用程序托管隐私政策文档网页

azure - 同一应用程序服务计划中的应用程序之间的通信?

Azure 请求的资源上不存在 'Access-Control-Allow-Origin' header

flutter - 命令行脚本中的 Azure Pipeline flutter 命令

azure - 我可以从 Azure 网站调用 Azure Webjob 并向其传递参数吗?