Разработка InnerLoop с использованием облачных рабочих станций с Python

1. Обзор

В этой лабораторной работе демонстрируются функции и возможности, предназначенные для оптимизации рабочего процесса разработки для инженеров-программистов, которым поручено разрабатывать приложения Python в контейнерной среде. Типичная разработка контейнеров требует от пользователя понимания деталей контейнеров и процесса сборки контейнеров. Кроме того, разработчикам обычно приходится прерывать рабочий процесс, выходя из своей IDE для тестирования и отладки своих приложений в удаленных средах. С помощью инструментов и технологий, упомянутых в этом руководстве, разработчики могут эффективно работать с контейнерными приложениями, не выходя из своей IDE.

Что вы узнаете

В этой лабораторной работе вы изучите методы разработки с использованием контейнеров в GCP, в том числе:

  • Создание нового стартового приложения Python
  • Пройдите процесс разработки
  • Разработайте простой сервис отдыха CRUD.
  • Развертывание в GKE
  • Отладка состояния ошибки
  • Использование точки останова/логов
  • Горячее развертывание изменений обратно в GKE

58a4cdd3ed7a123a.png

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

Самостоятельная настройка среды

  1. Войдите в Google Cloud Console и создайте новый проект или повторно используйте существующий. Если у вас еще нет учетной записи Gmail или Google Workspace, вам необходимо ее создать .

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • Имя проекта — это отображаемое имя для участников этого проекта. Это строка символов, не используемая API Google. Вы можете обновить его в любое время.
  • Идентификатор проекта уникален для всех проектов Google Cloud и является неизменяемым (невозможно изменить после его установки). Cloud Console автоматически генерирует уникальную строку; обычно тебя не волнует, что это такое. В большинстве лабораторий кода вам потребуется указать идентификатор проекта (обычно он обозначается как PROJECT_ID ). Если вам не нравится сгенерированный идентификатор, вы можете создать другой случайный идентификатор. Кроме того, вы можете попробовать свой собственный и посмотреть, доступен ли он. Его нельзя изменить после этого шага, и он останется в силе на протяжении всего проекта.
  • К вашему сведению, есть третье значение — номер проекта , который используют некоторые API. Подробнее обо всех трех этих значениях читайте в документации .
  1. Затем вам необходимо включить выставление счетов в Cloud Console, чтобы использовать облачные ресурсы/API. Прохождение этой лаборатории кода не должно стоить много, если вообще стоит. Чтобы отключить ресурсы и не взимать плату за пределами этого руководства, вы можете удалить созданные вами ресурсы или удалить весь проект. Новые пользователи Google Cloud имеют право на участие в программе бесплатной пробной версии стоимостью 300 долларов США .

Запустить редактор Cloudshell

Эта лабораторная работа была разработана и протестирована для использования с редактором Google Cloud Shell. Чтобы получить доступ к редактору,

  1. получите доступ к своему проекту Google по адресу https://console.cloud.google.com .
  2. В правом верхнем углу нажмите на значок редактора облачной оболочки.

8560cc8d45e8c112.png

  1. В нижней части окна откроется новая панель.
  2. Нажмите кнопку «Открыть редактор».

