Fermer

novembre 9, 2018

Dépannage d'une application ASP.NET Core s'exécutant dans Docker

TB_870x220


Mon ami et collègue, Paul Ballard, a partagé le récent article d'Andrew Lock sur un problème lié à une erreur de "chaîne de connexion manquante" dans une nouvelle application ASP.NET Core s'exécutant dans Docker. Andrew, l'auteur du nouveau livre ASP.NET Core in Action nous a gracieusement autorisé à republier son article . Dans cet article, non seulement il couvre l’arrière-plan de base des environnements dans ASP.NET Core, mais il explique également comment vous utiliseriez une configuration spécifique à l’environnement. Il décrit le bogue et fournit sa solution.

Andrew Lock, développeur à plein temps, travaille principalement au développement ASP.NET à pile complète à Devon, au Royaume-Uni.

J'espère que vous apprécierez!


Chargement de la configuration spécifique à l’environnement ASP.NET Core

Par Andrew Lock

Je venais de mettre au point une nouvelle application ASP.NET Core exécutée dans Docker, et j’observais un comportement très étrange. L'application pourrait démarrer sans aucun problème lors de l'exécution locale sur mon ordinateur Windows. Mais lorsque je le poussais sur le serveur de génération, l'application échouait immédiatement, invoquant une "chaîne de connexion manquante" ou quelque chose de similaire. J'ai passé une bonne demi-heure à essayer de résoudre le problème, alors cet article est juste au cas où quelqu'un d'autre rencontrerait le même problème!

Dans cet article, je vais couvrir le contexte de base des environnements dans ASP.NET Core et décrire comment vous utiliseriez généralement une configuration spécifique à un environnement. Enfin, je décrirai le bogue que j’ai rencontré et la raison pour laquelle il s’agissait d’un problème.

dr; IHostingEnvironment ignore le cas de l’environnement actuel lorsque vous utilisez le IsDevelopment () Cependant, si vous utilisez des fichiers de configuration spécifiques à l'environnement, par exemple appsettings.Development.json vous devez faire attention au cas. Si vous définissez l'environnement sur development au lieu de Development vos fichiers de configuration ne se chargeront pas sur un système d'exploitation sensible à la casse tel que Linux.

ASP.NET Core Environments

ASP. NET Core a le concept d'environnements qui représente les différents emplacements où votre code peut s'exécuter. Vous pouvez déterminer l'environnement actuel au moment de l'exécution et utiliser cette valeur pour modifier le comportement de votre application. Par exemple, dans Startup.Configure () il est courant de configurer votre pipeline d'intergiciels différemment si vous exécutez Development par opposition à Production :

. ] public void Configure (application IApplicationBuilder, env. IHostingEnvironment)
{
  // ajouté uniquement lors de l'exécution en développement
  if (env.IsDevelopment ())
  {
    app.UseDeveloperExceptionPage ();
  }

  // ajouté uniquement lors de l'exécution en production
  if (env.IsProduction ())
  {
    app.UseExceptionHandler ("/ Error");
  }

  app.UseStaticFiles ();
  app.UseMvc ();
} 

Vous pouvez utiliser IHostingEnvironment n'importe où dans votre application où vous souhaitez vérifier l'environnement actuel et vous comporter de manière différente en fonction de la valeur.

ASP.NET Core connaît par défaut trois environnements, et fournit des méthodes d'extension pour travailler avec elles:

  • Développement : identifié à l'aide de IHostingEnvironment.IsDevelopment ()
  • Staging : identifié à l'aide de IHostingEnvironment.IsStaging ()
  • Production . : identifié avec IHostingEnvironment.IsProduction ()

Vous pouvez également visualiser la valeur de l'environnement actuel en lisant IHostingEnvironment.EnvironmentName directement, mais il est vivement recommandé d'utiliser l'une des méthodes d'extension. . Les méthodes d'extension prennent soin de faire une comparaison insensible à la casse entre le EnvironmentName et la chaîne attendue (c'est-à-dire Development ).

code avec des vérifications impératives de l'environnement, une approche généralement plus propre consiste à utiliser une configuration spécifique à l'environnement, que je décrirai plus tard.

Primaire de configuration pour ASP.NET Core

Le système de configuration dans ASP.NET Core est construit. de couches de valeurs de configuration, compilées à partir de sources multiples. Vous pouvez charger des valeurs à partir de fichiers JSON fichiers XML variables d'environnement ou vous pouvez créer un fournisseur personnalisé pour charger des valeurs à partir de à peu près n'importe où .

Vous pouvez créer un objet de configuration en ajoutant des fournisseurs à un objet IConfigurationBuilder. Cela se produit généralement dans Program.cs à l'aide de la méthode IWebHostBuilder.ConfigureAppConfiguration . WebHost.CreateDefaultBuilder () appelle cette méthode en arrière-plan dans une application ASP.NET Core 2.x typique. Chaque fournisseur ajouté à IConfigurationBuilder ajoute une autre couche de configuration. Par exemple, le code suivant ajoute un fichier JSON ( appsettings.json ) et des variables d'environnement à l'objet de configuration final:

 IHostingEnvironment env;
