ASP.NET Core 借助 Helm 部署應用至K8S

前言

玩K8S也有一段時間了,借助云服務提供商的K8S控制臺,已經可以很方便的快速部署應用至K8S。通過簡單的點擊,可以一次性幫忙創建K8S 對象:Deployment、Service、Ingress、ConfigMap等。但是當服務的規模上來后,這種方式就有點捉襟見肘。尤其是需要同時更新多個關聯服務時,就需要一個一個的去更改,就有點不太方便。為了解決這個問題,最近上手實操了一下Helm,發現生產力大大提升。

Helm 簡介

Helm 是一個為K8S打造的包管理器。通過Helm可以方便管理Kubernetes應用程序。Helm主要有兩大核心概念:Charts、Release。

  1. Chart:用來定義,安裝和升級K8S 應用。亦可分享及版本化控制。
  2. 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 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目錄,通過helm template --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: k8shelmdemo
  labels:
    app: k8shelmdemo
    chart: k8shelmdemo-0.1.0
    release: k8s-helm-demo
    heritage: Helm
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app: k8shelmdemo
    release: k8s-helm-demo
---
# Source: k8shelmdemo/templates/deployment.yaml
apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: k8shelmdemo
  labels:
    app: k8shelmdemo
    chart: k8shelmdemo-0.1.0
    draft: draft-app
    release: k8s-helm-demo
    heritage: Helm
spec:
  replicas: 1
  selector:
    matchLabels:
      app: k8shelmdemo
      release: k8s-helm-demo
  template:
    metadata:
      labels:
        app: k8shelmdemo
        draft: draft-app
        release: k8s-helm-demo
      annotations:
        buildID: ""
    spec:
      containers:
        - name: k8shelmdemo
          image: "k8shelmdemo:stable"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          env:
          resources:
            {}

這里目前需要注意一點就是上面輸出的Deployment中使用的鏡像為image: "k8shelmdemo:stable"。所以在安裝該Chart之前,我們需要構造鏡像。鏡像構造很簡單,在Vs中右鍵Dockerfile選擇構建就好(請確保Docker已啟動)。

build docker image

