.NET Core 使用 K8S ConfigMap的正确姿势
背景
ASP.NET Core默認的配置文件定義在 appsetings.json和 appsettings.{Environment}.json文件中。這里面有一個問題就是,在使用容器部署時,每次修改配置文件都需要重新構建鏡像。當然你也可能會說,我的配置文件很穩定不需要修改,但你又如何確保配置文件中一些機密配置的安全問題呢?比如暴露了你的遠程數據庫的連接信息,哪天被員工不小心刪庫跑路了呢?那接下來就來講解下如何在.NET Core 中正確使用ConfigMap。
ConfigMap/Secret
K8S中引入了ConfigMap/Secret來存儲配置數據,分別用于存儲非敏感信息和敏感信息。其目的在于將應用和配置解耦,以確保容器化應用程序的可移植性。
創建 ConfigMap
玩耍K8S,請先自行準備環境,Win10用戶可以參考我的上篇文章ASP.NET Core 借助 K8S 玩轉容器編排來準備環境。
ConfigMap的創建很簡單,一句命令就可以直接將 appsettings.json文件轉換為ConfigMap。
PS:使用K8S一定要善用幫助命令,比如執行 kubectl create configmap-h,你就可以了解到多種創建ConfigMap的方式。
> kubectl create configmap -h Create a configmap based on a file, directory, or specified literal value. A single configmap may package one or more key/value pairs. When creating a configmap based on a file, the key will default to the basename of the file, and the value will default to the file content. If the basename is an invalid key, you may specify an alternate key. When creating a configmap based on a directory, each file whose basename is a valid key in the directory will be packaged into the configmap. Any directory entries except regular files are ignored (e.g. subdirectories, symlinks, devices, pipes, etc). Aliases: configmap, cm Examples: # Create a new configmap named my-config based on folder bar kubectl create configmap my-config --from-file=path/to/bar # Create a new configmap named my-config with specified keys instead of file basenames on disk kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt # Create a new configmap named my-config with key1=config1 and key2=config2 kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2 # Create a new configmap named my-config from the key=value pairs in the file kubectl create configmap my-config --from-file=path/to/bar # Create a new configmap named my-config from an env file kubectl create configmap my-config --from-env-file=path/to/bar.env其中我們可以看到可以通過指定 --from-file來從指定文件創建。
kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txtLet's have a try!
1. 先行創建示例項目:dotnetnewmvc-n K8S.NETCore.ConfigMap
2. 默認包含兩個配置文件 appsettings.json和 appsettings.Development.json
3. 先來嘗試將 appsettings.json轉換為ConfigMap:
> cd K8S.NETCore.ConfigMap # 創建一個namespace,此步可選 > kubectl create namespace demo namespace "demo" created # -n變量指定configmap創建到哪個namespace下 > kubectl create configmap appsettings --from-file=appsettings.json=./appsettings.json -n demo configmap "appsettings" created # 查看剛剛創建的configmap,-o指定輸出的格式 > kubectl get configmap appsettings -n demo -o yaml apiVersion: v1 data: appsettings.json: "{\r\n \"Logging\": {\r\n \"LogLevel\": {\r\n \"Default\": \"Warning\"\r\n }\r\n },\r\n \"AllowedHosts\": \"*\"\r\n}\r\n" kind: ConfigMap metadata: creationTimestamp: null name: appsettings namespace: demo從上面的輸出結果來看,其中包含了 \r\n換行符,顯然不是我們想要的結果。猜測是因為Windows和Linux系統換行符的差異導致的。先來插播下換行符的知識:
CR:Carriage Return,對應ASCII中轉義字符\r,表示回車 LF:Linefeed,對應ASCII中轉義字符\n,表示換行 CRLF:Carriage Return & Linefeed,\r\n,表示回車并換行 眾所周知,Windows操作系統采用兩個字符來進行換行,即CRLF;Unix/Linux/Mac OS X操作系統采用單個字符LF來進行換行;
所以解決方式就很簡單,將換行符切換為Linux系統的 \n即可。操作方式很簡單:對于VS Code 只需要按圖下所示操作即可,點擊右下角的 CRLF,選擇 LF即可。
對于VS,如果VS打開json文件有下面的提示,直接切換就好。沒有,可以安裝Line Endings Unifier)擴展來統一處理。
# 先刪除之前創建的configmap > kubectl delete configmap appsettings -n demo > kubectl create configmap appsettings --from-file=appsettings.json=./appsettings.json -n demo configmap "appsettings" created > kubectl get configmap appsettings -n demo -o yaml apiVersion: v1 data: appsettings.json: | { "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*" } kind: ConfigMap metadata: creationTimestamp: null name: appsettings namespace: demo現在ConfigMap的格式正常了。下面我們嘗試把 appsettings.Development.json也合并到一個ConfigMap中。
> kubectl delete configmap appsettings -n demo > kubectl create configmap appsettings --from-file=appsettings.json=./appsettings.json --from-file=appsettings.Development.json=./appsettings.Development.json -n demo configmap "appsettings" created > kubectl get configmap appsettings -n demo -o yaml apiVersion: v1 data: appsettings.Development.json: | { "Logging": { "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } } appsettings.json: | { "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*" } kind: ConfigMap metadata: creationTimestamp: null name: appsettings namespace: demoPS:
如果你的配置文件包含多余的空格,則生成的ConfigMap可能就會包含?\n字符,就像這樣:?appsettings.Development.json:"{\n \"Logging\": {\n \"LogLevel\": {\n \"Default\": \"Debug\",\n \"System\": \"Information\",\n \"Microsoft\": \"Information\"\n \ }\n }\n} \n"。解決辦法就是保存文件時記得格式化文件就好了,或者手動刪除多余空格。
創建ConfigMap的時候可以指定?--dry-run參數進行試運行,避免直接創建到服務器。
從文件創建ConfigMap時,可以不指定Key,默認會以文件名為Key。kubectl create configmap appsettings--from-file=./appsettings.json--from-file=./appsettings.Development.json-n demo--dry-run-o yaml
至此,完成了appsetting到configmap的切換。
應用 ConfigMap
ConfigMap的應用很簡單,只需要將configmap掛載到容器內的獨立目錄即可。
先來看一下借助VS幫生成的Dockerfile。
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base WORKDIR /app EXPOSE 80 EXPOSE 443 FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build WORKDIR /src COPY ["K8S.NETCore.ConfigMap.csproj", ""] RUN dotnet restore "./K8S.NETCore.ConfigMap.csproj" COPY . . WORKDIR "/src/." RUN dotnet build "K8S.NETCore.ConfigMap.csproj" -c Release -o /app FROM build AS publish RUN dotnet publish "K8S.NETCore.ConfigMap.csproj" -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "K8S.NETCore.ConfigMap.dll"]可以看出文件中定義的 WORKDIR/app指定的工作目錄為 /app,所以需要把ConfigMap掛載到 /app目錄下。先執行 docker build-t k8s.netcore.configmap:dev. 構建鏡像。
我們來新建一個 configmap-deploy.yaml文件配置如下:
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: k8s-configmap-demo spec: selector: matchLabels: app: k8s-configmap-demo template: metadata: labels: app: k8s-configmap-demo spec: containers: - name: k8s-configmap-demo image: k8s.netcore.configmap:dev imagePullPolicy: IfNotPresent resources: limits: memory: "128Mi" cpu: "500m" ports: - containerPort: 80 volumeMounts: - mountPath: /app/appsettings.json name: test readOnly: true subPath: appsettings.json - mountPath: /app/appsettings.Development.json name: test readOnly: true subPath: appsettings.Development.json volumes: - configMap: defaultMode: 420 name: appsettings name: test這里有必要解釋兩個參數:
volumes:-configMap:指定引用哪個ConfigMap
volumeMounts:用來指定將ConfigMap中的配置掛載到容器的哪個路徑
subPath:用來指定引用ConfigMap的哪個配置節點。
創建Deployment之前先修改下ConfigMap的配置,以方便確認最終成功從ConfigMap掛載配置。將 Logging:LogLevel:Default:節點的默認值改為Error。
> kubectl edit configmap appsettings -n demo configmap/appsettings edited > kubectl get cm appsettings -n demo -o yaml apiVersion: v1 data: appsettings.Development.json: |- { "Logging": { "LogLevel": { "Default": "Error", "System": "Information", "Microsoft": "Information" } } } appsettings.json: | { "Logging": { "LogLevel": { "Default": "Error" } }, "AllowedHosts": "*" } kind: ConfigMap metadata: creationTimestamp: "2019-09-02T22:50:14Z" name: appsettings namespace: demo resourceVersion: "445219" selfLink: /api/v1/namespaces/demo/configmaps/appsettings uid: 07048d5a-cdd4-11e9-ad6d-00155d3a3103修改完畢后,執行后續命令來創建Deployment,并驗證。
# 創建deployment > kubectl apply -f .\k8s-deploy.yaml -n demo deployment.extensions/k8s-configmap-demo created # 獲取創建的pod > kubectl get pods -n demo NAME READY STATUS RESTARTS AGE k8s-configmap-demo-7cfbdfff67-xdrcx 1/1 Running 0 12s # 進入pod內部 > kubectl exec -it k8s-configmap-demo-7cfbdfff67-xdrcx /bin/bash -n demo root@k8s-configmap-demo-7cfbdfff67-xdrcx:/app# cat appsettings.json { "Logging": { "LogLevel": { "Default": "Error" } }, "AllowedHosts": "*" } root@k8s-configmap-demo-7cfbdfff67-xdrcx:/app# cat appsettings.Development.json { "Logging": { "LogLevel": { "Default": "Error", "System": "Information", "Microsoft": "Information" } } }從以上輸出可以看出,默認的配置項已被ConfigMap的配置覆蓋。
熱更新
以Volume方式掛載的ConfigMap支持熱更新(大概需要10s左右)。但一種情況例外,就是指定subPath的情況下,更新ConfigMap,容器中掛載的ConfigMap是不會自動更新的。
?A container using a ConfigMap as a?subPath?volume will not receive ConfigMap updates.
對于這種情況,也很好處理,將ConfigMap掛載到 /app目錄下一個單獨目錄就好,比如掛載到 /app/config目錄,然后修改配置文件的加載路徑即可。
hostBuilder.ConfigureAppConfiguration((context, builder) => { builder.SetBasePath(Path.Join(AppContext.BaseDirectory, "config")) .AddJsonFile("appsettings.json") .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true); });最后
本文就.NET Core如何應用ConfigMap進行了詳細的介紹。其中最關鍵在于appsettings.json到ConfigMap的轉換,以及掛載目錄的指定。希望對你有所幫助。而至于Secret的應用,原理相通了,關鍵在于Secret的生成,這里就交給你自己探索了。
總結
以上是生活随笔為你收集整理的.NET Core 使用 K8S ConfigMap的正确姿势的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 【A】兼容Core3.0后 Natash
 - 下一篇: ASP.NET Core on K8S深