1. Przegląd
Microsoft .NET Core to wersja .NET typu open source, która działa na wielu platformach i może być uruchamiana natywnie w kontenerach. .NET Core jest dostępny na GitHubie i jest utrzymywany przez Microsoft oraz społeczność .NET. W tym module wdrożysz skonteneryzowaną aplikację .NET Core w Google Kubernetes Engine (GKE).
W tym module będziemy postępować zgodnie z typowym wzorcem programowania, w którym aplikacje są tworzone w lokalnym środowisku programisty, a następnie wdrażane w środowisku produkcyjnym. W pierwszej części tego laboratorium przykładowa aplikacja .NET Core jest weryfikowana za pomocą kontenera działającego w Cloud Shell. Po zweryfikowaniu aplikacja jest wdrażana w Kubernetes za pomocą GKE. Moduł zawiera instrukcje tworzenia klastra GKE.
W drugiej części modułu wprowadzimy niewielką zmianę w aplikacji, która wyświetla nazwę hosta kontenera, w którym działa instancja aplikacji. Zaktualizowana aplikacja jest następnie weryfikowana w Cloud Shell, a wdrożenie jest aktualizowane w celu używania nowej wersji. Ilustracja poniżej przedstawia kolejność działań w tym module:

Koszty
Jeśli wykonasz to ćwiczenie dokładnie tak, jak zostało opisane, zostaną naliczone standardowe opłaty za te usługi:
2. Konfiguracja i wymagania
Wymagania wstępne
Aby ukończyć ten moduł, musisz mieć konto i projekt Google Cloud. Szczegółowe instrukcje tworzenia nowego projektu znajdziesz w tym ćwiczeniu z programowania.
W tym module używamy Dockera działającego w Cloud Shell, który jest dostępny w konsoli Google Cloud i ma wstępnie skonfigurowane wiele przydatnych narzędzi, takich jak gcloud i Docker. Poniżej znajdziesz informacje o tym, jak uzyskać dostęp do Cloud Shell. Kliknij ikonę Cloud Shell w prawym górnym rogu, aby wyświetlić ją w dolnym panelu okna konsoli.

