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。

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的詳細資料
- 例如,這會提供建立 Kubernetes 叢集
如需 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」(或網頁預覽設定使用的任何通訊埠)。

系統會開啟瀏覽器視窗,網址如下:
https://8080-cs-754738286554-default.us-central1.cloudshell.dev/
使用網頁預覽功能查看 .NET 範例應用程式
現在,您可以啟動網頁預覽並載入提供的網址,查看上一步啟動的範例應用程式。如下所示:

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 中開啟新分頁,然後執行 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