var builder = new ConfigurationBuilder ()
  .SetBasePath (env.ContentRootPath) // le chemin depuis lequel le fichier JSON doit être chargé
  .AddEnvironmentVariables (); 

L’ordre des fournisseurs de configuration est important ici; Si une variable d'environnement porte le même nom qu'un paramètre du fichier JSON, le paramètre JSON sera écrasé. La configuration finale sera une vue "aplatie" des paramètres de toutes les sources de configuration.

Je pense que l'aplatissement des fournisseurs de configuration est similaire à l'aplatissement des calques dans une image Photoshop. chaque couche remplace les valeurs des couches précédentes, sauf si elle est transparente (c'est-à-dire si la couche n'a pas de valeur).

Par exemple, imaginez que vous disposiez du fichier de configuration suivant: appsettings.json :

 {
  "Enregistrement": {
    "LogLevel": {
      "Par défaut": "Debug",
      "Système": "Information",
      "Microsoft": "Information"
    }
  }
} 

Cela générerait seul les paramètres suivants:

 "Logging: LogLevel: Default" = "Debug";
"Logging: LogLevel: System" = "Informations";
"Logging: LogLevel: Microsoft" = "Information"; 

Toutefois, si vous aviez également une variable d'environnement:

 Logging__LogLevel__Default = Warning 

et l'a chargé après votre fichier JSON, le fichier final. la configuration serait la suivante (notez le changement de valeur pour le premier paramètre):

 "Logging: LogLevel: Default" = "Warning";
"Logging: LogLevel: System" = "Informations";
"Logging: LogLevel: Microsoft" = "Informations"; 

Configuration spécifique à l'environnement

"L'aplatissement" des fournisseurs de configuration vous permet d'avoir une configuration spécifique à l'environnement. Prenons le cas habituel où vous souhaitez utiliser un paramètre différent dans le développement local par rapport à la production. Vous pouvez y parvenir de différentes manières, par exemple:

  • Écraser les valeurs par défaut par ex. définissez uniquement une variable d'environnement pour le paramètre dans Production.
  • Utilisez des paramètres de fournisseur de configuration différents par ex. Chargez les paramètres depuis Azure Key Vault en production et les secrets d'utilisateur pour le développement local.
  • Chargez des fournisseurs de configuration supplémentaires p. Ex. charger un fichier JSON spécifique à l'environnement supplémentaire

Ces deux derniers points sont essentiellement la même chose, mais je voulais les appeler comme différents, car ils sont généralement utilisés pour deux choses légèrement différentes, les secrets par rapport aux paramètres.

Les secrets tels que les clés d'API et les chaînes de connexion, ne doivent pas être stockés dans votre référentiel. Pour le développement local, les valeurs sensibles doivent être stockées dans User Secrets . En production, les secrets doivent être récupérés auprès d'un fournisseur tel que Azure Key Vault .

En revanche, les paramètres ne sont pas des valeurs sensibles, ils représentent simplement un élément que vous pouvez configurer différemment. entre les environnements. Par exemple, vous souhaitez peut-être utiliser davantage de mémoire cache en production ou écrire des fichiers journaux à différents emplacements.

 Configuration Loading 9 "title =" Configuration Loading 9 "/></p data-recalc-dims=

WebHost.CreateDefaultBuilder () typique Cette méthode utilise les trois approches: écrasement, fournisseurs différents et fournisseurs supplémentaires. La méthode de configuration du générateur par défaut est présentée ci-dessous:

 Configuration Loading 10 "title =" Configuration Loading 10 "/></p data-recalc-dims=

Le constructeur configure jusqu'à 5 fournisseurs de configuration par défaut:

  • Un fichier JSON appelé appsettings.json
  • Un fichier JSON spécifique à l'environnement appelé appsettings.ENVIRONMENT.json ENVIRONMENT [1359019]. est le nom de l'environnement actuel
  • Secrets de l'utilisateur, si dans l'environnement de développement
  • Variables d'environnement
  • Arguments en ligne de commande (si des arguments ont été passés)

Pour le reste de cet article, je suis va se concentrer sur t Le fichier JSON spécifique à l’environnement, c’est ce qui a causé le problème que j’ai rencontré.

Le problème: la configuration spécifique à l’environnement ne se charge pas

Dans le cadre d’une nouvelle application .NET Core que je construisais, j’exécutais une test "sur le conteneur Docker produit, comme décrit dans mon dernier message . Cela implique d'exécuter le conteneur Docker sur le serveur de génération et de vérifier que le conteneur démarre correctement. L'idée est de vérifier que la configuration initiale au démarrage de l'application est correcte. Une de ces vérifications est que toute validation des paramètres fortement typés fonctionne correctement.

 Configuration Loading 11 "title =" Configuration Loading 11 "/></p data-recalc-dims=