觀察VS輸出窗口,會有以下輸出:

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 version 19.03.1, build 74b1e89
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>Sending build context to Docker daemon  6.192MB
1>
1>Step 1/18 : FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
1>Step 2/18 : WORKDIR /app
1> ---> e28362768eed
1> ---> Using cache
1> ---> 6457841dbdf1
1>Step 3/18 : EXPOSE 80
1> ---> Using cache
1> ---> bb9dc51530fe
1>Step 4/18 : FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
1> ---> 0a4c7c13f9d6
1>Step 5/18 : WORKDIR /src
1> ---> Using cache
1> ---> ddc36742b71c
1>Step 6/18 : COPY ["K8S.Helm.Demo/K8S.Helm.Demo.csproj", "K8S.Helm.Demo/"]
1> ---> d18831c83acd
1>Step 7/18 : RUN dotnet restore "K8S.Helm.Demo/K8S.Helm.Demo.csproj"
1> ---> Running in 64d3624eb9c0
1>  Restore completed in 7.19 sec for /src/K8S.Helm.Demo/K8S.Helm.Demo.csproj.
1>Removing intermediate container 64d3624eb9c0
1> ---> ba3624442138
1>Step 8/18 : COPY . .
1> ---> 43c4f6c4769f
1>Step 9/18 : WORKDIR "/src/K8S.Helm.Demo"
1> ---> Running in 145e155d3a5d
1>Removing intermediate container 145e155d3a5d
1>Step 10/18 : RUN dotnet build "K8S.Helm.Demo.csproj" -c Release -o /app/build
1> ---> e547e8caed4a
1> ---> Running in 146df981f291
1>Microsoft (R) Build Engine version 16.4.0+e901037fe for .NET Core
1>Copyright (C) Microsoft Corporation. All rights reserved.
1>
1>  Restore completed in 27.08 ms for /src/K8S.Helm.Demo/K8S.Helm.Demo.csproj.
1>  K8S.Helm.Demo -> /app/build/K8S.Helm.Demo.dll
1>Build succeeded.
1>    0 Warning(s)
1>
1>Time Elapsed 00:00:02.09
1>    0 Error(s)
1>Removing intermediate container 146df981f291
1>Step 11/18 : FROM build AS publish
1> ---> 94f07ad82c1c
1> ---> 94f07ad82c1c
1>Step 12/18 : RUN dotnet publish "K8S.Helm.Demo.csproj" -c Release -o /app/publish
1> ---> Running in 60d63984fe28
1>Microsoft (R) Build Engine version 16.4.0+e901037fe for .NET Core
1>
1>Copyright (C) Microsoft Corporation. All rights reserved.
1>  Restore completed in 26.94 ms for /src/K8S.Helm.Demo/K8S.Helm.Demo.csproj.
1>  K8S.Helm.Demo -> /src/K8S.Helm.Demo/bin/Release/netcoreapp3.1/K8S.Helm.Demo.dll
1>  K8S.Helm.Demo -> /app/publish/
1>Removing intermediate container 60d63984fe28
1>Step 13/18 : FROM base AS final
1> ---> 85d893dc4a81
1> ---> bb9dc51530fe
1>Step 14/18 : WORKDIR /app
1> ---> Running in 69b7fd56c371
1>Removing intermediate container 69b7fd56c371
1> ---> 219310025c54
1>Step 15/18 : COPY --from=publish /app/publish .
1>Step 16/18 : ENTRYPOINT ["dotnet", "K8S.Helm.Demo.dll"]
1> ---> 6e63a4449dbb
1> ---> Running in a43a0516c6dc
1>Removing intermediate container a43a0516c6dc
1> ---> 36f422c923fd
1>Step 17/18 : LABEL com.microsoft.created-by=visual-studio
1> ---> Running in 88d100227ee1
1>Removing intermediate container 88d100227ee1
1> ---> 4a71f8e5e761
1>Step 18/18 : LABEL com.microsoft.visual-studio.project-name=K8S.Helm.Demo
1> ---> Running in f609881010ad
1>Removing intermediate container f609881010ad
1> ---> 3301427c0fb8
1>Successfully built 3301427c0fb8
1>Successfully tagged k8shelmdemo:latest
1>SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.
========== 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: k8shelmdemo
  tag: stable
  pullPolicy: IfNotPresent

所以改法就很簡單了,將values.yaml的tag更改為latest即可。更改后在執行helm template --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-fcfx7 8090: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 會看到輸出Hello world

更新 Chart (helm upgrade)

假設我現在想將輸出更新為Hello Helm,我們來看下怎么辦。對于當前應用來說,更新輸出,只需要更改Startup的Hello World改為Hello Helm 就好,然后重新構建鏡像。
這里思考一下,因為重新構建的鏡像Tag還是k8shelmdemo:latest,所以無需對當前Helm Chart做任何改動,所以也就無需更新。那我們該如何更新應用呢。如果有K8S基礎的同學應該很快就能想到,直接刪除Pod即可。因為從上面kubectl get all -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: LoadBalancer
  port: 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呢,執行helm delete k8shelmdemo -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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 介紹 Helm 是 Deis 開發的一個用于 Kubernetes 應用的包管理工具,主要用來管理 Charts。...
    小波同學閱讀 1,455評論 0 4
  • 概覽 ? Helm 簡介 ? Helm 安裝使用 ? Helm 的基本使用 ? Helm 模板詳解之內置函數與 V...
    51reboot閱讀 2,511評論 0 2
  • Helm 是 Kubernetes 的軟件包管理工具。本文需要讀者對 Docker、Kubernetes 等相關知...
    guoweikuang閱讀 86,820評論 3 86
  • 自從有了小喵,總感覺身體被掏空。生活倒也沒翻天覆地,工作上的焦慮轉變成養育小喵的壓力。時刻得記掛著它,怕他它冷...
    你是自由幸福的閱讀 159評論 0 0
  • 《2017-2018》 如果過了今天就來到2018 你有什么話要對2017表達 今年的你是否和去年一樣 坐在某個角...
    葉威閱讀 169評論 0 0