在 Google Kubernetes Engine 中部署及更新 .NET Core 應用程式

1. 總覽

Microsoft .NET Core 是開放原始碼的跨平台 .NET 版本,可在容器中原生執行。.NET Core 可在 GitHub 取得,並由 Microsoft 和 .NET 社群維護。本實驗室會將容器化 .NET Core 應用程式部署至 Google Kubernetes Engine (GKE)。

本實驗室採用典型的開發模式,也就是在開發人員的本機環境中開發應用程式,然後部署至正式環境。在實驗室的第一部分,您會使用 Cloud Shell 中執行的容器,驗證範例 .NET Core 應用程式。驗證完成後,應用程式會透過 GKE 部署至 Kubernetes。實驗室中包含建立 GKE 叢集的步驟。

在實驗室的第二部分,您會對應用程式進行微幅調整,顯示執行該應用程式執行個體的容器主機名稱。接著,在 Cloud Shell 中驗證更新後的應用程式,並更新部署作業,改用新版本。下圖顯示本實驗室的活動順序:

示範流程圖

費用

如果完全按照指示進行本實驗室,下列服務將收取正常費用

2. 設定和需求

必要條件

您需要 Google Cloud 帳戶和專案,才能完成這個實驗室。如需建立新專案的詳細操作說明,請參閱這個程式碼實驗室。

本實驗室會使用在 Cloud Shell 中執行的 Docker。Cloud Shell 可透過 Google Cloud 控制台存取,並預先設定許多實用工具,例如 gcloud 和 Docker。存取 Cloud Shell 的方式如下所示。按一下右上角的 Cloud Shell 圖示,控制台視窗底部就會顯示 Cloud Shell。

Cloud Shell

GKE 叢集的其他設定選項 (選用)

本實驗室需要 Kubernetes 叢集。在下一節中,您將建立設定簡單的 GKE 叢集。本節介紹一些 gcloud 指令,可提供替代設定選項,用於透過 GKE 建構 Kubernetes 叢集。舉例來說,您可以使用下列指令,找出不同的機器類型、可用區,甚至是 GPU (加速器)。

  • 使用下列指令列出機型:gcloud compute machine-types list
  • 使用下列指令列出 GPU:gcloud compute accelerator-types list
  • 使用下列指令列出運算可用區:gcloud compute zones list
  • 取得任何 gcloud 指令的說明 gcloud container clusters --help
    • 例如,這會提供建立 Kubernetes 叢集 gcloud container clusters create --help 的詳細資料

如需 GKE 設定選項的完整清單,請參閱這份文件

準備建立 Kubernetes 叢集

在 Cloud Shell 中,您必須設定一些環境變數,並設定 gcloud 用戶端。請執行下列指令來完成這項操作。

export PROJECT_ID=YOUR_PROJECT_ID
export DEFAULT_ZONE=us-central1-c

gcloud config set project ${PROJECT_ID}
gcloud config set compute/zone ${DEFAULT_ZONE}

建立 GKE 叢集

本實驗室會在 Kubernetes 上部署 .NET Core 應用程式,因此必須建立叢集。使用下列指令,透過 GKE 在 Google Cloud 建立新的 Kubernetes 叢集。

gcloud container clusters create dotnet-cluster \
  --zone ${DEFAULT_ZONE} \
  --num-nodes=1 \
  --node-locations=${DEFAULT_ZONE},us-central1-b \
  --enable-stackdriver-kubernetes \
  --machine-type=n1-standard-1 \
  --workload-pool=${PROJECT_ID}.svc.id.goog \
  --enable-ip-alias
  • --num-nodes 是每個可用區要新增的節點數量,之後可以調整
  • --node-locations 是以半形逗號分隔的可用區清單。在本例中,系統會使用您在上述環境變數中指定的可用區和 us-central1-b
    • 注意:這份清單不得包含重複項目
  • --workload-pool 會建立 Workload Identity,讓 GKE 工作負載存取 Google Cloud 服務

叢集建構期間,系統會顯示下列內容