Lorsque j’ai exécuté le test de fumée pour la première Dans une nouvelle application, la validation des paramètres pour une URL d’API tierce a échoué. C’était très étrange, car j’avais testé l’application localement. Lors de l'exécution de tests de fumée, je règle généralement l'environnement d'hébergement de l'application sur Développement (ou Dans le fichier appsettings.Development.json je pouvais voir la valeur de configuration incriminée:

 Configuration Loading 12 "title =" Configuration Loading 12 " /></p data-recalc-dims=

Mais pour une raison quelconque, lorsque l'application était exécutée dans Docker pour les tests de détection de la fumée, la valeur n'était pas liée correctement. Dans la section suivante, je vais décrire brièvement certaines des choses auxquelles j'ai pensé et que j'ai examiné.

Dépannage

J'ai essayé de déboguer localement, d'ajouter et de supprimer le fichier et de modifier la valeur du paramètre. J'essayais de confirmer que le fichier était bien chargé correctement et que les paramètres ne venaient pas ailleurs lors de l'exécution locale. Tout était correct.

J'ai vérifié qu'il n'y avait pas de variables d'environnement inattendues remplaçant la valeur lorsque l'application était exécutée dans Docker pour le test de détection de fumée.

J'ai jeté un œil à l'intérieur du conteneur Docker et vérifié que le fichier appsettings.Development.json existait et se trouvait au bon endroit. Tout semblait bien se passer.

Enfin, j'ai vérifié que je courais dans l'environnement auquel je m'attendais - Développement. En regardant les journaux du conteneur lorsque le test de fumée a été exécuté, j'ai pu constater que l'environnement d'hébergement était correct selon l'application:

 Configuration Loading 13 "title =" Configuration Loading 13 "/></p data-recalc-dims=

Je me suis fait un café.

Lorsque je me suis assis et que j'ai ouvert le fichier script de test de fumée, la réponse m'a immédiatement frappé…

Sensibilité à la casse des systèmes de fichiers Linux

Le script de test de fumée que j'utilisais est très similaire au script de mon dernier message . La commande que j'utilisais pour exécuter ma nouvelle application pour le test de fumée est présentée ci-dessous:

 Configuration Loading 14 "title =" Configuration Loading 14 "/></p data-recalc-dims=

Le problème est la déclaration dans laquelle je règle la variable d'environnement pour définir l'environnement d'hébergement à l'aide de

 Configuration Loading 15" title = "Configuration Loading 15 "/></p data-recalc-dims=

Ceci établit un environnement de développement qui n'est pas le identique au développement. ASP.NET Core lui-même veille à ne pas différencier les environnements en fonction du cas - les méthodes d'extension IHostingEnvironment telles que IsDevelopment () sont sensibles à la casse sur . Tant que vous utilisez ces méthodes d'extension et que vous n'utilisez pas directement IHostingEnvironment.EnvironmentName, tout ira bien.

Cependant, le seul endroit où il est très commun d'utiliser directement EnvironmentName se trouve dans votre application configuration. Auparavant, j’avais décrit l’approche commune en matière de configuration propre à l’environnement: ajout d’un fichier supplémentaire appsettings.json :

 Configuration Loading 16 "title =" Configuration Loading 16 "/></p data-recalc-dims=

As you Vous pouvez voir que nous utilisons directement EnvironmentName pour calculer le fichier de configuration JSON spécifique à l'environnement. Dans mon script de test de fumée, EnvironmentName = "development", l'application recherchait donc le fichier appsettings.development.json . Le fichier s'appelait en réalité appsettings.Development.json .

Sous Windows, cette différence de casse n'a pas d'importance - ASP.NET Core respecte les conventions du système d'exploitation hôte et le charge ainsi. Même si vous définissez l'environnement sur DeVelOpMeNt, tout irait bien. Cependant, Linux est sensible à la casse, il ne trouvera pas le fichier.

La solution simple consistait à définir l'environnement avec la casse de titre standard:

 Chargement de la configuration 17 "title =" Configuration du chargement 17 "/></p data-recalc-dims=

Avec ce petit changement, l'application a pu démarrer et le test de fumée a réussi.

Résumé

Soyez toujours cohérent avec vos noms d'environnement. Le cas peut ne pas avoir d’importance si vous utilisez Windows, mais ce sera certainement le cas si votre application est exécutée sous Linux. Le framework ASP.NET Core lui-même veille à ignorer la casse lors de la détermination de l'environnement actuel, mais vous ne pouvez pas faire confiance au système d'exploitation sous-jacent pour faire de même!

Note de la rédaction: merci encore à Andrew Lock de nous avoir permis de partager cette information. corrigez-vous en republiant cet article.


Les commentaires sont désactivés en mode Prévisualisation.



Source link