9e504cb98a6a8005.png

  1. Редактор откроется с проводником справа и редактором в центральной части.
  2. Панель терминала также должна быть доступна в нижней части экрана.
  3. Если терминал НЕ открыт, используйте комбинацию клавиш `ctrl+``, чтобы открыть новое окно терминала.

Настройка среды

В Cloud Shell укажите идентификатор и номер вашего проекта. Сохраните их как переменные PROJECT_ID и PROJECT_ID .

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID \
    --format='value(projectNumber)')

Предоставление инфраструктуры, используемой в этой лаборатории

В ходе этой лабораторной работы вы развернете код в GKE и получите доступ к данным, хранящимся в базе данных Spanner. Вы также будете использовать облачные рабочие станции в качестве IDE. Приведенный ниже сценарий установки подготовит для вас эту инфраструктуру.

  1. Загрузите сценарий установки и сделайте его исполняемым.
wget https://raw.githubusercontent.com/GoogleCloudPlatform/container-developer-workshop/main/labs/python/setup_with_cw.sh
chmod +x setup_with_cw.sh
  1. Откройте файл setup_with_cw.sh и отредактируйте значения паролей, для которых в данный момент установлено значение CHANGEME.
  2. Запустите сценарий установки, чтобы подготовить кластер GKE и базу данных Spanner, которые вы будете использовать в этой лабораторной работе.
./setup_with_cw.sh &

Кластер облачных рабочих станций

  1. Откройте облачные рабочие станции в облачной консоли. Подождите, пока кластер перейдет в состояние READY .

305e1a3d63ac7ff6.png

Создание конфигурации рабочих станций

  1. Если ваш сеанс Cloud Shell был отключен, нажмите «Повторно подключиться», а затем запустите команду gcloud cli, чтобы установить идентификатор проекта. Перед запуском команды замените приведенный ниже идентификатор примера проекта на идентификатор вашего проекта qwiklabs.
gcloud config set project qwiklabs-gcp-project-id
  1. Загрузите и запустите приведенный ниже скрипт в терминале, чтобы создать конфигурацию облачных рабочих станций.
wget https://raw.githubusercontent.com/GoogleCloudPlatform/container-developer-workshop/main/labs/python/workstation_config_setup.sh
chmod +x workstation_config_setup.sh
./workstation_config_setup.sh
  1. Проверьте результаты в разделе «Конфигурации». Переход в состояние ГОТОВНОСТЬ займет 2 минуты.

2e23c2e9983d1ccf.png

  1. Откройте Cloud Workstations в консоли и создайте новый экземпляр.

a53adeeac81a78c8.png

  1. Измените имя на my-workstation и выберите существующую конфигурацию: codeoss-python .

f052cd47701ec774.png

  1. Проверьте результаты в разделе «Рабочие станции».

Запустить рабочую станцию

  1. Запустите и запустите рабочую станцию. Запуск рабочей станции займет пару минут.

682f8a307032cba3.png

  1. Разрешите сторонние файлы cookie, нажав на значок в адресной строке. 1b8923e2943f9bc4.png

fcf9405b6957b7d7.png

  1. Нажмите «Сайт не работает?».

36a84c0e2e3b85b.png

  1. Нажмите «Разрешить файлы cookie».

2259694328628fba.png

  1. После запуска рабочей станции вы увидите Code OSS IDE. Нажмите «Отметить готово» на странице «Начало работы» в среде IDE рабочей станции.

94874fba9b74cc22.png

3. Создайте новое начальное приложение Python.

В этом разделе вы создадите новое приложение Python.

  1. Откройте новый терминал.

c31d48f2e4938c38.png

  1. Создайте новый каталог и откройте его как рабочую область.
mkdir music-service && cd music-service

code-oss-cloud-workstations -r --folder-uri="$PWD"

Если вы видите это сообщение, нажмите кнопку «Разрешить», чтобы можно было скопировать и вставить на рабочую станцию.

58149777e5cc350a.png

  1. Создайте файл с именем requirements.txt и скопируйте в него следующее содержимое.

789e8389170bd900.png

Flask
gunicorn
google-cloud-spanner
ptvsd==4.3.2
  1. Создайте файл с именем app.py и вставьте в него следующий код.
import os
from flask import Flask, request, jsonify
from google.cloud import spanner

app = Flask(__name__)

@app.route("/")
def hello_world():
    message="Hello, World!"
    return message

if __name__ == '__main__':
    server_port = os.environ.get('PORT', '8080')
    app.run(debug=False, port=server_port, host='0.0.0.0')

  1. Создайте файл с именем Dockerfile и вставьте в него следующее:
FROM python:3.8
ARG FLASK_DEBUG=0
ENV FLASK_DEBUG=$FLASK_DEBUG
ENV FLASK_APP=app.py
WORKDIR /app
COPY requirements.txt .
RUN pip install --trusted-host pypi.python.org -r requirements.txt
COPY . .
ENTRYPOINT ["python3", "-m", "flask", "run", "--port=8080", "--host=0.0.0.0"]

Примечание . FLASK_DEBUG=1 позволяет автоматически перезагружать изменения кода в приложение Python flask. Этот Dockerfile позволяет вам передать это значение в качестве аргумента сборки.

Генерировать манифесты

В вашем терминале выполните следующую команду, чтобы создать файлы skaffold.yaml и Deployment.yaml по умолчанию.

  1. Инициализируйте Skaffold с помощью следующей команды
skaffold init --generate-manifests

При появлении запроса используйте стрелки для перемещения курсора и пробел для выбора параметров.

Выбирать:

  • 8080 для порта
  • y , чтобы сохранить конфигурацию

Обновление конфигураций Skaffold

  • Изменить имя приложения по умолчанию
  • Открыть skaffold.yaml
  • Выберите имя изображения, установленное в настоящее время как dockerfile-image
  • Щелкните правой кнопкой мыши и выберите «Изменить все вхождения».
  • Введите новое имя как python-app
  • Далее отредактируйте раздел сборки, чтобы
  • добавьте docker.buildArgs для передачи FLASK_DEBUG=1
  • Синхронизируйте настройки для загрузки любых изменений в файлы *.py из IDE в работающий контейнер.

После изменений раздел сборки в файле skaffold.yaml будет выглядеть следующим образом:

build:
 artifacts:
 - image: python-app
   docker:
     buildArgs:
       FLASK_DEBUG: "1"
     dockerfile: Dockerfile
   sync:
     infer:
     - '**/*.py'

Изменить файл конфигурации Kubernetes

  1. Изменить имя по умолчанию
  • Откройте файл deployment.yaml .
  • Выберите имя изображения, установленное в настоящее время как dockerfile-image
  • Щелкните правой кнопкой мыши и выберите «Изменить все вхождения».
  • Введите новое имя как python-app

4. Прогулка по процессу разработки

После добавления бизнес-логики вы можете развернуть и протестировать свое приложение. В следующем разделе будет продемонстрировано использование плагина Cloud Code. Помимо прочего, этот плагин интегрируется со skaffold, чтобы упростить процесс разработки. Когда вы выполните развертывание в GKE, выполнив следующие шаги, Cloud Code и Skaffold автоматически создадут образ вашего контейнера, отправят его в реестр контейнеров, а затем развернут your приложение в GKE. Это происходит «за кулисами», отвлекая детали от процесса разработки.

Войдите в Google Cloud

  1. Нажмите на значок Cloud Code и выберите «Войти в Google Cloud»:

1769afd39be372ff.png

  1. Нажмите «Продолжить вход».

923bb1c8f63160f9.png

  1. Проверьте вывод в Терминале и откройте ссылку:

517fdd579c34aa21.png

  1. Войдите, используя свои учетные данные студента Qwiklabs.

db99b345f7a8e72c.png

  1. Выберите «Разрешить»:

а5376553c430ac84.png

  1. Скопируйте код подтверждения и вернитесь на вкладку «Рабочая станция».

6719421277b92eac.png

  1. Вставьте код подтверждения и нажмите Enter.

e9847cfe3fa8a2ce.png

Добавить кластер Kubernetes

  1. Добавить кластер

62a3b97bdbb427e5.png

  1. Выберите Google Kubernetes Engine:

9577de423568bbaa.png

  1. Выберите проект.

c5202fcbeebcd41c.png

  1. Выберите «python-cluster», созданный при первоначальной настройке.

719c2fc0a7f9e84f.png

  1. Кластер теперь отображается в списке кластеров Kubernetes в разделе Cloud Code. Перемещайтесь и исследуйте кластер отсюда.

7e5f50662d4eea3c.png

Установите текущий идентификатор проекта с помощью gcloud cli

  1. Скопируйте идентификатор проекта для этой лабораторной работы со страницы qwiklabs.

fcff2d10007ec5bc.png

  1. В терминале выполните команду gcloud cli, чтобы установить идентификатор проекта. Замените идентификатор примера проекта перед запуском команды. ЗАМЕНИТЕ идентификатор проекта перед запуском команды ниже.
gcloud config set project qwiklabs-gcp-project-id

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

  1. На панели внизу редактора Cloud Shell выберите Cloud Code 

d99a88992e15fea9.png

  1. На появившейся вверху панели выберите «Выполнить в Kubernetes» . При появлении запроса выберите «Да», чтобы использовать текущий контекст Kubernetes.

bfd65e9df6d4a6cb.png

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

  1. При первом запуске команды в верхней части экрана появится приглашение с вопросом, хотите ли вы использовать текущий контекст Kubernetes, выберите «Да», чтобы принять и использовать текущий контекст.
  2. Затем появится приглашение с вопросом, какой реестр контейнеров использовать. Нажмите Enter, чтобы принять предоставленное значение по умолчанию.
  3. Выберите вкладку «Вывод» на нижней панели, чтобы просмотреть ход выполнения и уведомления. В раскрывающемся списке выберите «Kubernetes: Run/Debug».

9c87ccbf5d06f50a.png

  1. Выберите «Kubernetes: Run/Debug — Detailed» в раскрывающемся списке каналов справа, чтобы просмотреть дополнительные сведения и журналы, транслируемые в реальном времени из контейнеров.

804abc8833ffd571.png

После завершения сборки и тестирования в журналах вкладки «Вывод» URL-адрес http://localhost:8080 будет указан в представлении «Kubernetes: Run/Debug».

  1. В терминале Cloud Code наведите указатель мыши на первый URL-адрес в выводе (http://localhost:8080), а затем в появившейся подсказке выберите «Открыть веб-просмотр».
  2. Откроется новая вкладка браузера и отобразится сообщение Hello, World!

Горячая перезагрузка

  1. Откройте файл app.py
  2. Измените приветственное сообщение на Hello from Python

Сразу обратите внимание, что в окне Output в представлении Kubernetes: Run/Debug наблюдатель синхронизирует обновленные файлы с контейнером в Kubernetes.

Update initiated
Build started for artifact python-app
Build completed for artifact python-app

Deploy started
Deploy completed

Status check started
Resource pod/python-app-6f646ffcbb-tn7qd status updated to In Progress
Resource deployment/python-app status updated to In Progress
Resource deployment/python-app status completed successfully
Status check succeeded
...
  1. Если вы переключитесь на Kubernetes: Run/Debug - Detailed вид», вы заметите, что он распознает изменения файлов, затем собирает и повторно развертывает приложение.
files modified: [app.py]
Syncing 1 files for gcr.io/veer-pylab-01/python-app:3c04f58-dirty@sha256:a42ca7250851c2f2570ff05209f108c5491d13d2b453bb9608c7b4af511109bd
Copying files:map[app.py:[/app/app.py]]togcr.io/veer-pylab-01/python-app:3c04f58-dirty@sha256:a42ca7250851c2f2570ff05209f108c5491d13d2b453bb9608c7b4af511109bd
Watching for changes...
[python-app] * Detected change in '/app/app.py', reloading
[python-app] * Restarting with stat
[python-app] * Debugger is active!
[python-app] * Debugger PIN: 744-729-662
  1. Обновите вкладку браузера, на которой вы видели предыдущие результаты, чтобы увидеть обновленные результаты.

Отладка

  1. Перейдите в представление «Отладка» и остановите текущий поток. 647213126d7a4c7b.png . Если он спросит, вы можете выбрать очистку после каждого запуска.
  2. 70d6bd947d04d1e6.png
  3. Нажмите Cloud Code в нижнем меню и выберите Debug on Kubernetes чтобы запустить приложение в режиме debug .
  • Обратите внимание, что в окне Kubernetes Run/Debug - Detailed представление Output » skaffold развернет это приложение в режиме отладки.
  1. Когда процесс завершится. Вы заметите прикрепленный отладчик, а на вкладке «Вывод» появится сообщение: Attached debugger to container "python-app-8476f4bbc-h6dsl" successfully. , и отображается URL-адрес http://localhost:8080.
Port forwarding pod/python-app-8bd64cf8b-cskfl in namespace default, remote port 5678 -> http://127.0.0.1:5678
  1. Нижняя строка состояния меняет цвет с синего на оранжевый, указывая на то, что система находится в режиме отладки.
  2. Обратите внимание, что в представлении Kubernetes Run/Debug запускается отладочный контейнер.
**************URLs*****************
Forwarded URL from service python-app: http://localhost:8080
Debuggable container started pod/python-app-8bd64cf8b-cskfl:python-app (default)
Update succeeded
***********************************

Используйте точки останова

  1. Откройте файл app.py
  2. Найдите оператор, который читает return message
  3. Добавьте точку останова в эту строку, щелкнув пустое место слева от номера строки. Красный индикатор покажет, что точка останова установлена.
  4. При первом запуске появится запрос, где находится источник внутри контейнера. Это значение связано с каталогами в Dockerfile.

Нажмите Enter, чтобы принять значение по умолчанию

fccc866f32b5ed86.png

Сборка и развертывание приложения займет пару минут.

  1. Перезагрузите браузер и обратите внимание, что отладчик останавливает процесс в точке останова и позволяет вам исследовать переменные и состояние приложения, которое работает удаленно в GKE.
  2. Нажмите вниз в раздел ПЕРЕМЕННЫЕ.
  3. Нажмите «Локальные», там вы найдете переменную "message" .
  4. Дважды щелкните имя переменной «сообщение» и во всплывающем окне измените значение на другое, например "Greetings from Python"
  5. Нажмите кнопку «Продолжить» на панели управления отладкой. 607c33934f8d6b39.png
  6. Просмотрите ответ в своем браузере, который теперь показывает обновленное значение, которое вы только что ввели.
  7. Остановите режим «Отладка», нажав кнопку «Стоп». 647213126d7a4c7b.png и удалите точку останова, снова нажав на точку останова.

5. Разработка простой службы отдыха CRUD

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

Код остальной службы

Код ниже создает простую службу отдыха, которая использует Spanner в качестве базы данных, поддерживающей приложение. Создайте приложение, скопировав в него следующий код.

  1. Создайте основное приложение, заменив app.py следующим содержимым.
import os
from flask import Flask, request, jsonify
from google.cloud import spanner


app = Flask(__name__)


instance_id = "music-catalog"

database_id = "musicians"

spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)


@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

@app.route('/singer', methods=['POST'])
def create():
    try:
        request_json = request.get_json()
        singer_id = request_json['singer_id']
        first_name = request_json['first_name']
        last_name = request_json['last_name']
        def insert_singers(transaction):
            row_ct = transaction.execute_update(
                f"INSERT Singers (SingerId, FirstName, LastName) VALUES" \
                f"({singer_id}, '{first_name}', '{last_name}')"
            )
            print("{} record(s) inserted.".format(row_ct))

        database.run_in_transaction(insert_singers)

        return {"Success": True}, 200
    except Exception as e:
        return e



@app.route('/singer', methods=['GET'])
def get_singer():

    try:
        singer_id = request.args.get('singer_id')
        def get_singer():
            first_name = ''
            last_name = ''
            with database.snapshot() as snapshot:
                results = snapshot.execute_sql(
                    f"SELECT SingerId, FirstName, LastName FROM Singers " \
                    f"where SingerId = {singer_id}",
                    )
                for row in results:
                    first_name = row[1]
                    last_name = row[2]
                return (first_name,last_name )
        first_name, last_name = get_singer()  
        return {"first_name": first_name, "last_name": last_name }, 200
    except Exception as e:
        return e


@app.route('/singer', methods=['PUT'])
def update_singer_first_name():
    try:
        singer_id = request.args.get('singer_id')
        request_json = request.get_json()
        first_name = request_json['first_name']
        
        def update_singer(transaction):
            row_ct = transaction.execute_update(
                f"UPDATE Singers SET FirstName = '{first_name}' WHERE SingerId = {singer_id}"
            )

            print("{} record(s) updated.".format(row_ct))

        database.run_in_transaction(update_singer)
        return {"Success": True}, 200
    except Exception as e:
        return e


@app.route('/singer', methods=['DELETE'])
def delete_singer():
    try:
        singer_id = request.args.get('singer')
    
        def delete_singer(transaction):
            row_ct = transaction.execute_update(
                f"DELETE FROM Singers WHERE SingerId = {singer_id}"
            )
            print("{} record(s) deleted.".format(row_ct))

        database.run_in_transaction(delete_singer)
        return {"Success": True}, 200
    except Exception as e:
        return e

port = int(os.environ.get('PORT', 8080))
if __name__ == '__main__':
    app.run(threaded=True, host='0.0.0.0', port=port)

Добавить конфигурации базы данных

Чтобы безопасно подключиться к Spanner, настройте приложение на использование удостоверений рабочей нагрузки. Это позволяет вашему приложению действовать как собственная учетная запись службы и иметь индивидуальные разрешения при доступе к базе данных.

  1. Обновите deployment.yaml . Добавьте следующий код в конец файла (убедитесь, что отступы табуляции сохранены, как в примере ниже):
      serviceAccountName: python-ksa
      nodeSelector:
        iam.gke.io/gke-metadata-server-enabled: "true" 

После изменений раздел спецификации должен выглядеть так

   spec:
     containers:
     - name: python-app
       image: python-app
     serviceAccountName: python-ksa
     nodeSelector:
       iam.gke.io/gke-metadata-server-enabled: "true"

Развертывание и проверка приложения

  1. На панели внизу редактора Cloud Shell выберите Cloud Code , затем выберите Debug on Kubernetes вверху экрана.
  2. Когда сборка и тесты завершены, на вкладке «Вывод» отображается сообщение: Resource deployment/python-app status completed successfully » и указан URL-адрес: «Перенаправленный URL-адрес из службы python-app: http://localhost:8080».
  3. Добавьте пару записей.

В терминале CloudShell выполните команду ниже

curl -X POST http://localhost:8080/singer -H 'Content-Type: application/json' -d '{"first_name":"Cat","last_name":"Meow", "singer_id": 6}'
  1. Проверьте GET, выполнив команду ниже в терминале
curl -X GET http://localhost:8080/singer?singer_id=6
  1. Тестовое удаление: теперь попробуйте удалить запись, выполнив следующую команду. При необходимости измените значение item-id.
curl -X DELETE http://localhost:8080/singer?singer_id=6
    This throws an error message
500 Internal Server Error

Определите и устраните проблему

  1. Режим отладки и найдите проблему. Вот несколько советов:
  • Мы знаем, что с командой DELETE что-то не так, поскольку она не возвращает желаемый результат. Итак, вы должны установить точку останова в app.py в методе delete_singer .
  • Запустите пошаговое выполнение и наблюдайте за переменными на каждом шаге, чтобы увидеть значения локальных переменных в левом окне.
  • Чтобы наблюдать за конкретными значениями, такими как singer_id и request.args , добавьте эти переменные в окно Watch.
  1. Обратите внимание, что значение, присвоенное singer_id равно None . Измените код, чтобы устранить проблему.

Фиксированный фрагмент кода будет выглядеть так.

@app.route('/delete-singer', methods=['DELETE', 'GET'])
def delete_singer():
    try:
        singer_id = request.args.get('singer_id')
  1. После перезапуска приложения проверьте еще раз, попытавшись удалить.
  2. Остановите сеанс отладки, щелкнув красный квадрат на панели инструментов отладки. 647213126d7a4c7b.png

6. Очистка

Поздравляем! В ходе этой лабораторной работы вы создали новое приложение Python с нуля и настроили его для эффективной работы с контейнерами. Затем вы развернули и отладили свое приложение в удаленном кластере GKE, следуя той же схеме разработки, что и в традиционных стеках приложений.

Чтобы навести порядок после завершения лабораторной работы:

  1. Удалите файлы, используемые в лаборатории.
cd ~ && rm -rf ~/music-service
  1. Удалите проект, чтобы удалить всю связанную инфраструктуру и ресурсы.