Creating cluster dotnet-cluster in us-central1-b... Cluster is being deployed...⠼

設定 kubectl

kubectl CLI 是與 Kubernetes 叢集互動的主要方式。如要搭配剛建立的新叢集使用,則須設定 kubectl,以便對叢集進行驗證。請執行下列指令。

$ gcloud container clusters get-credentials dotnet-cluster --zone ${DEFAULT_ZONE}
Fetching cluster endpoint and auth data.
kubeconfig entry generated for dotnet-cluster.

現在應該可以使用 kubectl 與叢集互動。

$ kubectl get nodes
NAME                                            STATUS   ROLES    AGE     VERSION
gke-dotnet-cluster-default-pool-02c9dcb9-fgxj   Ready    <none>   2m15s   v1.16.13-gke.401
gke-dotnet-cluster-default-pool-ed09d7b7-xdx9   Ready    <none>   2m24s   v1.16.13-gke.401

3. 在本機測試並確認所需功能

本實驗室會使用 Docker Hub 官方 .NET 存放區中的下列容器映像檔。

在本機執行容器,驗證功能

在 Cloud Shell 執行下列 Docker 指令,確認 Docker 已啟動並正常運作,且 .NET 容器運作正常:

$ docker run --rm mcr.microsoft.com/dotnet/samples

      Hello from .NET!
      __________________
                        \
                        \
                            ....
                            ....'
                            ....
                          ..........
                      .............'..'..
                  ................'..'.....
                .......'..........'..'..'....
                ........'..........'..'..'.....
              .'....'..'..........'..'.......'.
              .'..................'...   ......
              .  ......'.........         .....
              .                           ......
              ..    .            ..        ......
            ....       .                 .......
            ......  .......          ............
              ................  ......................
              ........................'................
            ......................'..'......    .......
          .........................'..'.....       .......
      ........    ..'.............'..'....      ..........
    ..'..'...      ...............'.......      ..........
    ...'......     ...... ..........  ......         .......
  ...........   .......              ........        ......
  .......        '...'.'.              '.'.'.'         ....
  .......       .....'..               ..'.....
    ..       ..........               ..'........
            ............               ..............
          .............               '..............
          ...........'..              .'.'............
        ...............              .'.'.............
        .............'..               ..'..'...........
        ...............                 .'..............
        .........                        ..............
          .....
  
Environment:
.NET 5.0.1-servicing.20575.16
Linux 5.4.58-07649-ge120df5deade #1 SMP PREEMPT Wed Aug 26 04:56:33 PDT 2020

確認網頁應用程式功能

您也可以在 Cloud Shell 中驗證範例網頁應用程式。下方的 Docker run 指令會建立新的容器,公開通訊埠 80,並將其對應至 localhost 通訊埠 8080。請注意,在本例中,localhost 位於 Cloud Shell。

$ docker run -it --rm -p 8080:80 --name aspnetcore_sample mcr.microsoft.com/dotnet/samples:aspnetapp
warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
      Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
      No XML encryptor configured. Key {64a3ed06-35f7-4d95-9554-8efd38f8b5d3} may be persisted to storage in unencrypted form.
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://[::]:80
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /app

由於這是網頁應用程式,因此必須在網路瀏覽器中查看及驗證。下一節將說明如何在 Cloud Shell 中使用網頁預覽功能。

4. 使用「網頁預覽」功能,透過 Cloud Shell 存取服務

Cloud Shell 提供網頁預覽功能,可讓您使用瀏覽器與 Cloud Shell 執行個體中執行的程序互動。

使用「網頁預覽」在 Cloud Shell 中查看應用程式

在 Cloud Shell 中,按一下網頁預覽按鈕,然後選擇「透過以下通訊埠預覽:8080」(或網頁預覽設定使用的任何通訊埠)。

Cloud Shell

系統會開啟瀏覽器視窗,網址如下:

https://8080-cs-754738286554-default.us-central1.cloudshell.dev/

使用網頁預覽功能查看 .NET 範例應用程式

