ASP.NET Core 借助 Helm 部署应用至 K8S
前言
玩K8S也有一段時間了,借助云服務(wù)提供商的K8S控制臺,已經(jīng)可以很方便的快速部署應(yīng)用至K8S。通過簡單的點擊,可以一次性幫忙創(chuàng)建K8S 對象:Deployment、Service、Ingress、ConfigMap等。但是當(dāng)服務(wù)的規(guī)模上來后,這種方式就有點捉襟見肘。尤其是需要同時更新多個關(guān)聯(lián)服務(wù)時,就需要一個一個的去更改,就有點不太方便。為了解決這個問題,最近上手實操了一下Helm,發(fā)現(xiàn)生產(chǎn)力大大提升。
Helm 簡介
Helm 是一個為K8S打造的包管理器。通過Helm可以方便管理Kubernetes應(yīng)用程序。Helm主要有兩大核心概念:Charts、Release。
Chart:用來定義,安裝和升級K8S 應(yīng)用。亦可分享及版本化控制。
Release:類似Image之于Container,Release是Chart的運行實例。
目前Helm最新的版本為V3.1,較之前版本,在整體架構(gòu)上移除服務(wù)端Tiller。對于Windows系統(tǒng)而言可借助Choco快速安裝:choco install kubernetes-helm,通過執(zhí)行 helm version確認是否安裝成功。
version.BuildInfo{Version:"v3.1.0",GitCommit:"b29d20baf09943e134c2fa5e1e1cab3bf93315fa",GitTreeState:"clean",GoVersion:"go1.13.7"}
在繼續(xù)往前,請確保已具備基礎(chǔ)的K8S基礎(chǔ)知識,并且確保本機已安裝Docker和K8S。安裝教程和K8S簡單入門可參考我的這篇文章:ASP.NET Core 借助 K8S 玩轉(zhuǎn)容器編排。
對于第一次接觸Helm .NETer 來說我們可以通過VS 2019來快速體驗一下。請確保已安裝Visual Studio Tools for Kubernetes。
創(chuàng)建 Chart (helm create)
打開VS 創(chuàng)建項目,選擇Container Application for Kubernetes,創(chuàng)建一個空的ASP.NET Core Web 項目。創(chuàng)建后,項目結(jié)構(gòu)如下圖所示,與平時之間創(chuàng)建的Web項目而言,主要是多了一個 charts目錄、 Dockerfile和一個 azds.yaml。除了創(chuàng)建項目時通過選擇Container Application for Kubernetes類型外,我們也可以通過其他方式創(chuàng)建。我們這里手動刪除 charts目錄、 Dockerfile和一個 azds.yaml。然后如下圖步驟即可重新生成Helm Chart。
當(dāng)然也可以通過 helm create創(chuàng)建。
安裝 Chart (helm install)
在展開之前,先來簡要介紹Chart目錄:
k8shelmdemo/ # Chart 目錄 ├── charts # 這個 charts 依賴的其他 charts,始終被安裝 ├── Chart.yaml # 描述這個 Chart 的相關(guān)信息、包括名字、描述信息、版本等 ├── templates # 模板目錄 │ ├── deployment.yaml # deployment 控制器的 Go 模板文件 │ ├── _helpers.tpl # 以 _ 開頭的文件不會部署到 k8s 上,可用于定制通用信息 │ ├── ingress.yaml # ingress 的模板文件 │ ├── NOTES.txt # Chart 幫助文本,安裝后會顯示給用戶,例如:如何使用、列出缺省值 │ ├── service.yaml # service 的 Go 模板文件 │ ├── secrets.yaml # secrets 的 Go 模板文件 │ └── tests │ └── test-connection.yaml └── values.yaml # 模板的值文件,這些值會在安裝時應(yīng)用到 GO 模板生成部署文件簡單來說,Helm Chart 定義常用的K8S 對象模板,通過values.yaml來填充模板。那我們就來看看填充后的輸出結(jié)果是怎樣的。打開命令提示符,進入到Chart目錄,通過 helmtemplate--debug[release name][chart dir]命令就可以測試Chart(亦可通過 helm--dry-run--debug[release name][chart dir]測試)。可以看到輸出了Service和Deployment。這里你可能就納悶了,不是定義了4個K8S對象模板嗎,為什么就是輸出2個yaml文件呢。這里先按住不表。
PS \K8S.Helm.Demo\charts> helm template --debug k8s-helm-demo .\k8shelmdemo\ install.go:158: [debug] Original chart version: "" install.go:175: [debug] CHART PATH: \K8S.Helm.Demo\charts\k8shelmdemo --- # Source: k8shelmdemo/templates/service.yaml apiVersion: v1 kind: Service metadata:name: k8shelmdemolabels:app: k8shelmdemochart: k8shelmdemo-0.1.0release: k8s-helm-demoheritage: Helm spec:type: ClusterIPports:- port: 80targetPort: httpprotocol: TCPname: httpselector:app: k8shelmdemorelease: k8s-helm-demo --- # Source: k8shelmdemo/templates/deployment.yaml apiVersion: apps/v1beta2 kind: Deployment metadata:name: k8shelmdemolabels:app: k8shelmdemochart: k8shelmdemo-0.1.0draft: draft-apprelease: k8s-helm-demoheritage: Helm spec:replicas: 1selector:matchLabels:app: k8shelmdemorelease: k8s-helm-demotemplate:metadata:labels:app: k8shelmdemodraft: draft-apprelease: k8s-helm-demoannotations:buildID: ""spec:containers:- name: k8shelmdemoimage: "k8shelmdemo:stable"imagePullPolicy: IfNotPresentports:- name: httpcontainerPort: 80protocol: TCPenv:resources:{}這里目前需要注意一點就是上面輸出的Deployment中使用的鏡像為 image:"k8shelmdemo:stable"。所以在安裝該Chart之前,我們需要構(gòu)造鏡像。鏡像構(gòu)造很簡單,在Vs中右鍵Dockerfile選擇構(gòu)建就好(請確保Docker已啟動)。觀察VS輸出窗口,會有以下輸出:
<code class="1 style=" font-size:="" 10px;line-height:="" 12px"="" style="box-sizing: border-box; margin-left: -20px; display: flex; overflow: initial; overflow-wrap: normal; border: 0px; font-family: inherit !important; line-height: 20px !important; white-space: pre !important;">------ Rebuild All started: Project: K8S.Helm.Demo, Configuration: Debug Any CPU ------ language-1>------ Rebuild All started: Project: K8S.Helm.Demo, Configuration: Debug Any CPU ------">1>K8S.Helm.Demo -> D:\Programming\Coding\dotnet\K8S.Ocelot.Demo\src\K8S.Helm.Demo\bin\Debug\netcoreapp3.1\K8S.Helm.Demo.dll 1>docker build -f "d:\programming\coding\dotnet\k8s.ocelot.demo\src\k8s.helm.demo\dockerfile" --force-rm -t k8shelmdemo --label "com.microsoft.created-by=visual-studio" --label "com.microsoft.visual-studio.project-name=K8S.Helm.Demo" "d:\programming\coding\dotnet\k8s.ocelot.demo\src" 1> 1>Step 2/18 : WORKDIR /app 1> ---> Using cache 1>Step 3/18 : EXPOSE 80 1> ---> bb9dc51530fe 1> ---> 0a4c7c13f9d6 1> ---> Using cache 1>Step 6/18 : COPY ["K8S.Helm.Demo/K8S.Helm.Demo.csproj", "K8S.Helm.Demo/"] 1>Step 7/18 : RUN dotnet restore "K8S.Helm.Demo/K8S.Helm.Demo.csproj" 1> Restore completed in 7.19 sec for /src/K8S.Helm.Demo/K8S.Helm.Demo.csproj. 1> ---> ba3624442138 1> ---> 43c4f6c4769f <code class="1 style=" font-size:="" 10px;line-height:="" 12px"="" style="box-sizing: border-box; margin-left: -20px; display: flex; overflow: initial; overflow-wrap: normal; border: 0px; font-family: inherit !important; line-height: 20px !important; white-space: pre !important;">1>Step 9/18 : WORKDIR "/src/K8S.Helm.Demo" 1>Removing intermediate container 145e155d3a5d <code class="1 style=" font-size:="" 10px;line-height:="" 12px"="" style="box-sizing: border-box; margin-left: -20px; display: flex; overflow: initial; overflow-wrap: normal; border: 0px; font-family: inherit !important; line-height: 20px !important; white-space: pre !important;">1>Step 10/18 : RUN dotnet build "K8S.Helm.Demo.csproj" -c Release -o /app/build 1> ---> Running in 146df981f291 1>Copyright (C) Microsoft Corporation. All rights reserved. 1> Restore completed in 27.08 ms for /src/K8S.Helm.Demo/K8S.Helm.Demo.csproj. 1>Build succeeded. 1> 1> 0 Error(s) 1>Step 11/18 : FROM build AS publish 1> ---> 94f07ad82c1c <code class="1 style=" font-size:="" 10px;line-height:="" 12px"="" style="box-sizing: border-box; margin-left: -20px; display: flex; overflow: initial; overflow-wrap: normal; border: 0px; font-family: inherit !important; line-height: 20px !important; white-space: pre !important;">1>Step 12/18 : RUN dotnet publish "K8S.Helm.Demo.csproj" -c Release -o /app/publish 1>Microsoft (R) Build Engine version 16.4.0+e901037fe for .NET Core 1>Copyright (C) Microsoft Corporation. All rights reserved. 1> K8S.Helm.Demo -> /src/K8S.Helm.Demo/bin/Release/netcoreapp3.1/K8S.Helm.Demo.dll 1>Removing intermediate container 60d63984fe28 1> ---> 85d893dc4a81 1>Step 14/18 : WORKDIR /app 1>Removing intermediate container 69b7fd56c371 1>Step 15/18 : COPY --from=publish /app/publish . <code class="1 style=" font-size:="" 10px;line-height:="" 12px"="" style="box-sizing: border-box; margin-left: -20px; display: flex; overflow: initial; overflow-wrap: normal; border: 0px; font-family: inherit !important; line-height: 20px !important; white-space: pre !important;">1>Step 16/18 : ENTRYPOINT ["dotnet", "K8S.Helm.Demo.dll"] 1> ---> Running in a43a0516c6dc 1> ---> 36f422c923fd 1> ---> Running in 88d100227ee1 1> ---> 4a71f8e5e761 1> ---> Running in f609881010ad 1> ---> 3301427c0fb8 1>Successfully tagged k8shelmdemo:latest ========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========最終構(gòu)建的鏡像名稱為 k8shelmdemo:latest。與我們上面Chart中使用的鏡像 k8shelmdemo:stable不一致。如果現(xiàn)在安裝Chart,那么應(yīng)用將無法找對應(yīng)的鏡像無法啟動。那怎么辦呢。查看 deployment.yaml模板文件,我們發(fā)現(xiàn)其鏡像引用定義為 image:"{{ .Values.image.repository }}:{{ .Values.image.tag }}"。查看 values.yaml發(fā)現(xiàn)如下定義:
image:repository: k8shelmdemotag: stablepullPolicy: IfNotPresent所以改法就很簡單了,將 values.yaml的tag更改為 latest即可。更改后在執(zhí)行 helmtemplate--debug[release name][chart dir] 驗證下。接下來通過 helm install來安裝Chart。在執(zhí)行之前,我們先通過 kubectl create ns helmdemo創(chuàng)建一個獨立的命名空間以方便確認和清理。再執(zhí)行 helm install k8shelmdemo.\k8shelmdemo\-n helmdemo 安裝(-n 指定我們剛剛創(chuàng)建的命名空間)。具體命令如下:
PS \K8S.Helm.Demo\charts> kubectl create ns helmdemo namespace/helmdemo created PS \K8S.Helm.Demo\charts> helm install k8shelmdemo .\k8shelmdemo\ -n helmdemo NAME: k8shelmdemo LAST DEPLOYED: Sun Feb 23 17:33:54 2020 NAMESPACE: helmdemo STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: 1. Get the application URL by running these commands:export POD_NAME=$(kubectl get pods --namespace helmdemo -l "app=k8shelmdemo,release=k8shelmdemo" -o jsonpath="{.items[0].metadat a.name}")echo "Visit http://127.0.0.1:8080 to use your application"kubectl port-forward $POD_NAME 8080:80 PS \K8S.Helm.Demo\charts>我們看到同時輸出了模板文件夾下的NOTES模板,這時說明helm已經(jīng)安裝了。那怎樣確保是否安裝成功了呢。繼續(xù)執(zhí)行以下命令:
PS \K8S.Helm.Demo\charts> helm list -n helmdemo #查看指定命名空間下已安裝的chart,也就是運行中的Release NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION k8shelmdemo helmdemo 1 2020-02-23 17:33:54.2196357 +0800 CST deployed k8shelmdemo-0.1.0 1.0 PS \K8S.Helm.Demo\charts> kubectl get all -n helmdemo # 查看指定命名空間下K8S下所有的對象 NAME READY STATUS RESTARTS AGE pod/k8shelmdemo-689bd54677-fcfx7 1/1 Running 0 5m8s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/k8shelmdemo ClusterIP 10.97.204.227 <none> 80/TCP 5m8s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/k8shelmdemo 1/1 1 1 5m8s NAME DESIRED CURRENT READY AGE replicaset.apps/k8shelmdemo-689bd54677 1 1 1 5m8s至此我們可以確定Chart成功安裝。那如何訪問剛剛部署的Web應(yīng)用呢,安裝剛剛Chart的安裝Notes,通過 kubectl port-forward配置端口轉(zhuǎn)發(fā),來完成。從上面的輸出我們已經(jīng)知道對應(yīng)的Pod Name 為k8shelmdemo-689bd54677-fcfx7。執(zhí)行 kubectl port-forward k8shelmdemo-689bd54677-fcfx78090:80。
PS \K8S.Helm.Demo\charts> kubectl port-forward k8shelmdemo-689bd54677-fcfx7 8090:80 -n helmdemo Forwarding from 127.0.0.1:8090 -> 80 Forwarding from [::1]:8090 -> 80換一個命令行執(zhí)行 curl-l http://localhost:8090 會看到輸出 Helloworld。
更新 Chart (helm upgrade)
假設(shè)我現(xiàn)在想將輸出更新為 HelloHelm,我們來看下怎么辦。對于當(dāng)前應(yīng)用來說,更新輸出,只需要更改Startup的 HelloWorld改為 HelloHelm 就好,然后重新構(gòu)建鏡像。這里思考一下,因為重新構(gòu)建的鏡像Tag還是 k8shelmdemo:latest,所以無需對當(dāng)前Helm Chart做任何改動,所以也就無需更新。那我們該如何更新應(yīng)用呢。如果有K8S基礎(chǔ)的同學(xué)應(yīng)該很快就能想到,直接刪除Pod即可。因為從上面 kubectlgetall-n helmdemo的輸出中,我們可以看到Chart為我們的應(yīng)用自動創(chuàng)建了一個 ReplicaSet實例,ReplicaSet主要用于確保應(yīng)用始終保持指定數(shù)量的實例運行。所以如果刪除一個Pod,K8S會按照ReplicaSet的定義,重新啟用一個新的Pod。再重新執(zhí)行 kubectl port-forward,會發(fā)現(xiàn)應(yīng)用已更新。
PS: 因為當(dāng)前demo使用的是本地鏡像,所以刪除Pod后,重新運行的pod能夠輸出更新后的結(jié)果。如果鏡像來源并非本地,那么對于同一個鏡像tag來說,就要考慮更新鏡像拉取策略。
PS \K8S.Helm.Demo\charts> kubectl delete pod/k8shelmdemo-689bd54677-fcfx7 -n helmdemo # 刪除pod PS \K8S.Helm.Demo\charts> kubectl get all -n helmdemo NAME READY STATUS RESTARTS AGE pod/k8shelmdemo-689bd54677-mrr64 1/1 Running 0 29s # 新的Pod創(chuàng)建成功 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/k8shelmdemo ClusterIP 10.97.204.227 <none> 80/TCP 33m NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/k8shelmdemo 1/1 1 1 33m NAME DESIRED CURRENT READY AGE replicaset.apps/k8shelmdemo-689bd54677 1 1 1 33m假設(shè)現(xiàn)在需要服務(wù)部署后直接通過指定端口訪問,無需通過 kubeclt port-forward進行端口轉(zhuǎn)發(fā)。那么我們就需要對Chart進行相應(yīng)更新,將生成的Service的類型由默認的 Cluster更改為 LoadBalancer模式。更新 values.yaml中的service節(jié)點如下:
service:type: LoadBalancerport: 8093緊接著通過執(zhí)行 helm upgrade[release name][chart dir]命令更新應(yīng)用,如下。
PS \K8S.Helm.Demo\charts> helm upgrade k8shelmdemo .\k8shelmdemo\ -n helmdemo Release "k8shelmdemo" has been upgraded. Happy Helming! NAME: k8shelmdemo LAST DEPLOYED: Sun Feb 23 18:39:30 2020 NAMESPACE: helmdemo STATUS: deployed REVISION: 3 TEST SUITE: None NOTES: 1. Get the application URL by running these commands:NOTE: It may take a few minutes for the LoadBalancer IP to be available.You can watch the status of by running 'kubectl get svc -w k8shelmdemo'export SERVICE_IP=$(kubectl get svc --namespace helmdemo k8shelmdemo -o jsonpath='{.status.loadBalancer.ingress[0].ip}')echo http://$SERVICE_IP:8093 PS \K8S.Helm.Demo\charts> kubectl get all -n helmdemo NAME READY STATUS RESTARTS AGE pod/k8shelmdemo-689bd54677-pj5pd 1/1 Running 0 22m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/k8shelmdemo LoadBalancer 10.97.204.227 localhost 8093:30035/TCP 65m NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/k8shelmdemo 1/1 1 1 65m NAME DESIRED CURRENT READY AGE replicaset.apps/k8shelmdemo-689bd54677 1 1 1 65m PS \K8S.Ocelot.Demo\src\K8S.Helm.Demo\charts> curl -l localhost:8093 Hello Helm! PS \K8S.Helm.Demo\charts> helm ls -n helmdemo # 版本已更新 NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION k8shelmdemo helmdemo 2 2020-02-23 18:39:30.2059395 +0800 CST deployed k8shelmdemo-0.1.0 1.0刪除 Chart(helm delete)
演示完畢,那如何刪除已發(fā)布的Release呢,執(zhí)行 helmdeletek8shelmdemo-n helmdemo。
PS \K8S.Helm.Demo\charts> helm delete k8shelmdemo -n helmdemo release "k8shelmdemo" uninstalled PS \K8S.Helm.Demo\charts> helm ls -n helmdemo NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION PS \K8S.Helm.Demo\charts> kubectl get all -n helmdemo No resources found.執(zhí)行后,可以發(fā)現(xiàn)創(chuàng)建的K8S資源也已清理干凈。
最后
以上僅是對 ASP.NET Core 如何使用 Helm 部署到K8S的簡單介紹,希望對入門的你有所幫助!對于Helm復(fù)雜的應(yīng)用,主要在于模板填充的復(fù)雜應(yīng)用,大家可以結(jié)合官方Helm文檔以及eShopOnContainer中Helm示例進行學(xué)習(xí)。
參考資料:
Get started with Visual Studio Kubernetes Tools
玩K8S不得不會的HELM
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的ASP.NET Core 借助 Helm 部署应用至 K8S的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【视频教程】使用 ASP.NET Cor
- 下一篇: .Net Core中IOC容器的使用