ASP.NET Core 借助 Helm 部署应用至 K8S
前言
玩K8S也有一段時間了,借助云服務提供商的K8S控制臺,已經可以很方便的快速部署應用至K8S。通過簡單的點擊,可以一次性幫忙創建K8S 對象:Deployment、Service、Ingress、ConfigMap等。但是當服務的規模上來后,這種方式就有點捉襟見肘。尤其是需要同時更新多個關聯服務時,就需要一個一個的去更改,就有點不太方便。為了解決這個問題,最近上手實操了一下Helm,發現生產力大大提升。
Helm 簡介
Helm 是一個為K8S打造的包管理器。通過Helm可以方便管理Kubernetes應用程序。Helm主要有兩大核心概念:Charts、Release。
Chart:用來定義,安裝和升級K8S 應用。亦可分享及版本化控制。
Release:類似Image之于Container,Release是Chart的運行實例。
目前Helm最新的版本為V3.1,較之前版本,在整體架構上移除服務端Tiller。對于Windows系統而言可借助Choco快速安裝:choco install kubernetes-helm,通過執行 helm version確認是否安裝成功。
version.BuildInfo{Version:"v3.1.0",GitCommit:"b29d20baf09943e134c2fa5e1e1cab3bf93315fa",GitTreeState:"clean",GoVersion:"go1.13.7"}
在繼續往前,請確保已具備基礎的K8S基礎知識,并且確保本機已安裝Docker和K8S。安裝教程和K8S簡單入門可參考我的這篇文章:ASP.NET Core 借助 K8S 玩轉容器編排。
對于第一次接觸Helm .NETer 來說我們可以通過VS 2019來快速體驗一下。請確保已安裝Visual Studio Tools for Kubernetes。
創建 Chart (helm create)
打開VS 創建項目,選擇Container Application for Kubernetes,創建一個空的ASP.NET Core Web 項目。創建后,項目結構如下圖所示,與平時之間創建的Web項目而言,主要是多了一個 charts目錄、 Dockerfile和一個 azds.yaml。除了創建項目時通過選擇Container Application for Kubernetes類型外,我們也可以通過其他方式創建。我們這里手動刪除 charts目錄、 Dockerfile和一個 azds.yaml。然后如下圖步驟即可重新生成Helm Chart。
當然也可以通過 helm create創建。
安裝 Chart (helm install)
在展開之前,先來簡要介紹Chart目錄:
k8shelmdemo/ # Chart 目錄 ├── charts # 這個 charts 依賴的其他 charts,始終被安裝 ├── Chart.yaml # 描述這個 Chart 的相關信息、包括名字、描述信息、版本等 ├── 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 # 模板的值文件,這些值會在安裝時應用到 GO 模板生成部署文件簡單來說,Helm Chart 定義常用的K8S 對象模板,通過values.yaml來填充模板。那我們就來看看填充后的輸出結果是怎樣的。打開命令提示符,進入到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之前,我們需要構造鏡像。鏡像構造很簡單,在Vs中右鍵Dockerfile選擇構建就好(請確保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 ==========最終構建的鏡像名稱為 k8shelmdemo:latest。與我們上面Chart中使用的鏡像 k8shelmdemo:stable不一致。如果現在安裝Chart,那么應用將無法找對應的鏡像無法啟動。那怎么辦呢。查看 deployment.yaml模板文件,我們發現其鏡像引用定義為 image:"{{ .Values.image.repository }}:{{ .Values.image.tag }}"。查看 values.yaml發現如下定義:
image:repository: k8shelmdemotag: stablepullPolicy: IfNotPresent所以改法就很簡單了,將 values.yaml的tag更改為 latest即可。更改后在執行 helmtemplate--debug[release name][chart dir] 驗證下。接下來通過 helm install來安裝Chart。在執行之前,我們先通過 kubectl create ns helmdemo創建一個獨立的命名空間以方便確認和清理。再執行 helm install k8shelmdemo.\k8shelmdemo\-n helmdemo 安裝(-n 指定我們剛剛創建的命名空間)。具體命令如下:
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已經安裝了。那怎樣確保是否安裝成功了呢。繼續執行以下命令:
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應用呢,安裝剛剛Chart的安裝Notes,通過 kubectl port-forward配置端口轉發,來完成。從上面的輸出我們已經知道對應的Pod Name 為k8shelmdemo-689bd54677-fcfx7。執行 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換一個命令行執行 curl-l http://localhost:8090 會看到輸出 Helloworld。
更新 Chart (helm upgrade)
假設我現在想將輸出更新為 HelloHelm,我們來看下怎么辦。對于當前應用來說,更新輸出,只需要更改Startup的 HelloWorld改為 HelloHelm 就好,然后重新構建鏡像。這里思考一下,因為重新構建的鏡像Tag還是 k8shelmdemo:latest,所以無需對當前Helm Chart做任何改動,所以也就無需更新。那我們該如何更新應用呢。如果有K8S基礎的同學應該很快就能想到,直接刪除Pod即可。因為從上面 kubectlgetall-n helmdemo的輸出中,我們可以看到Chart為我們的應用自動創建了一個 ReplicaSet實例,ReplicaSet主要用于確保應用始終保持指定數量的實例運行。所以如果刪除一個Pod,K8S會按照ReplicaSet的定義,重新啟用一個新的Pod。再重新執行 kubectl port-forward,會發現應用已更新。
PS: 因為當前demo使用的是本地鏡像,所以刪除Pod后,重新運行的pod能夠輸出更新后的結果。如果鏡像來源并非本地,那么對于同一個鏡像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創建成功 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假設現在需要服務部署后直接通過指定端口訪問,無需通過 kubeclt port-forward進行端口轉發。那么我們就需要對Chart進行相應更新,將生成的Service的類型由默認的 Cluster更改為 LoadBalancer模式。更新 values.yaml中的service節點如下:
service:type: LoadBalancerport: 8093緊接著通過執行 helm upgrade[release name][chart dir]命令更新應用,如下。
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)
演示完畢,那如何刪除已發布的Release呢,執行 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.執行后,可以發現創建的K8S資源也已清理干凈。
最后
以上僅是對 ASP.NET Core 如何使用 Helm 部署到K8S的簡單介紹,希望對入門的你有所幫助!對于Helm復雜的應用,主要在于模板填充的復雜應用,大家可以結合官方Helm文檔以及eShopOnContainer中Helm示例進行學習。
參考資料:
Get started with Visual Studio Kubernetes Tools
玩K8S不得不會的HELM
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的ASP.NET Core 借助 Helm 部署应用至 K8S的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【视频教程】使用 ASP.NET Cor
- 下一篇: .Net Core中IOC容器的使用