現在,您可以啟動網頁預覽並載入提供的網址,查看上一步啟動的範例應用程式。如下所示:

.NET 應用程式 V1 的螢幕截圖

5. 部署至 Kubernetes

建構 YAML 檔案並套用

下一步需要 YAML 檔案,描述 Deployment 和 Service 這兩項 Kubernetes 資源。在 Cloud Shell 中建立名為 dotnet-app.yaml 的檔案,並在其中加入下列內容。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dotnet-deployment
  labels:
    app: dotnetapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: dotnetapp
  template:
    metadata:
      labels:
        app: dotnetapp
    spec:
      containers:
      - name: dotnet
        image: mcr.microsoft.com/dotnet/samples:aspnetapp
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: dotnet-service
spec:
    selector:
      app: dotnetapp
    ports:
      - protocol: TCP
        port: 8080
        targetPort: 80

現在,請使用 kubectl 將這個檔案套用至 Kubernetes。

$ kubectl apply -f dotnet-app.yaml
deployment.apps/dotnet-deployment created
service/dotnet-service created

請注意,訊息會指出已建立所需資源。

探索產生的資源

我們可以使用 kubectl CLI 檢查上述建立的資源。首先,查看 Deployment 資源,確認新的 Deployment 是否存在。

$ kubectl get deployment
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
dotnet-deployment   3/3     3            3           80s

接著,請查看 ReplicaSet。上述 Deployment 應會建立 ReplicaSet。

$ kubectl get replicaset
NAME                           DESIRED   CURRENT   READY   AGE
dotnet-deployment-5c9d4cc4b9   3         3         3       111s

最後,請查看 Pod。Deployment 指出應有三個執行個體。下列指令應會顯示三個執行個體。新增 -o wide 選項,即可顯示執行這些執行個體的節點。

$ kubectl get pod -o wide
NAME                                 READY   STATUS    RESTARTS   AGE     IP          NODE                                            NOMINATED NODE   READINESS GATES
dotnet-deployment-5c9d4cc4b9-cspqd   1/1     Running   0          2m25s   10.16.0.8   gke-dotnet-cluster-default-pool-ed09d7b7-xdx9   <none>           <none>
dotnet-deployment-5c9d4cc4b9-httw6   1/1     Running   0          2m25s   10.16.1.7   gke-dotnet-cluster-default-pool-02c9dcb9-fgxj   <none>           <none>
dotnet-deployment-5c9d4cc4b9-vvdln   1/1     Running   0          2m25s   10.16.0.7   gke-dotnet-cluster-default-pool-ed09d7b7-xdx9   <none>           <none>

查看 Service 資源

Kubernetes 中的服務資源是負載平衡器。端點是由 Pod 上的標籤決定。這樣一來,只要透過上述 kubectl scale deployment 作業將新的 Pod 新增至 Deployment,產生的 Pod 就能立即處理該 Service 處理的要求。

下列指令應會顯示 Service 資源。

$ kubectl get svc
NAME             TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
dotnet-service   ClusterIP   10.20.9.124   <none>        8080/TCP   2m50s
...

您可以使用下列指令,查看 Service 的詳細資料。

$ kubectl describe svc dotnet-service
Name:              dotnet-service
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=dotnetapp
Type:              ClusterIP
IP:                10.20.9.124
Port:              <unset>  8080/TCP
TargetPort:        80/TCP
Endpoints:         10.16.0.7:80,10.16.0.8:80,10.16.1.7:80
Session Affinity:  None
Events:            <none>

請注意,Service 的類型為 ClusterIP。也就是說,叢集內的任何 Pod 都能將 Service 名稱 dotnet-service 解析為其 IP 位址。傳送至服務的要求會在所有執行個體 (Pod) 之間進行負載平衡。上方的 Endpoints 值顯示目前可用於這項服務的 Pod IP。將這些 IP 與上方輸出的 Pod IP 進行比較。

驗證執行中的應用程式

此時,應用程式已上線,可處理使用者要求。如要存取,請使用 Proxy。下列指令會建立本機 Proxy,接受通訊埠 8080 的要求,並將要求傳遞至 Kubernetes 叢集。

