web-dev-qa-db-fra.com

Existe-t-il un moyen de déterminer pourquoi Azure App Service a redémarré?

J'ai un tas de sites Web exécutés sur une seule instance d'Azure App Service, et ils sont tous définis sur Always On. Ils ont tous soudainement redémarré en même temps, ce qui a ralenti tout pendant quelques minutes alors que tout a frappé une demande froide.

Je m'attendrais à cela si le service m'avait déplacé vers un nouvel hôte, mais cela ne s'est pas produit - je suis toujours sur le même nom d'hôte.

L'utilisation du CPU et de la mémoire était normale au moment du redémarrage, et je n'ai initié aucun déploiement ou quelque chose du genre. Je ne vois pas de raison évidente pour le redémarrage.

Y a-t-il une journalisation n'importe où que je peux voir pour comprendre pourquoi ils ont tous redémarré? Ou est-ce juste une chose normale qu'App Service fait de temps en temps?

21
Nicholas Piasecki

Donc, il semble que la réponse à cette question soit "non, vous ne pouvez pas vraiment savoir pourquoi, vous pouvez simplement en déduire qu'il l'a fait."

Je veux dire, vous pouvez ajouter une journalisation Application Insights comme

    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)); 
    }

et vous vous retrouverez avec "The application is shutting down because of 'ConfigurationChange'." ou "The application is shutting down because of 'HostingEnvironment'.", mais cela ne vous dit pas vraiment ce qui se passe au niveau de l'hôte.

Ce que je devais accepter, c'est qu'App Service va redémarrer les choses de temps en temps, et me demander pourquoi je m'en souciais. App Service est censé être suffisamment intelligent pour attendre que le pool d'applications soit réchauffé avant de lui envoyer des demandes (comme le recyclage qui se chevauchent). Pourtant, mes applications resteraient là à croquer le processeur pendant 1-2 minutes après un recyclage.

Il m'a fallu un certain temps pour comprendre, mais le coupable était que toutes mes applications ont une règle de réécriture pour rediriger de HTTP vers HTTPS. Cela ne fonctionne pas avec le module Application Initialization: il envoie une demande à la racine, et tout ce qu'il obtient est une redirection 301 du module URL Rewrite, et le pipeline ASP.NET n'est pas du tout atteint, le travail acharné n'a pas été '' t réellement fait. App Service/IIS a alors pensé que le processus de travail était prêt, puis lui envoie du trafic. Mais la première "vraie" demande suit en fait la redirection 301 vers l'URL HTTPS, et bam! cet utilisateur subit la douleur d'un démarrage à froid.

J'ai ajouté une règle de réécriture décrite ici pour exempter le module d'initialisation d'application d'avoir besoin de HTTPS, donc quand il atteindra la racine du site, il déclenchera en fait le chargement de la page et donc tout le pipeline:

<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>

C'est l'une des nombreuses entrées d'un journal de déplacement d'anciennes applications dans Azure - il s'avère qu'il y a beaucoup de choses que vous pouvez vous passer quand quelque chose fonctionne sur un VM qui redémarre rarement, mais il 'ai besoin d'un peu de TLC pour résoudre les problèmes lors de la migration vers notre nouveau monde courageux dans le cloud ....

-

MISE À JOUR 27/10/2017: Depuis cette écriture, Azure a ajouté un nouvel outil sous "Diagnostiquer et résoudre les problèmes". Cliquez sur "Redémarrage de l'application Web", et cela vous indiquera la raison, généralement en raison de la latence du stockage ou des mises à niveau de l'infrastructure. Ce qui précède reste cependant, dans la mesure où lors du passage à Azure App Service, la meilleure façon de procéder est que vous n'avez vraiment qu'à amener votre application à être à l'aise avec les redémarrages aléatoires.

-

MISE À JOUR 2/11/2018: Après la migration de plusieurs systèmes hérités vers une seule instance d'un plan de service App moyen (avec beaucoup de CPU et de mémoire), J'avais un problème vexant où mes déploiements à partir des emplacements de transfert se déroulaient de manière transparente, mais chaque fois que je commençais à démarrer sur un nouvel hôte en raison de la maintenance de l'infrastructure Azure, tout se détraquait avec un temps d'arrêt de 2-3 minutes. Je me rendais fou en essayant de comprendre pourquoi cela se produisait, car App Service est censé attendre qu'il reçoive une réponse réussie de votre application avant de vous démarrer sur le nouvel hôte.

J'étais tellement frustré par cela que j'étais prêt à classer App Service en tant qu'ordures d'entreprise et à revenir aux machines virtuelles IaaS.

Cela s'est avéré être de multiples problèmes, et je soupçonne que d'autres les rencontreront lors du portage de leurs propres applications ASP.NET héritées sur App Service, j'ai donc pensé les parcourir tous ici.

La première chose à vérifier est que vous faites réellement du vrai travail dans votre Application_Start. Par exemple, j'utilise NHibernate, qui, bien que bon à bien des égards, est un vrai cochon pour charger sa configuration, donc je m'assure de créer réellement le SessionFactory pendant Application_Start pour vous assurer que le travail acharné est fait.

