ASP.NET Core 與 Kubernetes:從 ConfigMap 中讀取配置到 IConfiguration

我們部署在 kubernetes 集羣上的每個 ASP.NET Core 應用的 appsettings.Production.json 都保存在各個應用的 ConfigMap 中,這些 appsettings.Production.json 中有些重複的配置,如果要修改這些配置,需要到各個應用中一個一個修改,很是麻煩。

針對這個麻煩,我們想到一個解決方法,將這些重複的配置放到一個公用的 ConfigMap 中(appsettings.shared.json),但是要到各個應用的 deployment 配置文件中通過 volumeMounts 一個一個 mount 這個 ConfigMap 也很是麻煩。

針對新的麻煩,我們又想到一個解決方法,在代碼中直接讀取 ConfigMap,選用的 Kubernetes C# 客戶端是 KubernetesClient,實現方法如下。

安裝 nuget 包

dotnet add package KubernetesClient

在 Program 中添加讀取 ConfigMap 的方法實現

private static byte[] ReadK8sConfigMap()
{
    var config = KubernetesClientConfiguration.InClusterConfig(); 
    IKubernetes client = new Kubernetes(config);

    var cm = client.ReadNamespacedConfigMap(name: "appsettings.shared.json",  "production");
    return System.Text.Encoding.UTF8.GetBytes(cm.Data["appsettings.shared.json"]);
}

將從 ConfigMap 讀取到的數據以 json 的方式加載到 IConfiguration 中

Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
    webBuilder.ConfigureAppConfiguration((hostingContext, config) =>
    {
        var env = hostingContext.HostingEnvironment;
        config.AddJsonFile("appsettings.json", optional: true)
            .AddJsonStream(new MemoryStream(ReadK8sConfigMap()))
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

        config.AddEnvironmentVariables();
    });

    webBuilder.UseStartup<Startup>();
});

改進後的代碼

namespace Microsoft.Extensions.Configuration
{
    public static class ConfigMapExtensions
    {
        public static IConfigurationBuilder AddJsonKubeConfigMap(this IConfigurationBuilder builder, string name, string @namespace, string key)
        {
            var config = KubernetesClientConfiguration.IsInCluster() ?
                KubernetesClientConfiguration.InClusterConfig() :
                KubernetesClientConfiguration.BuildDefaultConfig();
            IKubernetes client = new Kubernetes(config);

            var json = client.ReadNamespacedConfigMap(name, @namespace)?.Data[key];
            if (!string.IsNullOrEmpty(json))
            {
                builder.AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(json)));
            }

            return builder;
        }
    }
}

在 pod 中讀取 ConfigMap 報錯

Unhandled exception. Microsoft.Rest.HttpOperationException: Operation returned an invalid status code 'Forbidden'
   at k8s.Kubernetes.ReadNamespacedConfigMapWithHttpMessagesAsync(String name, String namespaceParameter, Nullable`1 exact, Nullable`1 export, String pretty, Dictionary`2 customHeaders, CancellationToken cancellationToken)
   at k8s.KubernetesExtensions.ReadNamespacedConfigMapAsync(IKubernetes operations, String name, String namespaceParameter, Nullable`1 exact, Nullable`1 export, String pretty, CancellationToken cancellationToken)
   at k8s.KubernetesExtensions.ReadNamespacedConfigMap(IKubernetes operations, String name, String namespaceParameter, Nullable`1 exact, Nullable`1 export, String pretty)
   at Microsoft.Extensions.Configuration.ConfigMapExtensions.AddJsonKubeConfigMap(IConfigurationBuilder builder, String name, String namespace, String key)

在 pod 中用 curl 命令進行請求

curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
     -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
     https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/api/v1/namespaces/production/configmaps/appsettings.shared.json

響應如下

{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
    
  },
  "status": "Failure",
  "message": "configmaps \"appsettings.shared.json\" is forbidden: User \"system:serviceaccount:production:default\" cannot get resource \"configmaps\" in API group \"\" in the namespace \"production\"",
  "reason": "Forbidden",
  "details": {
    "name": "appsettings.shared.json",
    "kind": "configmaps"
  },
  "code": 403
}

原來是 pod 的默認 service account system:serviceaccount:production:default 沒有權限請求 api。

通過添加 Role 與 RoleBinding 解決了這個問題,詳見博問:k8s 中如何授權 pod 內可以訪問指定的 ConfigMap

後來將 AddJsonKubeConfigMap 擴展方法放到了 github 倉庫 https://github.com/cnblogs/KubernetesClient.Extensions

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章