$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080

現在請使用 Cloud Shell 的網頁預覽功能,存取網頁應用程式。

在網頁預覽產生的網址中加入 /api/v1/namespaces/default/services/dotnet-service:8080/proxy/。結果應如下所示:

https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/

恭喜!您已成功在 Google Kubernetes Engine 部署 .NET Core 應用程式。接著,我們會變更並重新部署應用程式。

6. 修改應用程式

在本節中,您將修改應用程式,顯示執行個體運作的主機。這樣就能確認負載平衡功能是否正常運作,以及可用的 Pod 是否如預期回應。

取得原始碼

git clone https://github.com/dotnet/dotnet-docker.git
cd dotnet-docker/samples/aspnetapp/

更新應用程式,加入主機名稱

vi aspnetapp/Pages/Index.cshtml
    <tr>
        <td>Host</td>
        <td>@Environment.MachineName</td>
    </tr>

建構新的容器映像檔,並在本機測試

使用更新後的程式碼建構新的容器映像檔。

docker build --pull -t aspnetapp:alpine -f Dockerfile.alpine-x64 .

和之前一樣,在本機測試新應用程式

$ docker run --rm -it -p 8080:80 aspnetapp:alpine
warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
      Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
      No XML encryptor configured. Key {f71feb13-8eae-4552-b4f2-654435fff7f8} may be persisted to storage in unencrypted form.
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://[::]:80
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /app

和之前一樣,您可以使用網頁預覽功能存取應用程式。這次應該會看到 Host 參數,如下所示:

Cloud Shell

在 Cloud Shell 中開啟新分頁,然後執行 docker ps,確認容器 ID 與上方顯示的主機值相符。

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
ab85ce11aecd        aspnetapp:alpine    "./aspnetapp"       2 minutes ago       Up 2 minutes        0.0.0.0:8080->80/tcp   relaxed_northcutt

標記並推送映像檔,以便 Kubernetes 使用

映像檔必須加上標記並推送,Kubernetes 才能提取。首先,列出容器映像檔並找出所需映像檔。

$ docker image list
REPOSITORY                                         TAG                 IMAGE ID            CREATED             SIZE
aspnetapp                                          alpine              95b4267bb6d0        6 days ago          110MB

接著,為該映像檔加上標記,並推送至 Google Container Registry。使用上述映像檔 ID,看起來會像這樣

docker tag 95b4267bb6d0 gcr.io/${PROJECT_ID}/aspnetapp:alpine
docker push gcr.io/${PROJECT_ID}/aspnetapp:alpine

7. 重新部署更新後的應用程式

編輯 YAML 檔案

切換回儲存 dotnet-app.yaml 檔案的目錄。在 YAML 檔案中找出以下這行程式碼

        image: mcr.microsoft.com/dotnet/core/samples:aspnetapp

您必須變更這項設定,參照先前建立並推送至 gcr.io 的容器映像檔。

        image: gcr.io/PROJECT_ID/aspnetapp:alpine

別忘了修改程式碼,改用您的 PROJECT_ID。完成後,畫面應如下所示

        image: gcr.io/myproject/aspnetapp:alpine

套用更新後的 YAML 檔案

$ kubectl apply -f dotnet-app.yaml
deployment.apps/dotnet-deployment configured
service/dotnet-service unchanged

請注意,Deployment 資源顯示為「已更新」,Service 資源則顯示為「未變更」。您可以使用 kubectl get pod 指令查看更新後的 Pod,但這次我們會新增 -w,監控所有變更。