La deuxième chose à vérifier, comme mentionné ci-dessus, est que vous n'avez pas de règle de réécriture pour SSL qui interfère avec la vérification de préchauffage d'App Service. Vous pouvez exclure les contrôles d'échauffement de votre règle de réécriture comme mentionné ci-dessus. Ou, depuis le moment où j'ai écrit ce travail, App Service a ajouté un indicateur HTTPS uniquement qui vous permet de faire la redirection HTTPS sur l'équilibreur de charge plutôt que dans votre fichier web.config. Puisqu'il est géré à une couche d'indirection au-dessus de votre code d'application, vous n'avez pas à y penser, donc je recommanderais le drapeau HTTPS uniquement comme voie à suivre.

La troisième chose à considérer est de savoir si vous utilisez ou non App Service Local Cache Option . En bref, il s'agit d'une option où App Service copiera les fichiers de votre application vers le stockage local des instances sur lesquelles elle s'exécute plutôt que hors d'un partage réseau, et est une excellente option à activer si votre application s'en fiche perd les modifications écrites dans le système de fichiers local. Il accélère les performances d'E/S (ce qui est important car, rappelez-vous, App Service s'exécute sur des pommes de terre ) et élimine les redémarrages causés par toute maintenance sur le partage réseau. Mais, il existe une subtilité spécifique concernant les mises à niveau de l'infrastructure d'App Service qui est mal documentée et dont vous devez être conscient. Plus précisément, l'option Cache local est lancée en arrière-plan dans un domaine d'application distinct après la première demande, puis vous passez au domaine d'application lorsque le cache local est prêt. Cela signifie qu'App Service lancera une demande de préchauffage sur votre site, obtiendra une réponse réussie, dirigera le trafic vers cette instance, mais (whoops!) Maintenant, le cache local broie les E/S en arrière-plan, et si vous avez beaucoup de sites dans ce cas, vous avez interrompu votre activité car les E/S App Service sont horribles. Si vous ne savez pas que cela se produit, cela semble effrayant dans les journaux, car c'est comme si votre application démarre deux fois sur la même instance (car c'est le cas). La solution consiste à suivre cette article de blog Jet et à créer une page de préchauffage d'initialisation d'application pour surveiller la variable d'environnement qui vous indique quand le cache local est prêt. De cette façon, vous pouvez forcer App Service à retarder le démarrage vers la nouvelle instance jusqu'à ce que le cache local soit entièrement préparé. En voici une que j'utilise pour m'assurer que je peux aussi parler à la base de données:

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);
    }
}

N'oubliez pas non plus de mapper un itinéraire et d'ajouter l'initialisation de l'application à votre web.config:

<applicationInitialization doAppInitAfterRestart="true">
  <add initializationPage="/warmup" />
</applicationInitialization>

La quatrième chose à considérer est que parfois App Service redémarrera votre application pour des raisons apparemment inutiles. Il semble que la définition de la propriété fcnMode sur Disabled puisse aider; il empêche le runtime de redémarrer votre application si quelqu'un manipule des fichiers de configuration ou du code sur le serveur. Si vous utilisez des emplacements de transfert et effectuez des déploiements de cette façon, cela ne devrait pas vous déranger. Mais si vous vous attendez à être en mesure de FTP et de traiter avec un fichier et de voir ce changement reflété dans la production, alors n'utilisez pas cette option:

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

La cinquième chose à considérer, et c'était surtout mon problème depuis le début, est de savoir si vous utilisez ou non les emplacements intermédiaires avec l'option AlwaysOn activée. L'option AlwaysOn fonctionne en envoyant une requête ping à votre site toutes les minutes environ pour vous assurer qu'il fait chaud afin que IIS ne le fasse pas tourner. Inexplicablement, ce n'est pas le cas) t un paramètre collant , donc vous avez peut-être activé AlwaysOn sur vos emplacements de production et de transfert afin de ne pas avoir à vous en occuper à chaque fois. Cela pose un problème avec les mises à niveau de l'infrastructure App Service lorsque ils vous démarrent sur un nouvel hôte. Voici ce qui se passe: disons que vous avez 7 sites hébergés sur une instance, chacun avec son propre emplacement de transfert, tout avec AlwaysOn activé. App Service effectue le préchauffage et l'initialisation de l'application sur votre 7 emplacements de production et attend consciencieusement qu'ils répondent avec succès avant de rediriger le trafic. Mais il ne fait pas cela pour les emplacements de transfert. Il dirige donc le trafic sur la nouvelle instance, mais AlwaysOn démarre dans 1-2 minutes plus tard sur les emplacements intermédiaires, vous avez donc maintenant 7 autres sites démarrant en même temps. Rappelez-vous, App Service s'exécute sur pommes de terre , donc un Toutes ces E/S supplémentaires se produisant en même temps vont détruire les performances de vos emplacements de production et seront perçues comme des temps d'arrêt.

La solution consiste à garder AlwaysOn hors tension sur vos emplacements de transfert afin de ne pas être cloué par cette frénésie d'E/S simultanée après une mise à jour de l'infrastructure. Si vous utilisez un script de swap via PowerShell, le maintien de ce "Off en staging, On en production" est étonnamment bavard à faire:

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

Ce script définit l'emplacement de transfert pour que AlwaysOn soit activé, effectue l'échange de sorte que le transfert soit désormais en production, puis définit l'emplacement de transfert pour que AlwaysOn soit désactivé, afin de ne pas faire exploser les choses. après une mise à niveau de l'infrastructure.

Une fois que cela fonctionne, il est en effet agréable d'avoir un PaaS qui gère les mises à jour de sécurité et les pannes matérielles pour vous. Mais c'est un peu plus difficile à réaliser dans la pratique que ne le suggèrent les supports marketing. J'espère que cela aide quelqu'un.

38
Nicholas Piasecki