Развертывание и обновление приложения .NET Core в Google Kubernetes Engine

1. Обзор

Microsoft .NET Core — это кроссплатформенная версия .NET с открытым исходным кодом, которая может работать в контейнерах. .NET Core доступен на GitHub и поддерживается Microsoft и сообществом .NET. В ходе этой лабораторной работы контейнерное приложение .NET Core развертывается в Google Kubernetes Engine (GKE).

Эта лабораторная работа соответствует типичному шаблону разработки, при котором приложения разрабатываются в локальной среде разработчика, а затем развертываются в рабочей среде. В первой части лабораторной работы пример основного приложения .NET проверяется с использованием контейнера, работающего в Cloud Shell . После проверки приложение развертывается в Kubernetes с помощью GKE. Лабораторная работа включает в себя шаги по созданию кластера GKE.

Во второй части лабораторной работы в приложение вносится небольшое изменение, в котором отображается имя хоста контейнера, на котором запущен этот экземпляр приложения. Обновленное приложение затем проверяется в облачной оболочке, и развертывание обновляется для использования новой версии. На следующем рисунке показана последовательность действий в этой лабораторной работе:

Диаграмма последовательности демонстраций

Затраты

Если вы проведете эту лабораторную работу в точности так, как написано, будут взиматься обычные расходы на следующие услуги.

2. Настройка и требования

Предварительные условия

Для выполнения этой лабораторной работы требуется учетная запись Google Cloud и проект. Более подробные инструкции о том, как создать новый проект, можно найти в этой Codelab .

В этой лабораторной работе используется Docker, работающий в Cloud Shell , который доступен через Google Cloud Console и поставляется с предварительно настроенным множеством полезных инструментов, таких как gcloud и Docker. Как получить доступ к облачной оболочке показано ниже. Щелкните значок Cloud Shell в правом верхнем углу, чтобы отобразить его на нижней панели окна консоли.

Облачная оболочка

Альтернативные варианты конфигурации для кластера GKE (необязательно)

Для этой лабораторной работы требуется кластер Kubernetes. В следующем разделе создается кластер GKE с простой конфигурацией. В этом разделе показаны некоторые команды gcloud , которые предоставляют альтернативные параметры конфигурации, которые можно использовать при создании кластера Kubernetes с использованием GKE. Например, используя приведенные ниже команды, можно идентифицировать различные типы машин, зоны и даже графические процессоры (ускорители).

  • Список gcloud compute machine-types list машин с помощью этой команды
  • Выведите список графических процессоров с помощью этой команды 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

Поскольку в ходе этой лабораторной работы приложение .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 устанавливает идентификатор рабочей нагрузки, чтобы рабочие нагрузки GKE могли получить доступ к сервисам Google Cloud.

Во время сборки кластера отображается следующее

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

Настроить кубектл

Интерфейс командной строки kubectl — это основной способ взаимодействия с кластером 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.

Запустите контейнер локально, чтобы проверить функциональность.

В облачной оболочке убедитесь, что 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

Подтвердите функциональность веб-приложения

Пример веб-приложения также можно проверить в облачной оболочке. Команда запуска Docker, приведенная ниже, создает новый контейнер, который предоставляет порт 80 и сопоставляет его с портом 8080 localhost . Помните, что localhost в данном случае находится в облачной оболочке.

$ 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

Поскольку это веб-приложение, его необходимо просматривать и проверять в веб-браузере. В следующем разделе показано, как это сделать в облачной оболочке с помощью Web Preview.

4. Доступ к сервисам из облачной оболочки с помощью «Предварительного просмотра в Интернете».

Cloud Shell предлагает веб-предварительный просмотр — функцию, которая позволяет использовать браузер для взаимодействия с процессами, выполняющимися в экземпляре облачной оболочки.

Используйте «Предварительный просмотр в Интернете» для просмотра приложений в Cloud Shell.

В Cloud Shell нажмите кнопку веб-предварительного просмотра и выберите « Просмотр на порту 8080 » (или любой другой порт, который настроен для веб-просмотра).

