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,該工具可透過 Google Cloud 控制台取得,並已預先設定許多實用工具,例如 gcloud 和 Docker。以下顯示如何存取 Cloud Shell。點選右上角的 Cloud Shell 圖示,控制台視窗的底部窗格就會顯示該圖示。
GKE 叢集的替代設定選項 (選用)
本研究室需要 Kubernetes 叢集。在下一節中,您將建立設定簡單的 GKE 叢集。本節介紹了一些 gcloud
指令,提供替代設定選項,以便在透過 GKE 建構 Kubernetes 叢集時使用。舉例來說,您可以使用下列指令來識別不同的機器類型、可用區,甚至是 GPU (加速器)。
- 使用這個指令
gcloud compute machine-types list
列出機器類型 - 使用這項指令「
gcloud compute accelerator-types list
」列出 GPU - 使用這個指令
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 應用程式,因此您必須建立叢集。執行下列指令,在 Google Cloud 中透過 GKE 建立新的 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 叢集互動的主要方式。如要將這個叢集與剛建立的新叢集搭配使用,必須設為對該叢集進行驗證。執行下列指令即可完成這項操作。
$ 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 執行指令會建立一個新容器,該容器會公開通訊埠 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/?authuser=0
使用網頁預覽功能查看 .NET 範例應用程式
您現在可以開啟「網頁預覽」並載入提供的網址,檢視在最後一個步驟中啟動的範例應用程式。如下所示:
5. 部署至 Kubernetes
建立並套用 YAML 檔案
下一步需要說明兩個 Kubernetes 資源 (一個 Deployment 和一個 Service) 的 YAML 檔案。在 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。應該有一個由上述部署作業建立的 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>
查看服務資源
Kubernetes 中的 Service 資源是負載平衡器。端點是由 Pod 上的標籤決定。這樣一來,只要透過上述 kubectl scale deployment
作業將新的 Pod 加到 Deployment 中,產生的 Pod 就能立即用於該服務處理的要求。
下列指令應會顯示服務資源。
$ 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 都能將服務名稱 dotnet-service
解析為 IP 位址。傳送至服務的要求將在所有執行個體 (Pod) 之間進行負載平衡。上述 Endpoints
值顯示了這項服務目前可用的 Pod 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
和先前一樣,您可以透過網頁預覽功能存取這個應用程式。這次應該會顯示主機參數,如下所示:
在 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。使用上方的 IMAGE 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 服務正在分配負載
重新整理此網址數次,您會發現由於由服務在不同的 Pod 之間平衡要求,因此主機會變更。比較主機值與上方的 Pod 清單,確定所有 Pod 都收到流量。
向上擴充執行個體
在 Kubernetes 中調度應用程式資源相當簡單。下列指令會將部署作業擴充到最多 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 更新至新版本,並向上擴充應用程式,讓 Kubernetes 瞭解負載分配至其他執行個體的方式。
如要進一步瞭解 .NET 和 Kubernetes,請參考這些教學課程。這些架構奠基於本研究室中學到的知識,透過介紹 Istio 服務網格,以及更精細的轉送和彈性模式。
9. 清除所用資源
請使用下列指令,刪除在這個研究室中建立的叢集和容器映像檔,以免產生非預期的費用。
gcloud container clusters delete dotnet-cluster --zone ${DEFAULT_ZONE}
gcloud container images delete gcr.io/${PROJECT_ID}/aspnetapp:alpine