Alternatywne opcje konfiguracji klastra GKE (opcjonalne)
Ten moduł wymaga klastra Kubernetes. W następnej sekcji utworzymy klaster GKE o prostej konfiguracji. W tej sekcji znajdziesz kilka poleceń gcloud, które zapewniają alternatywne opcje konfiguracji do użycia podczas tworzenia klastra Kubernetes za pomocą GKE. Na przykład za pomocą poniższych poleceń można zidentyfikować różne typy maszyn, strefy, a nawet procesory GPU (akceleratory).
- Wyświetl listę typów maszyn za pomocą tego polecenia:
gcloud compute machine-types list - Aby wyświetlić listę procesorów graficznych, użyj tego polecenia:
gcloud compute accelerator-types list - Aby wyświetlić listę stref obliczeniowych, użyj tego polecenia:
gcloud compute zones list - Uzyskiwanie pomocy dotyczącej dowolnego polecenia gcloud
gcloud container clusters --help- Na przykład ten artykuł zawiera szczegółowe informacje o tworzeniu klastra Kubernetes
gcloud container clusters create --help
- Na przykład ten artykuł zawiera szczegółowe informacje o tworzeniu klastra Kubernetes
Pełną listę opcji konfiguracji GKE znajdziesz w tym dokumencie.
Przygotowywanie do utworzenia klastra Kubernetes
W Cloud Shell musisz ustawić niektóre zmienne środowiskowe i skonfigurować klienta gcloud. Można to zrobić za pomocą tych poleceń.
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}
Tworzenie klastra GKE
W tym module wdrażamy aplikację .NET Core w Kubernetes, dlatego musimy utworzyć klaster. Aby utworzyć nowy klaster Kubernetes w Google Cloud za pomocą GKE, użyj tego polecenia:
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-nodesto liczba węzłów do dodania w każdej strefie. Możesz ją później zmienić.--node-locationsto lista stref rozdzielona przecinkami. W tym przypadku używana jest strefa określona w zmiennej środowiskowej powyżej ius-central1-b.- UWAGA: ta lista nie może zawierać duplikatów.
--workload-poolustala tożsamość zadań, aby zadania GKE mogły uzyskiwać dostęp do usług Google Cloud;
Podczas tworzenia klastra wyświetla się ten komunikat:
Creating cluster dotnet-cluster in us-central1-b... Cluster is being deployed...⠼
Konfigurowanie narzędzia kubectl
kubectlCLI to główny sposób interakcji z klastrem Kubernetes. Aby można było go używać z nowo utworzonym klastrem, musi być skonfigurowany do uwierzytelniania w klastrze. Możesz to zrobić za pomocą tego polecenia.
$ gcloud container clusters get-credentials dotnet-cluster --zone ${DEFAULT_ZONE}
Fetching cluster endpoint and auth data.
kubeconfig entry generated for dotnet-cluster.
Teraz powinno być możliwe używanie kubectl do interakcji z klastrem.
$ 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. Przeprowadź test lokalny i sprawdź, czy funkcja działa zgodnie z oczekiwaniami.
W tym module używane są te obrazy kontenerów z oficjalnego repozytorium .NET w Docker Hub.
Uruchom kontener lokalnie, aby sprawdzić jego działanie
W Cloud Shell sprawdź, czy Docker działa prawidłowo i czy kontener .NET działa zgodnie z oczekiwaniami. W tym celu uruchom to polecenie Dockera:
$ 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
Potwierdź funkcje aplikacji internetowej
Przykładową aplikację internetową można też zweryfikować w Cloud Shell. Poniższe polecenie Docker run tworzy nowy kontener, który udostępnia port 80 i mapuje go na port localhost 8080. Pamiętaj, że localhost w tym przypadku znajduje się w 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
Jest to aplikacja internetowa, więc musi zostać wyświetlona i zweryfikowana w przeglądarce. W następnej sekcji dowiesz się, jak to zrobić w Cloud Shell za pomocą podglądu w przeglądarce.
4. Dostęp do usług z Cloud Shell za pomocą funkcji „Podgląd w przeglądarce”
Cloud Shell oferuje podgląd w przeglądarce, czyli funkcję, która umożliwia korzystanie z przeglądarki do interakcji z procesami działającymi w instancji Cloud Shell.
Wyświetlanie aplikacji w Cloud Shell za pomocą funkcji „Podgląd w przeglądarce”
W Cloud Shell kliknij przycisk podglądu w przeglądarce i wybierz „Podejrzyj na porcie 8080” (lub dowolny port, który ma być używany przez podgląd w przeglądarce).

Otworzy się okno przeglądarki z adresem podobnym do tego:
https://8080-cs-754738286554-default.us-central1.cloudshell.dev/?authuser=0
Wyświetlanie przykładowej aplikacji .NET za pomocą podglądu w przeglądarce
Aplikację przykładową, którą utworzyliśmy w ostatnim kroku, można teraz wyświetlić, uruchamiając podgląd w przeglądarce i wczytując podany adres URL. Powinna wyglądać mniej więcej tak:

5. Wdrażanie w Kubernetes
Utwórz plik YAML i zastosuj go
Kolejny krok wymaga pliku YAML opisującego 2 zasoby Kubernetes: wdrożenie i usługę. Utwórz w Cloud Shell plik o nazwie dotnet-app.yaml i dodaj do niego tę treść.
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
Teraz użyj polecenia kubectl, aby zastosować ten plik do Kubernetes.
$ kubectl apply -f dotnet-app.yaml
deployment.apps/dotnet-deployment created
service/dotnet-service created
Zwróć uwagę na komunikaty informujące o utworzeniu wybranych zasobów.
Przeglądanie utworzonych zasobów
Za pomocą interfejsu wiersza poleceń kubectl możemy sprawdzić utworzone powyżej zasoby. Najpierw sprawdźmy zasoby wdrożenia i upewnijmy się, że nowe wdrożenie jest dostępne.
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
dotnet-deployment 3/3 3 3 80s
Następnie przyjrzyj się ReplicaSetom. W wyniku powyższego wdrożenia powinien zostać utworzony obiekt ReplicaSet.
$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
dotnet-deployment-5c9d4cc4b9 3 3 3 111s
Na koniec przyjrzyj się podom. Wdrożenie wskazywało, że powinny być 3 instancje. Poniższe polecenie powinno pokazać, że istnieją 3 instancje. Dodana jest opcja -o wide, dzięki czemu wyświetlane są węzły, w których działają te instancje.
$ 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>
Sprawdź zasób usługi
Zasób Service w Kubernetes to system równoważenia obciążenia. Punkty końcowe są określane przez etykiety w podach. W ten sposób, gdy tylko nowe pody zostaną dodane do wdrożenia za pomocą operacji kubectl scale deployment powyżej, będą one natychmiast dostępne dla żądań obsługiwanych przez tę usługę.
To polecenie powinno wyświetlić zasób usługi.
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dotnet-service ClusterIP 10.20.9.124 <none> 8080/TCP 2m50s
...
Więcej szczegółów o usłudze możesz wyświetlić za pomocą tego polecenia:
$ 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>
Zwróć uwagę, że usługa jest typu ClusterIP. Oznacza to, że każdy pod w klastrze może rozpoznać nazwę usługi dotnet-service i uzyskać jej adres IP. Żądania wysyłane do usługi będą równoważone na wszystkich instancjach (podach). Wartość Endpoints powyżej pokazuje adresy IP podów, które są obecnie dostępne dla tej usługi. Porównaj je z adresami IP zasobników, które zostały podane powyżej.
Weryfikowanie uruchomionej aplikacji
W tym momencie aplikacja jest aktywna i gotowa do obsługi żądań użytkowników. Aby uzyskać do niego dostęp, użyj serwera proxy. To polecenie tworzy lokalny serwer proxy, który akceptuje żądania na porcie 8080 i przekazuje je do klastra Kubernetes.
$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080
Teraz użyj podglądu w przeglądarce w Cloud Shell, aby uzyskać dostęp do aplikacji internetowej.
Do adresu URL wygenerowanego przez Podgląd w przeglądarce dodaj: /api/v1/namespaces/default/services/dotnet-service:8080/proxy/. Będzie to wyglądać mniej więcej tak:
https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/
Gratulujemy wdrożenia aplikacji .NET Core w Google Kubernetes Engine. Następnie wprowadzimy zmianę w aplikacji i ponownie ją wdrożymy.
6. Modyfikowanie aplikacji
W tej sekcji zmodyfikujemy aplikację, aby wyświetlała hosta, na którym działa instancja. Dzięki temu możesz potwierdzić, że równoważenie obciążenia działa, a dostępne pody odpowiadają zgodnie z oczekiwaniami.
Pobieranie kodu źródłowego
git clone https://github.com/dotnet/dotnet-docker.git
cd dotnet-docker/samples/aspnetapp/
Zaktualizuj aplikację, aby zawierała nazwę hosta
vi aspnetapp/Pages/Index.cshtml
<tr>
<td>Host</td>
<td>@Environment.MachineName</td>
</tr>
Kompilowanie nowego obrazu kontenera i testowanie go lokalnie
Utwórz nowy obraz kontenera ze zaktualizowanym kodem.
docker build --pull -t aspnetapp:alpine -f Dockerfile.alpine-x64 .
Tak jak wcześniej, przetestuj nową aplikację lokalnie.
$ 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
Jak wcześniej, do aplikacji można uzyskać dostęp za pomocą Podglądu w przeglądarce. Tym razem parametr Host powinien być widoczny, jak pokazano tutaj:

Otwórz nową kartę w Cloud Shell i uruchom polecenie docker ps, aby sprawdzić, czy identyfikator kontenera jest zgodny z wartością Host widoczną powyżej.
$ 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
Dodaj tag do obrazu i prześlij go, aby był dostępny dla Kubernetes.
Aby Kubernetes mógł pobrać obraz, musi on zostać otagowany i przesłany. Zacznij od wyświetlenia listy obrazów kontenerów i znalezienia odpowiedniego obrazu.
$ docker image list
REPOSITORY TAG IMAGE ID CREATED SIZE
aspnetapp alpine 95b4267bb6d0 6 days ago 110MB
Następnie dodaj tag do tego obrazu i prześlij go do Google Container Registry. W przypadku identyfikatora IMAGE ID podanego powyżej będzie to wyglądać tak:
docker tag 95b4267bb6d0 gcr.io/${PROJECT_ID}/aspnetapp:alpine
docker push gcr.io/${PROJECT_ID}/aspnetapp:alpine
7. Ponowne wdrażanie zaktualizowanej aplikacji
Edytowanie pliku YAML
Wróć do katalogu, w którym zapisany jest plik dotnet-app.yaml. W pliku YAML znajdź ten wiersz:
image: mcr.microsoft.com/dotnet/core/samples:aspnetapp
Należy to zmienić, aby odwoływać się do obrazu kontenera, który został utworzony i przesłany do gcr.io powyżej.
image: gcr.io/PROJECT_ID/aspnetapp:alpine
Nie zapomnij zmodyfikować go, aby używać Twojego PROJECT_ID. Po zakończeniu powinno to wyglądać mniej więcej tak:
image: gcr.io/myproject/aspnetapp:alpine
Zastosuj zaktualizowany plik YAML
$ kubectl apply -f dotnet-app.yaml
deployment.apps/dotnet-deployment configured
service/dotnet-service unchanged
Zwróć uwagę, że zasób wdrożenia jest zaktualizowany, a zasób usługi pozostaje bez zmian. Zaktualizowane pody można wyświetlić jak wcześniej za pomocą polecenia kubectl get pod, ale tym razem dodamy -w, które będzie śledzić wszystkie zmiany na bieżąco.
$ 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
Powyższe dane wyjściowe pokazują aktualizację kroczącą w trakcie jej przeprowadzania. Najpierw uruchamiane są nowe kontenery, a gdy działają, stare kontenery są zamykane.
Weryfikowanie uruchomionej aplikacji
Aplikacja jest teraz zaktualizowana i gotowa do obsługi żądań użytkowników. Jak wcześniej, można uzyskać do niego dostęp za pomocą serwera proxy.
$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080
Teraz użyj podglądu w przeglądarce w Cloud Shell, aby uzyskać dostęp do aplikacji internetowej.
Do adresu URL wygenerowanego przez Podgląd w przeglądarce dodaj: /api/v1/namespaces/default/services/dotnet-service:8080/proxy/. Będzie to wyglądać mniej więcej tak:
https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/
Sprawdź, czy usługa Kubernetes równoważy obciążenie
Odśwież ten adres URL kilka razy i zwróć uwagę, że host zmienia się, ponieważ żądania są równoważone w różnych zasobnikach przez usługę. Porównaj wartości Host z listą zasobników powyżej, aby sprawdzić, czy wszystkie zasobniki otrzymują ruch.
Skalowanie instancji w górę
Skalowanie aplikacji w Kubernetes jest proste. To polecenie przeskaluje wdrożenie do 6 instancji aplikacji.
$ kubectl scale deployment dotnet-deployment --replicas 6
deployment.apps/dotnet-deployment scaled
Za pomocą tego polecenia możesz wyświetlić nowe pody i ich bieżący stan.
kubectl get pod -w
Zauważ, że odświeżenie tego samego okna przeglądarki pokazuje, że ruch jest teraz równoważony we wszystkich nowych podach.
8. Gratulacje!
W tym module sprawdziliśmy przykładową aplikację internetową .NET Core w środowisku deweloperskim, a następnie wdrożyliśmy ją w Kubernetes za pomocą GKE. Następnie zmodyfikowano aplikację, aby wyświetlała nazwę hosta kontenera, w którym była uruchomiona. Wdrożenie Kubernetes zostało następnie zaktualizowane do nowej wersji, a aplikacja została przeskalowana w górę, aby pokazać, jak obciążenie jest rozdzielane na dodatkowe instancje.
Więcej informacji o .NET i Kubernetes znajdziesz w tych samouczkach. W tym module wykorzystujemy wiedzę zdobytą w tym module, wprowadzając siatkę usług Istio, która umożliwia bardziej zaawansowane wzorce routingu i odporności.
9. Czyszczenie danych
Aby uniknąć nieoczekiwanych kosztów, użyj tych poleceń, aby usunąć klaster i obraz kontenera utworzone w tym module.
gcloud container clusters delete dotnet-cluster --zone ${DEFAULT_ZONE}
gcloud container images delete gcr.io/${PROJECT_ID}/aspnetapp:alpine