1. Обзор
Microsoft .NET Core – это кроссплатформенная версия .NET с открытым исходным кодом, которая может работать в контейнерах. Платформа .NET Core доступна на GitHub и поддерживается Microsoft и сообществом .NET. В этой практической работе вы развернете контейнеризованное приложение .NET Core в Google Kubernetes Engine (GKE).
В этой лабораторной работе используется типичный шаблон разработки, в котором приложения разрабатываются в локальной среде разработчиков, а затем развертываются в рабочей среде. В первой части практической работы пример приложения .NET Core проверяется с помощью контейнера, работающего в Cloud Shell. После проверки приложение развертывается в Kubernetes с помощью GKE. В лабораторной работе описаны шаги по созданию кластера GKE.
Во второй части лабораторной работы в приложение вносится небольшое изменение, которое показывает имя хоста контейнера, в котором запущен этот экземпляр приложения. Затем обновленное приложение проверяется в Cloud Shell, а развертывание обновляется для использования новой версии. На рисунке ниже показана последовательность действий в этой практической работе.

Расходы
Если вы выполните эту лабораторную работу в точности так, как написано, будут применяться обычные тарифы на следующие сервисы:
2. Настройка и требования
Требования
Для выполнения этой лабораторной работы необходимы аккаунт и проект Google Cloud. Подробные инструкции по созданию проекта приведены в этой практической работе.
В этой лабораторной работе используется Docker, запущенный в Cloud Shell. Cloud Shell доступен в консоли Google Cloud и поставляется с предварительно настроенными инструментами, такими как gcloud и Docker. Ниже показано, как получить доступ к Cloud Shell. Нажмите на значок Cloud Shell в правом верхнем углу, чтобы открыть ее в нижней части окна консоли.

Альтернативные варианты конфигурации для кластера GKE (необязательно)
Для этой лабораторной работы требуется кластер Kubernetes. В следующем разделе создается кластер GKE с простой конфигурацией. В этом разделе приведены некоторые команды gcloud, которые предоставляют альтернативные варианты конфигурации для использования при создании кластера Kubernetes с помощью GKE. Например, с помощью приведенных ниже команд можно определить разные типы машин, зоны и даже графические процессоры (ускорители).
- Чтобы посмотреть список типов машин, выполните команду
gcloud compute machine-types list. - Выведите список графических процессоров с помощью команды
gcloud compute accelerator-types list. - Вывести список зон Compute с помощью команды
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
Поскольку в этой лабораторной работе приложение .NET Core развертывается в Kubernetes, необходимо создать кластер. Чтобы создать кластер Kubernetes в Google Cloud с помощью GKE, используйте следующую команду:
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. Протестируйте локально и убедитесь, что все работает правильно
В этой лабораторной работе используются следующие образы контейнеров из официального репозитория .NET в Docker Hub.
Запустите контейнер локально, чтобы проверить его работу
В Cloud Shell убедитесь, что Docker запущен и работает правильно, а контейнер .NET работает должным образом, выполнив следующую команду Docker:
$ 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 есть функция Web Preview, которая позволяет использовать браузер для взаимодействия с процессами, запущенными в экземпляре Cloud Shell.
Как использовать веб-просмотр для работы с приложениями в Cloud Shell
В Cloud Shell нажмите кнопку веб-просмотра и выберите Preview on port 8080 (Просмотр на порте 8080) или другой порт, настроенный для веб-просмотра.

Откроется окно браузера с адресом, похожим на этот:
https://8080-cs-754738286554-default.us-central1.cloudshell.dev/
Как посмотреть пример приложения .NET с помощью веб-просмотра
Чтобы посмотреть пример приложения, запущенный на предыдущем шаге, запустите веб-просмотр и загрузите предоставленный URL. Он выглядит примерно так:

5. Развертывание в Kubernetes
Создайте и примените YAML-файл
На следующем шаге потребуется файл YAML, описывающий два ресурса Kubernetes: Deployment и Service. Создайте в 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
Обратите внимание на сообщения о том, что нужные ресурсы созданы.
Изучите полученные ресурсы
Мы можем использовать CLI kubectl, чтобы изучить ресурсы, созданные выше. Сначала давайте посмотрим на ресурсы развертывания и убедимся, что новое развертывание там есть.
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
dotnet-deployment 3/3 3 3 80s
Теперь посмотрите на ReplicaSets. В результате развертывания должен быть создан набор ReplicaSet.
$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
dotnet-deployment-5c9d4cc4b9 3 3 3 111s
Наконец, рассмотрим пакеты. В развертывании указано, что должно быть три экземпляра. Приведенная ниже команда должна показать, что существует три экземпляра. Параметр -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
Ресурс Service в Kubernetes – это балансировщик нагрузки. Конечные точки определяются ярлыками на Pod. Таким образом, как только новые поды будут добавлены в развертывание с помощью операции kubectl scale deployment, они сразу же станут доступны для запросов, обрабатываемых этим сервисом.
Следующая команда должна показать ресурс сервиса.
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dotnet-service ClusterIP 10.20.9.124 <none> 8080/TCP 2m50s
...
Чтобы посмотреть подробную информацию о сервисе, выполните следующую команду:
$ 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>
Обратите внимание, что сервис имеет тип ClusterIP. Это означает, что любой Pod в кластере может разрешить имя сервиса dotnet-service в его IP-адрес. Запросы, отправленные в сервис, будут распределяться между всеми экземплярами (подами). Значение Endpoints выше показывает IP-адреса подов, доступных для этого сервиса. Сравните их с IP-адресами подов, которые были выведены выше.
Проверьте запущенное приложение
После этого приложение будет запущено и готово к обработке запросов пользователей. Чтобы получить к нему доступ, используйте прокси-сервер. Следующая команда создает локальный прокси-сервер, который принимает запросы на порт 8080 и передает их в кластер Kubernetes.
$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080
Теперь используйте Web Preview в Cloud Shell, чтобы получить доступ к веб-приложению.
Добавьте к URL, созданному функцией предварительного просмотра, следующий код: /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/
Поздравляем! Вы развернули приложение .NET Core в Google Kubernetes Engine. Теперь мы внесем изменения в приложение и повторно развернем его.
6. Изменить приложение
В этом разделе мы изменим приложение, чтобы оно показывало хост, на котором запущен экземпляр. Это позволит убедиться, что балансировка нагрузки работает и доступные поды отвечают ожидаемым образом.
Получить исходный код
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, чтобы убедиться, что идентификатор контейнера совпадает со значением Host, указанным выше.
$ 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. Если использовать идентификатор изображения, указанный выше, то получится следующее:
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, но на этот раз мы добавим -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
В выходных данных выше показано последовательное обновление. Сначала запускаются новые контейнеры, а затем, когда они начинают работать, старые контейнеры завершают работу.
Проверьте запущенное приложение
После этого приложение будет обновлено и готово к обработке запросов пользователей. Как и раньше, доступ к нему можно получить через прокси-сервер.
$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080
Теперь используйте Web Preview в Cloud Shell, чтобы получить доступ к веб-приложению.
Добавьте к URL, созданному функцией предварительного просмотра, следующий код: /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 распределяет нагрузку
Обновите этот URL несколько раз и обратите внимание, что хост меняется, поскольку запросы распределяются между разными подами с помощью сервиса. Сравните значения Host со списком подов выше, чтобы убедиться, что все поды получают трафик.
Увеличить количество экземпляров
Масштабировать приложения в Kubernetes очень просто. Следующая команда увеличит развертывание до шести экземпляров приложения.
$ kubectl scale deployment dotnet-deployment --replicas 6
deployment.apps/dotnet-deployment scaled
Чтобы посмотреть новые поды и их текущее состояние, выполните следующую команду:
kubectl get pod -w
Обратите внимание, что при обновлении того же окна браузера трафик теперь балансируется по всем новым Pod.
8. Поздравляем!
В этой лабораторной работе пример веб-приложения .NET Core был проверен в среде разработчика и затем развернут в Kubernetes с помощью GKE. Затем приложение было изменено для отображения имени хоста контейнера, в котором оно работало. Затем развертывание Kubernetes было обновлено до новой версии, а приложение было масштабировано, чтобы продемонстрировать, как нагрузка распределяется между дополнительными экземплярами.
Чтобы узнать больше о .NET и Kubernetes, ознакомьтесь с этими руководствами. Они основаны на материале этой лабораторной работы и знакомят с сервисной сеткой Istio, которая позволяет использовать более сложные шаблоны маршрутизации и отказоустойчивости.
9. Организовать
Чтобы избежать непредвиденных расходов, удалите кластер и образ контейнера, созданные в ходе этой лабораторной работы, с помощью следующих команд:
gcloud container clusters delete dotnet-cluster --zone ${DEFAULT_ZONE}
gcloud container images delete gcr.io/${PROJECT_ID}/aspnetapp:alpine