$ kubectl get pod -w
NAME                                 READY   STATUS              RESTARTS   AGE
dotnet-deployment-5c9d4cc4b9-cspqd   1/1     Running             0          34m
dotnet-deployment-5c9d4cc4b9-httw6   1/1     Running             0          34m
dotnet-deployment-5c9d4cc4b9-vvdln   1/1     Running             0          34m
dotnet-deployment-85f6446977-tmbdq   0/1     ContainerCreating   0          4s
dotnet-deployment-85f6446977-tmbdq   1/1     Running             0          5s
dotnet-deployment-5c9d4cc4b9-vvdln   1/1     Terminating         0          34m
dotnet-deployment-85f6446977-lcc58   0/1     Pending             0          0s
dotnet-deployment-85f6446977-lcc58   0/1     Pending             0          0s
dotnet-deployment-85f6446977-lcc58   0/1     ContainerCreating   0          0s
dotnet-deployment-5c9d4cc4b9-vvdln   0/1     Terminating         0          34m
dotnet-deployment-85f6446977-lcc58   1/1     Running             0          6s
dotnet-deployment-5c9d4cc4b9-cspqd   1/1     Terminating         0          34m
dotnet-deployment-85f6446977-hw24v   0/1     Pending             0          0s
dotnet-deployment-85f6446977-hw24v   0/1     Pending             0          0s
dotnet-deployment-5c9d4cc4b9-cspqd   0/1     Terminating         0          34m
dotnet-deployment-5c9d4cc4b9-vvdln   0/1     Terminating         0          34m
dotnet-deployment-5c9d4cc4b9-vvdln   0/1     Terminating         0          34m
dotnet-deployment-85f6446977-hw24v   0/1     Pending             0          2s
dotnet-deployment-85f6446977-hw24v   0/1     ContainerCreating   0          2s
dotnet-deployment-5c9d4cc4b9-cspqd   0/1     Terminating         0          34m
dotnet-deployment-5c9d4cc4b9-cspqd   0/1     Terminating         0          34m
dotnet-deployment-85f6446977-hw24v   1/1     Running             0          3s
dotnet-deployment-5c9d4cc4b9-httw6   1/1     Terminating         0          34m
dotnet-deployment-5c9d4cc4b9-httw6   0/1     Terminating         0          34m

上述輸出內容會顯示滾動式更新的過程。首先,系統會啟動新的容器,並在容器執行時終止舊容器。

驗證執行中的應用程式

此時,應用程式已更新,可以處理使用者要求。和之前一樣,您可以透過 Proxy 存取。

$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080

現在請使用 Cloud Shell 的網頁預覽功能,存取網頁應用程式。

在網頁預覽產生的網址中加入 /api/v1/namespaces/default/services/dotnet-service:8080/proxy/。結果應如下所示:

https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/

確認 Kubernetes Service 正在分配負載

重新整理這個網址幾次,您會發現主機有所變更,因為 Service 會在不同 Pod 之間平衡負載。將主機值與上方的 Pod 清單進行比較,確認所有 Pod 都有接收流量。

向上擴充執行個體

在 Kubernetes 中調度應用程式資源非常簡單。下列指令會將 Deployment 擴充至 6 個應用程式執行個體。

$ kubectl scale deployment dotnet-deployment --replicas 6
deployment.apps/dotnet-deployment scaled

您可以使用下列指令,查看新的 Pod 和目前狀態

kubectl get pod -w

請注意,重新整理同一個瀏覽器視窗後,流量現在會平衡分配到所有新的 Pod。

8. 恭喜!

在本實驗室中,您在開發環境中驗證 .NET Core 範例網頁應用程式,並使用 GKE 將其部署至 Kubernetes。接著,我們修改了應用程式,讓它顯示執行容器的主機名稱。接著,您將 Kubernetes Deployment 更新至新版本,並擴充應用程式,瞭解負載如何分配至其他執行個體。

如要進一步瞭解 .NET 和 Kubernetes,請參閱這些教學課程。這些實驗室會以本實驗室學到的知識為基礎,介紹 Istio 服務網格,讓您瞭解更複雜的轉送和韌性模式。

9. 清除

為避免產生不必要的費用,請使用下列指令,刪除在本實驗室建立的叢集和容器映像檔。

gcloud container clusters delete dotnet-cluster --zone ${DEFAULT_ZONE}
gcloud container images delete gcr.io/${PROJECT_ID}/aspnetapp:alpine