ASP.NET Core 配置值有时在 Kubernetes 中返回空

sin*_*yil 1 environment-variables docker kubernetes asp.net-core

我目前正在开发一个在 Kubernetes 上使用 .NET Core 2.1 运行的项目。我的应用程序从文件和环境变量中读取配置值appsettings.json

问题是当我尝试从接口读取配置时IConfiguration,“大多数时候”值返回 null,但仅限于 Kubernetes。它在我的开发机器和带有 Windows VM 和 IIS 的测试机器上完美运行。这些环境之间的区别在于开发和测试机器是 Windows,但生产是在 Linux 容器上运行的 Kubernetes(基于microsoft/dotnet:2.1-aspnetcore-runtime映像)以及我如何实现环境变量。在我的开发机器中没有定义环境变量。web.config在 Windows VM 中,我确实通过如下条目使用环境变量:

<aspNetCore processPath="dotnet" arguments="myapp.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout">
    <environmentVariables>
         <environmentVariable name="Site:SiteId" value="1" />
         <environmentVariable name="Site:Theme" value="MyTheme" />
    </environmentVariables>
</aspNetCore>
Run Code Online (Sandbox Code Playgroud)

我用于WebHost.CreateDefaultBuilder()配置和文档说方法实现了appsettings.json配置appsettings.{EnvironmentName}.json源的环境变量和命令行参数。

我的appsettings.json文件:

{
  "Site": {
    "SiteId": 1,
    "Theme": "Limitless"
  },
  "RemoteClient": {
    "HostUrl": "http://localhost:59554/management/",
    "AppKey": "test",
    "AppSecret": "1"
  }
}
Run Code Online (Sandbox Code Playgroud)

最后是我的 Kubernetes 部署文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      component: myapp
  strategy: {}
  template:
    metadata:
      labels:
        component: myapp
    spec:
      nodeSelector:
        beta.kubernetes.io/os: linux
      containers:
      - image: 'myregistry/myapp:latest'
        imagePullPolicy: IfNotPresent
        name: myapp
        env:
        - name: "Site__SiteId"
          value: "1"
        - name: "Site__Theme"
          value: "Limitless"
        - name: "RemoteClient__HostUrl"
          value: "http://apphost/management"
        - name: "RemoteClient__AppKey"
          value: e824b2670e644824bdd5cdee1db9bd4b
        - name: "RemoteClient__AppSecret"
          value: 12d4deadf948
        - name: "ASPNETCORE_ENVIRONMENT"
          value: Production
        ports:
        - containerPort: 80
      restartPolicy: Always
Run Code Online (Sandbox Code Playgroud)

我说的是“大多数时候”,因为每次 Pod 重新启动时它的行为都会有所不同。有时它读取一个变量而不读取其他变量,有时它读取其余变量,最终它可以读取所有变量并且应用程序正常运行。你可以在kubectl describe pod输出中看到

kubectl 描述 pod 输出

但是当我从服务读取值IConfiguration并将其输出到控制台时,输出如下所示:

配置输出1

以及其他重启

配置输出2

在这两次运行之间我没有改变任何东西,它只是重新启动。它没有读取值,甚至没有读取 appsettings.json 中定义的默认值。

那么您认为为什么会发生这种情况?

编辑:

我正在使用此代码块进行初始配置,CreateDefaultBuilder()负责配置。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .AddPreConfigureServices();
Run Code Online (Sandbox Code Playgroud)

和注释CreateDefaultBinder()

//
// Summary:
//     Initializes a new instance of the Microsoft.AspNetCore.Hosting.WebHostBuilder
//     class with pre-configured defaults.
//
// Parameters:
//   args:
//     The command line args.
//
// Returns:
//     The initialized Microsoft.AspNetCore.Hosting.IWebHostBuilder.
//
// Remarks:
//     The following defaults are applied to the returned Microsoft.AspNetCore.Hosting.WebHostBuilder:
//     use Kestrel as the web server and configure it using the application's configuration
//     providers, set the Microsoft.AspNetCore.Hosting.IHostingEnvironment.ContentRootPath
//     to the result of System.IO.Directory.GetCurrentDirectory, load Microsoft.Extensions.Configuration.IConfiguration
//     from 'appsettings.json' and 'appsettings.[Microsoft.AspNetCore.Hosting.IHostingEnvironment.EnvironmentName].json',
//     load Microsoft.Extensions.Configuration.IConfiguration from User Secrets when
//     Microsoft.AspNetCore.Hosting.IHostingEnvironment.EnvironmentName is 'Development'
//     using the entry assembly, load Microsoft.Extensions.Configuration.IConfiguration
//     from environment variables, load Microsoft.Extensions.Configuration.IConfiguration
//     from supplied command line args, configures the Microsoft.Extensions.Logging.ILoggerFactory
//     to log to the console and debug output, enables IIS integration, and enables
//     the ability for frameworks to bind their options to their default configuration
//     sections.
public static IWebHostBuilder CreateDefaultBuilder(string[] args);
Run Code Online (Sandbox Code Playgroud)

编辑2:

像这样读取我的值Startup.cs

services.AddMvc(options => { })
    .AddEntityBinders()
    .AddRazorOptions(options =>
    {
        string theme = Configuration["Site:Theme"];

        options.ViewLocationFormats.Clear();
        options.ViewLocationFormats.Add($"~/Views/{theme}Theme/{{1}}/{{0}}.cshtml");
        options.ViewLocationFormats.Add($"~/Views/{theme}Theme/Shared/{{0}}.cshtml");
    })
    .SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_1);
Run Code Online (Sandbox Code Playgroud)

sin*_*yil 5

经过漫长而困难的调试过程。我终于可以找出问题所在了。

.Net Core 使用:配置键分隔符,但 Kubernetes 配置文件在命名环境变量时不支持此类字符。EnvironmentVariablesConfigurationProvider通过接受作为分隔符并在读取值时__替换为来解决此问题。:

因此,我在 Kubernetes 部署文件中使用 __ 分隔符声明环境变量(如上所示)。但 Dockerfile 没有这个问题,我用:分隔符编写 ENV 定义(如下),这会创建重复的变量,.Net Core 会读取所有这些变量并产生不稳定的结果。

ENV RemoteClient:HostUrl ${HOST_URL}
ENV RemoteClient:AppKey ${APP_KEY}
ENV RemoteClient:AppSecret ${APP_SECRET}
ENV ASPNETCORE_ENVIRONMENT ${ENVIRONMENT_NAME}
Run Code Online (Sandbox Code Playgroud)