Облачная оболочка

Откроется окно браузера с таким адресом:

https://8080-cs-754738286554-default.us-central1.cloudshell.dev/?authuser=0

Просмотрите пример приложения .NET с помощью Web Preview.

Пример приложения, запущенный на последнем шаге, теперь можно просмотреть, запустив веб-просмотр и загрузив предоставленный URL-адрес. Это должно выглядеть примерно так:

Снимок экрана приложения .NET V1

5. Развертывание в Kubernetes

Создайте файл YAML и примените его.

Для следующего шага потребуется файл YAML, описывающий два ресурса Kubernetes: развертывание и службу. Создайте файл с именем 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

Далее взгляните на наборы реплик. Должен быть ReplicaSet, созданный указанным выше развертыванием.

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

Наконец, взгляните на Pods . В развертывании указано, что таких экземпляров должно быть три. Команда ниже должна показать, что существует три экземпляра. Добавлена ​​опция -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 — это балансировщик нагрузки. Конечные точки определяются метками на модулях. Таким образом, как только в развертывание добавляются новые модули с помощью описанной выше операции 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 . Это означает, что любой модуль в кластере может преобразовать имя службы dotnet-service в свой IP-адрес. Запросы, отправленные в службу, будут распределяться по всем экземплярам (подам). Значение Endpoints выше показывает IP-адреса модулей, доступных в настоящее время для этой службы. Сравните их с IP-адресами модулей, которые были выведены выше.

Проверьте работающее приложение

На данный момент приложение работает и готово к запросам пользователей. Для доступа к нему используйте прокси. Следующая команда создает локальный прокси-сервер, который принимает запросы на порт 8080 и передает их в кластер Kubernetes.

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

Теперь используйте веб-просмотр в Cloud Shell для доступа к веб-приложению.

Добавьте следующее к URL-адресу, созданному с помощью Web Preview: /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 должен быть виден, как показано здесь:

Облачная оболочка

Откройте новую вкладку в облачной оболочке и запустите docker ps чтобы убедиться, что идентификатор контейнера соответствует значению хоста, показанному выше.

$ 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 . Используя указанный выше идентификатор IMAGE, это будет выглядеть так

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

Обратите внимание, что ресурс «Развертывание» отображается обновленным, а ресурс «Сервис» — без изменений. Обновленные поды можно увидеть, как и раньше, с помощью команды 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

Теперь используйте веб-просмотр в Cloud Shell для доступа к веб-приложению.

Добавьте следующее к URL-адресу, созданному с помощью Web Preview: /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-адрес несколько раз и обратите внимание, что хост меняется, поскольку запросы балансируются службой между разными модулями. Сравните значения хоста со списком модулей сверху, чтобы убедиться, что все модули получают трафик.

Масштабируйте экземпляры

Масштабировать приложения в Kubernetes легко. Следующая команда масштабирует развертывание до 6 экземпляров приложения.

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

Новые поды и их текущее состояние можно просмотреть с помощью этой команды

kubectl get pod -w

Обратите внимание, что обновление того же окна браузера показывает, что трафик теперь сбалансирован между всеми новыми модулями.

8. Поздравляем!

В ходе этой лабораторной работы пример веб-приложения .NET Core был проверен в среде разработки и впоследствии развернут в Kubernetes с помощью GKE. Затем приложение было изменено для отображения имени хоста контейнера, в котором оно работало. Затем развертывание Kubernetes было обновлено до новой версии, а приложение было масштабировано, чтобы продемонстрировать, как нагрузка распределяется между дополнительными экземплярами.

Чтобы узнать больше о .NET и Kubernetes, ознакомьтесь с этими руководствами. Они основаны на знаниях, полученных в ходе этой лабораторной работы, путем внедрения Istio Service Mesh для более сложных шаблонов маршрутизации и устойчивости.

9. Очистка

Чтобы избежать непредвиденных затрат, используйте следующие команды для удаления кластера и образа контейнера, созданных в ходе этой лабораторной работы.

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