Техническое практическое руководство Duet AI для разработчиков Codelab

1. Цели

Цель этого семинара — предоставить пользователям и специалистам практическое обучение работе с Duet AI.

В ходе этого практического занятия вы изучите следующее:

  1. Активируйте Duet AI в своем проекте GCP и настройте его для использования в IDE и облачной консоли.
  2. Используйте Duet AI для генерации кода, автозавершения и пояснения.
  3. Используйте Duet AI для объяснения и устранения неполадок в приложении.
  4. Функции Duet AI включают в себя чат в IDE и многоходовый чат, генерацию кода в чате и непосредственно в коде, интеллектуальные действия, такие как объяснение кода и подтверждение участия в обсуждении, и многое другое.

Повествование

Чтобы продемонстрировать, как Duet AI for Developers используется в повседневной разработке, занятия на этом семинаре проходят в повествовательном контексте.

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

Поскольку разработчик недавно работает в компании, он будет использовать Duet AI для генерации кода, его объяснений и документирования.

После завершения разработки сервиса администратор платформы использует Duet AI (чат) для создания артефакта (контейнера Docker) и ресурсов, необходимых для развертывания артефакта в GCP (например, реестр артефактов, разрешения IAM, репозиторий кода, вычислительная инфраструктура, например, GKE или CloudRun и т. д.).

После развертывания приложения в GCP оператор приложения/SRE будет использовать Duet AI (и Cloud Ops) для устранения ошибок в новом сервисе.

Персона

Данный семинар охватывает следующие категории участников:

  1. Разработчик приложений — требуются базовые знания в области программирования и разработки программного обеспечения.

Этот вариант мастер-класса Duet AI предназначен только для разработчиков. Знание облачных ресурсов GCP не требуется. Скрипты для сборки необходимых ресурсов GCP для запуска этого приложения можно найти здесь . Вы можете следовать инструкциям в этом руководстве для развертывания необходимых ресурсов GCP.

2. Подготовка окружающей среды

Активация Duet AI

Вы можете активировать Duet AI в проекте GCP либо через API (gcloud или инструменты IaC, такие как Terraform), либо через пользовательский интерфейс Cloud Console.

Для активации Duet AI в проекте Google Cloud необходимо включить API Cloud AI Companion и предоставить пользователям роли управления идентификацией и доступом (IAM) для Cloud AI Companion User и Service Usage Viewer.

Через gcloud

Активировать Cloud Shell:

Настройте PROJECT_ID ) и USER , а также включите API Cloud AI Companion.

export PROJECT_ID=<YOUR PROJECT ID>
export USER=<YOUR USERNAME> # Use your full LDAP, e.g. name@example.com
gcloud config set project ${PROJECT_ID}
gcloud services enable cloudaicompanion.googleapis.com --project ${PROJECT_ID}

Результат выглядит следующим образом:

Updated property [core/project].
Operation "operations/acat.p2-60565640195-f37dc7fe-b093-4451-9b12-934649e2a435" finished successfully.

Предоставьте учетной записи пользователя роли управления идентификацией и доступом (IAM) для Cloud AI Companion User и Service Usage Viewer. API Cloud Companion работает в фоновом режиме как в IDE, так и в консоли, которые мы будем использовать. Разрешение Service Usage Viewer используется для быстрой проверки перед включением пользовательского интерфейса в консоли (чтобы пользовательский интерфейс Duet отображался только в проектах, в которых включен API).

gcloud projects add-iam-policy-binding  ${PROJECT_ID} \
--member=user:${USER} --role=roles/cloudaicompanion.user

gcloud projects add-iam-policy-binding  ${PROJECT_ID} \
--member=user:${USER} --role=roles/serviceusage.serviceUsageViewer

Результат выглядит следующим образом:

...
- members:
  - user:<YOUR USER ACCOUNT>
  role: roles/cloudaicompanion.user

...
- members:
  - user:<YOUR USER ACCOUNT>
  role: roles/serviceusage.serviceUsageViewer

Через облачную консоль

Чтобы включить API, перейдите на страницу Cloud AI Companion API в консоли Google Cloud.

В окне выбора проекта выберите проект.

Нажмите «Включить» .

Страница обновляется и отображает статус «Включено» . Duet AI теперь доступен в выбранном проекте Google Cloud всем пользователям, имеющим необходимые роли IAM.

Чтобы предоставить необходимые роли IAM для использования Duet AI, перейдите на страницу IAM .

В столбце «Основной пользователь» найдите пользователя, которому вы хотите предоставить доступ к Duet AI, а затем нажмите значок карандаша ✏️ «Изменить основной пользователь» в этой строке.

В панели «Редактировать доступ» нажмите « Добавить еще одну роль» .

В разделе «Выберите роль» выберите «Пользователь Cloud AI Companion» .

Нажмите «Добавить еще одну роль» и выберите «Просмотрщик использования сервиса» .

Нажмите « Сохранить ».

Настройка IDE

Разработчики могут выбирать из множества IDE, которые наилучшим образом соответствуют их потребностям. Функция помощи в написании кода Duet AI доступна в нескольких IDE, таких как Visual Studio Code , IDE от JetBrains (IntelliJ, PyCharm, GoLand, WebStorm и другие), Cloud Workstations , Cloud Shell Editor .

В этой лабораторной работе вы можете использовать либо облачные рабочие станции , либо редактор облачной оболочки.

В этом мастер-классе используется редактор Cloud Shell.

Обратите внимание, что настройка облачных рабочих станций может занять 20-30 минут.

Для немедленного использования воспользуйтесь редактором Cloud Shell Editor .

Откройте редактор Cloud Shell, нажав на значок карандаша ✏️ в верхней панели меню Cloud Shell.

Редактор Cloud Shell имеет очень похожий пользовательский интерфейс и взаимодействие с пользователем, как и VSCode.

d6a6565f83576063.png

Нажмите CTRL (в Windows)/CMD (в Mac) + , (запятая), чтобы открыть панель настроек.

В строке поиска введите "duet ai".

Убедитесь или включите Cloudcode › Duet AI: включить и Cloudcode › Duet AI › Встроенные предложения: включить автоматически

111b8d587330ec74.png

В нижней строке состояния нажмите «Cloud Code — Войти» и следуйте инструкциям по входу в систему.

Если вы уже вошли в систему, в строке состояния отобразится Cloud Code - Нет проекта .

Нажмите на «Cloud Code - No project», и вверху появится выпадающее меню действий. Нажмите « Select a Google Cloud project» .

3241a59811e3c84a.png

Начните вводить идентификатор вашего проекта, и ваш проект должен появиться в списке.

c5358fc837588fe.png

Выберите свой PROJECT_ID из списка проектов.

В нижней строке состояния отображается идентификатор вашего проекта. Если этого не происходит, возможно, вам потребуется обновить вкладку «Редактор Cloud Shell».

Нажмите на значок Duet AI. d97fc4e7b594c3af.png В левой боковой панели меню появится окно чата Duet AI. Если появится сообщение «Выберите проект GCP», щелкните по нему и выберите проект снова.

Теперь вы видите окно чата Duet AI.

781f888360229ca6.png

3. Создание инфраструктуры

d3234d237f00fdbb.png

Для запуска новой службы доставки в GCP вам потребуются следующие ресурсы GCP:

  1. Экземпляр Cloud SQL с базой данных.
  2. Кластер GKE для запуска контейнеризированного сервиса.
  3. Реестр артефактов для хранения образа Docker.
  4. Облачный репозиторий исходного кода.

В терминале Cloud Shell клонируйте следующий репозиторий и выполните следующие команды для настройки инфраструктуры в вашем проекте GCP.

# Set your project
export PROJECT_ID=<INSERT_YOUR_PROJECT_ID>
gcloud config set core/project ${PROJECT_ID}

# Enable Cloudbuild and grant Cloudbuild SA owner role 
export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format 'value(projectNumber)')
gcloud services enable cloudbuild.googleapis.com
gcloud projects add-iam-policy-binding ${PROJECT_ID} --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com --role roles/owner

# Clone the repo
git clone https://github.com/duetailabs/dev.git ~/duetaidev
cd ~/duetaidev

# Run Cloudbuild to create the necessary resources
gcloud builds submit --substitutions=_PROJECT_ID=${PROJECT_ID}

# To destroy all GCP resources, run the following
# gcloud builds submit --substitutions=_PROJECT_ID=${PROJECT_ID} --config=cloudbuild_destroy.yaml

4. Разработка сервиса Flask на Python.

9745ba5c70782e76.png

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

  1. package-service.yaml — Спецификация Open API для службы доставки пакетов, содержащая такие данные, как высота, ширина, вес и специальные инструкции по обработке.
  2. data_model.py — Модель данных для спецификации API package-service. Также создает таблицу packages в базе данных product_details.
  3. connect_connector.py — Подключение к CloudSQL (определяет движок, сессию и базовый ORM)
  4. db_init.py — генерирует примерные данные для таблицы packages .
  5. main.py - Сервис Python Flask с GET запросом для получения сведений о пакете на основе данных packages по полю product_id.
  6. test.py - Модульный тест
  7. requirement.txt - Требования Python
  8. Dockerfile — для контейнеризации этого приложения.

Если во время выполнения упражнений у вас возникнут какие-либо сложности, все итоговые файлы находятся в ПРИЛОЖЕНИИ к данному практическому руководству.

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

В терминале Cloud Shell выполните следующую команду для клонирования репозитория.

cd ~
gcloud source repos clone shipping shipping
cd ~/shipping 

Откройте боковую панель чата Duet AI из левого меню редактора Cloud Shell. Значок выглядит так: 8b135a000b259175.png Теперь вы можете использовать Duet AI для помощи в написании кода.

package-service.yaml

Не открывая никаких файлов, попросите Duet сгенерировать спецификацию Open API для службы доставки.

Задание 1: Сгенерируйте спецификацию OpenAPI в формате YAML для сервиса, предоставляющего информацию о доставке и упаковке на основе числового идентификатора продукта. Сервис должен включать информацию о высоте, ширине, глубине, весе упаковки и любых специальных инструкциях по обработке.

ba12626f491a1204.png

В правом верхнем углу окна сгенерированного кода перечислены три варианта.

Вы можете либо COPY 71194556d8061dae.png Вставьте код в файл.

Вы можете ADD df645de8c65607a.png Код открытого в данный момент файла в редакторе.

Или вы можете OPEN a4c7ed6d845df343.png Код находится в новом файле.

Нажмите кнопку « OPEN a4c7ed6d845df343.png Код находится в новом файле.

Нажмите CTRL/CMD + s , чтобы сохранить файл, и сохраните его в папке приложения под именем package-service.yaml . Нажмите OK.

f6ebd5b836949366.png

Итоговый файл находится в разделе ПРИЛОЖЕНИЕ к данному практическому заданию. Если его там нет, внесите необходимые изменения вручную.

Вы также можете попробовать различные подсказки, чтобы увидеть ответы Duet AI.

Чтобы сбросить историю чата Duet AI, нажмите на значок корзины. f574ca2c1e114856.png в верхней части боковой панели Duet AI.

data_model.py

Далее вы создаете файл Python с моделью данных для сервиса на основе спецификации OpenAPI.

Откройте файл package-service.yaml и введите следующую команду.

Задание 1: Используя ORM sqlalchemy для Python, сгенерируйте модель данных для этого API-сервиса. Также добавьте отдельную функцию и основную точку входа, которые создают таблицы базы данных.

b873a6a28bd28ca1.png

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

Во-первых, существует класс под названием Package типа Base , который определяет модель данных для базы данных packages следующим образом:

class Package(Base):
    __tablename__ = 'packages'

    id = Column(Integer, primary_key=True)
    product_id = Column(String(255))
    height = Column(Float)
    width = Column(Float)
    depth = Column(Float)
    weight = Column(Float)
    special_handling_instructions = Column(String(255))

Далее вам понадобится функция, которая создаст таблицу в базе данных, примерно следующего вида:

def create_tables(engine):
    Base.metadata.create_all(engine)

Наконец, вам потребуется основная функция, которая запускает функцию create_tables для фактического создания таблицы в базе данных CloudSQL, примерно следующего вида:

if __name__ == '__main__':
    from sqlalchemy import create_engine

    engine = create_engine('sqlite:///shipping.db')
    create_tables(engine)

    print('Tables created successfully.')

Обратите внимание, что main функция — создание движка с использованием локальной базы данных sqlite . Для использования CloudSQL вам потребуется внести изменения. Это вы сделаете чуть позже.

Используя OPEN a4c7ed6d845df343.png Сохраните код в новом файле, как и раньше. Затем сохраните код в файле с именем data_model.py (обратите внимание на нижнее подчеркивание в имени, а не на дефис).

Чтобы сбросить историю чата Duet AI, нажмите на значок корзины. f574ca2c1e114856.png в верхней части боковой панели Duet AI.

connect-connector.py

Создайте коннектор CloudSQL.

Откройте файл data_model.py и введите следующие команды.

Задание 1: Используя библиотеку cloud-sql-python-connector, создайте функцию, которая инициализирует пул соединений для экземпляра Cloud SQL в Postgres.

ed05cb6ff85d34c5.png

Обратите внимание, что в ответе не используется библиотека cloud-sql-python-connector . Вы можете уточнить подсказки, чтобы немного подтолкнуть Duet к действиям, добавив конкретные детали в тот же чат.

Давайте воспользуемся другой подсказкой.

Подсказка 2: Необходимо использовать библиотеку cloud-sql-python-connector.

d09095b44dde35bf.png

Убедитесь, что используется библиотека cloud-sql-python-connector .

Используя OPEN a4c7ed6d845df343.png Сохраните код в новом файле, как и раньше. Затем сохраните код в файле с именем connect_conector.py . Возможно, вам потребуется вручную импортировать библиотеку pg8000 ; см. файл ниже.

Очистите историю чата Duet AI, а затем, открыв файл connect_connector.py , сгенерируйте DB engine , sessionmaker и base ORM для использования в приложении.

Задание 1: Создайте движок, класс SessionMaker и базовый ORM, используя метод connect_with_connector.

6e4214b72ab13a63.png

В ответном сообщении могут быть добавлены данные engine , Session и Base в файл connect_connector.py .

Итоговый файл находится в разделе ПРИЛОЖЕНИЕ к данному практическому заданию. Если его там нет, внесите необходимые изменения вручную.

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

Чтобы сбросить историю чата Duet AI, нажмите на значок корзины. f574ca2c1e114856.png в верхней части боковой панели Duet AI.

Обновление файла data_model.py

Для создания таблицы в базе данных CloudSQL вам необходимо использовать механизм, созданный на предыдущем шаге (в файле connect_connector.py ).

Очистите историю чата Duet AI. Откройте файл data_model.py . Попробуйте выполнить следующую команду.

Задание 1: В главной функции импортируйте и используйте движок из файла connect_connector.py.

2e768c9b6c523b9a.png

Вы должны увидеть ответ, в котором импортируется engine из connect_connector (для CloudSQL). Функция create_table использует этот движок (вместо локальной базы данных sqlite по умолчанию).

Обновите файл data_model.py .

Итоговый файл находится в разделе ПРИЛОЖЕНИЕ к данному практическому заданию. Если его там нет, внесите необходимые изменения вручную.

Вы также можете попробовать различные подсказки, чтобы увидеть разные ответы ИИ в Duet.

Чтобы сбросить историю чата Duet AI, нажмите на значок корзины. f574ca2c1e114856.png в верхней части боковой панели Duet AI.

requirements.txt

Создайте файл requirements.txt для приложения.

Откройте файлы connect_connector.py и data_model.py и введите следующую команду.

Задание 1: Сгенерируйте файл требований pip для этой модели данных и сервиса.

Задание 2: Сгенерируйте файл требований pip для этой модели данных и сервиса, используя последние версии.

69fae373bc5c6a18.png

Убедитесь в правильности названий и версий. Например, в приведенном выше ответе название и версия google-cloud-sql-connecter указаны неверно. Исправьте версии вручную и создайте файл requirements.txt следующего вида:

cloud-sql-python-connector==1.2.4
sqlalchemy==1.4.36
pg8000==1.22.0

В командной строке выполните следующую команду:

pip3 install -r requirements.txt

Чтобы сбросить историю чата Duet AI, нажмите на значок корзины. f574ca2c1e114856.png в верхней части боковой панели Duet AI.

Создание таблицы пакетов в CloudSQL

Настройте переменные среды для коннектора базы данных CloudSQL.

export INSTANCE_NAME=$(gcloud sql instances list --format='value(name)')
export INSTANCE_CONNECTION_NAME=$(gcloud sql instances describe ${INSTANCE_NAME} --format="value(connectionName)")
export DB_USER=evolution
export DB_PASS=evolution
export DB_NAME=product_details

Теперь запустите файл data_model.py.

python data_model.py

Результат будет примерно таким (проверьте код, чтобы увидеть, что на самом деле ожидается):

Tables created successfully.

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

gcloud sql connect ${INSTANCE_NAME} --user=evolution --database=product_details

После ввода пароля (также evolution ) получите таблицы.

product_details=> \dt

Результат будет примерно следующим:

           List of relations
 Schema |   Name   | Type  |   Owner   
--------+----------+-------+-----------
 public | packages | table | evolution
(1 row)

Вы также можете проверить модель данных и подробные сведения о таблице.

product_details=> \d+ packages

Результат будет примерно следующим:

                                                                        Table "public.packages"
            Column             |       Type        | Collation | Nullable |               Default                | Storage  | Compression | Stats target | Description 
-------------------------------+-------------------+-----------+----------+--------------------------------------+----------+-------------+--------------+-------------
 id                            | integer           |           | not null | nextval('packages_id_seq'::regclass) | plain    |             |              | 
 product_id                    | integer           |           | not null |                                      | plain    |             |              | 
 height                        | double precision  |           | not null |                                      | plain    |             |              | 
 width                         | double precision  |           | not null |                                      | plain    |             |              | 
 depth                         | double precision  |           | not null |                                      | plain    |             |              | 
 weight                        | double precision  |           | not null |                                      | plain    |             |              | 
 special_handling_instructions | character varying |           |          |                                      | extended |             |              | 
Indexes:
    "packages_pkey" PRIMARY KEY, btree (id)
Access method: heap

Для выхода из CloudSQL введите \q .

db_init.py

Далее добавим несколько примеров данных в таблицу packages .

Очистите историю чата Duet AI. Откройте файл data_model.py и попробуйте выполнить следующие действия.

Задание 1: Создайте функцию, которая создаст 10 строк с примерами пакетов и добавит их в таблицу пакетов.

Задание 2: Используя сессию из connect_connector, создайте функцию, которая создаст 10 строк с примерами пакетов и добавит их в таблицу packages.

34a9afc5f04ba5.png

Используя OPEN a4c7ed6d845df343.png Как и прежде, создайте новый файл с кодом. Сохраните код в файле с именем db_init.py .

Итоговый файл находится в разделе ПРИЛОЖЕНИЕ к данному практическому заданию. Если его там нет, внесите необходимые изменения вручную.

Вы также можете попробовать различные подсказки, чтобы увидеть разные ответы ИИ в Duet.

Чтобы сбросить историю чата Duet AI, нажмите на значок корзины. f574ca2c1e114856.png в верхней части боковой панели Duet AI.

Создание образцов данных пакетов

Запустите файл db_init.py из командной строки.

python db_init.py

Результат будет примерно следующим:

Packages created successfully.

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

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

gcloud sql connect ${INSTANCE_NAME} --user=evolution --database=product_details

После ввода пароля (также evolution ) получите все данные из таблицы пакетов.

product_details=> SELECT * FROM packages;

Результат будет примерно следующим:

 id | product_id | height | width | depth | weight |   special_handling_instructions   
----+------------+--------+-------+-------+--------+-----------------------------------
  1 |          0 |     10 |    10 |    10 |     10 | No special handling instructions.
  2 |          1 |     10 |    10 |    10 |     10 | No special handling instructions.
  3 |          2 |     10 |    10 |    10 |     10 | No special handling instructions.
  4 |          3 |     10 |    10 |    10 |     10 | No special handling instructions.
  5 |          4 |     10 |    10 |    10 |     10 | No special handling instructions.
  6 |          5 |     10 |    10 |    10 |     10 | No special handling instructions.
  7 |          6 |     10 |    10 |    10 |     10 | No special handling instructions.
  8 |          7 |     10 |    10 |    10 |     10 | No special handling instructions.
  9 |          8 |     10 |    10 |    10 |     10 | No special handling instructions.
 10 |          9 |     10 |    10 |    10 |     10 | No special handling instructions.
(10 rows)

Для выхода из CloudSQL введите \q .

main.py

Откройте файлы data_model.py , package-service.yaml и connect_connector.py и создайте файл main.py для приложения.

Задание 1: Используя библиотеку Python Flask, создайте реализацию, которая использует HTTP REST-конечные точки для этого сервиса.

Задание 2: Используя библиотеку Python Flask, создайте реализацию, которая использует HTTP REST-конечные точки для этого сервиса. Импортируйте и используйте SessionMaker из файла connect_conector.py для работы с данными пакетов.

Задание 3: Используя библиотеку Python Flask, создайте реализацию, которая использует HTTP REST-конечные точки для этого сервиса. Импортируйте и используйте Package из файла data_model.py и SessionMaker из файла connect_conector.py для работы с данными пакетов.

Задание 4: Используя библиотеку Python Flask, создайте реализацию, которая использует HTTP REST-конечные точки для этого сервиса. Импортируйте и используйте Package из файла data_model.py и SessionMaker из файла connect_conector.py для работы с данными пакетов. Используйте IP-адрес хоста 0.0.0.0 для запуска приложения.

6d794fc52a90e6ae.png

Обновите зависимости для main.py

Подсказка: Создайте файл требований для main.py

1cc0b318d2d4ca2f.png

Добавьте это в файл requirements.txt . Убедитесь, что используете Flask версии 3.0.0.

Используя OPEN a4c7ed6d845df343.png Как и раньше, создайте новый файл с кодом в рамках одного рабочего процесса. Сохраните код в файле с именем main.py

Итоговый файл находится в разделе ПРИЛОЖЕНИЕ к данному практическому заданию. Если его там нет, внесите необходимые изменения вручную.

Чтобы сбросить историю чата Duet AI, нажмите на значок корзины. f574ca2c1e114856.png в верхней части боковой панели Duet AI.

5. Тестирование и запуск приложения

Установите необходимые зависимости.

pip3 install -r requirements.txt

Запустите main.py

python main.py

Результат будет примерно следующим:

 * Serving Flask app 'main'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://10.88.0.3:5000
Press CTRL+C to quit

Проверьте доступ к конечной точке /packages/<product_id> со второго терминала.

curl localhost:5000/packages/1

Результат будет примерно следующим:

{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}

Вы также можете протестировать любой другой идентификатор продукта в ваших примерах данных.

Нажмите CTRL_C , чтобы выйти из запущенного контейнера Docker в терминале.

Генерация модульных тестов

Откройте файл main.py и сгенерируйте модульные тесты.

Задание 1: Сгенерируйте модульные тесты.

e861e5b63e1b2657.png

Используя OPEN a4c7ed6d845df343.png Как и раньше, создайте новый файл с кодом в рамках одного рабочего процесса. Сохраните код в файле с именем test.py

В функции test_get_package необходимо указать product_id . Вы можете добавить его вручную.

Итоговый файл находится в разделе ПРИЛОЖЕНИЕ к данному практическому заданию. Если его там нет, внесите необходимые изменения вручную.

Чтобы сбросить историю чата Duet AI, нажмите на значок корзины. f574ca2c1e114856.png в верхней части боковой панели Duet AI.

Выполнение модульных тестов

Запустите модульный тест.

python test.py

Результат будет примерно следующим:

.
----------------------------------------------------------------------
Ran 1 test in 1.061s

OK

Закройте все файлы в редакторе Cloud Shell и очистите историю чата, нажав на значок корзины. 1ecccfe10d6c540.png в верхней строке состояния.

Dockerfile

Создайте Dockerfile для этого приложения.

Откройте main.py и попробуйте выполнить следующие действия.

Задание 1: Сгенерируйте Dockerfile для этого приложения.

Задание 2: Сгенерируйте Dockerfile для этого приложения. Скопируйте все файлы в контейнер.

9c473caea437a5c3.png

Также необходимо установить ENVARS для INSTANCE_CONNECTION_NAME , DB_USER , DB_PASS и DB_NAME . Это можно сделать вручную. Ваш Dockerfile должен выглядеть следующим образом:

FROM python:3.10-slim

WORKDIR /app

COPY . ./

RUN pip install -r requirements.txt

# Add these manually for your project
ENV INSTANCE_CONNECTION_NAME=YOUR_INSTANCE_CONNECTION_NAME
ENV DB_USER=evolution
ENV DB_PASS=evolution
ENV DB_NAME=product_details

CMD ["python", "main.py"]

Используя OPEN a4c7ed6d845df343.png Как и раньше, создайте новый файл с кодом в рамках рабочего процесса. Сохраните код в файле с именем Dockerfile.

Итоговый файл находится в разделе ПРИЛОЖЕНИЕ к данному практическому заданию. Если его там нет, внесите необходимые изменения вручную.

Запуск приложения локально.

Откройте файл Dockerfile и попробуйте ввести следующую команду.

Вопрос 1: Как запустить контейнер локально, используя этот Dockerfile?

570fd5c296ca8c83.png

Следуйте инструкциям.

# Build
docker build -t shipping .
# And run
docker run -p 5000:5000 -it shipping

Результат будет примерно следующим:

 * Serving Flask app 'main'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://172.17.0.2:5000
Press CTRL+C to quit

Во втором окне терминала получите доступ к контейнеру.

curl localhost:5000/packages/1

Результат будет примерно следующим:

{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}

Контейнеризированное приложение работает.

Нажмите CTRL_C , чтобы выйти из запущенного контейнера Docker в терминале.

Создание образа контейнера в реестре артефактов

Создайте образ контейнера и загрузите его в реестр артефактов.

cd ~/shipping
gcloud auth configure-docker us-central1-docker.pkg.dev
docker build -t us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping .
docker push us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping

Контейнер приложения теперь находится по адресу us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping и может быть развернут в GKE.

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

При создании ресурсов GCP для этого семинара был создан кластер GKE Autopilot. Подключитесь к кластеру GKE.

gcloud container clusters get-credentials gke1 \
    --region=us-central1

Добавьте в аннотацию к учетной записи службы Kubernetes по умолчанию учетную запись службы Google.

kubectl annotate serviceaccount default iam.gke.io/gcp-service-account=cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com

Результат будет примерно следующим:

serviceaccount/default annotated

Подготовьте и примените файл k8s.yaml.

cp ~/duetaidev/k8s.yaml_tmpl ~/shipping/.
export INSTANCE_NAME=$(gcloud sql instances list --format='value(name)')
export INSTANCE_CONNECTION_NAME=$(gcloud sql instances describe ${INSTANCE_NAME} --format="value(connectionName)")
export IMAGE_REPO=us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping
envsubst < ~/shipping/k8s.yaml_tmpl > k8s.yaml
kubectl apply -f k8s.yaml

Результат будет примерно следующим:

deployment.apps/shipping created
service/shipping created

Дождитесь, пока поды запустятся и сервису будет назначен IP-адрес внешнего балансировщика нагрузки.

kubectl get pods
kubectl get service shipping

Результат будет примерно следующим:

# kubectl get pods
NAME                      READY   STATUS    RESTARTS   AGE
shipping-f5d6f8d5-56cvk   1/1     Running   0          4m47s
shipping-f5d6f8d5-cj4vv   1/1     Running   0          4m48s
shipping-f5d6f8d5-rrdj2   1/1     Running   0          4m47s

# kubectl get service shipping
NAME       TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)        AGE
shipping   LoadBalancer   34.118.225.125   34.16.39.182   80:30076/TCP   5m41s

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

Для доступа к сервису используйте EXTERNAL-IP адрес.

export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1

Результат будет примерно следующим:

{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}

7. Дополнительное задание: Устранение неполадок в приложении.

Удалите роль IAM клиента CloudSQL из учетной записи службы cloudsqlsa . Это приведет к ошибке подключения к базе данных CloudSQL.

gcloud projects remove-iam-policy-binding ${PROJECT_ID} \
    --member="serviceAccount:cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/cloudsql.client"

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

kubectl rollout restart deployment shipping

После перезагрузки Pod попробуйте снова получить доступ к службе shipping .

export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1 

Результат будет примерно следующим:

...
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>

Просмотрите журналы, перейдя в раздел Kubernetes Engine > Workloads.

d225b1916c829167.png

Перейдите в раздел shipping , а затем во вкладку «Журналы» .

1d0459141483d6a7.png

Нажмите « Просмотреть в обозревателе журналов». df8b9d19a9fe4c73.png Значок находится в правой части строки состояния. Он открывает новое окно «Проводник журналов» .

e86d1c265e176bc4.png

Щелкните по одной из записей об ошибке Traceback , а затем нажмите «Показать эту запись в журнале» .

d6af045cf03008bc.png

Вы можете ознакомиться с объяснением ошибки.

Далее, давайте воспользуемся помощью Duet AI для устранения ошибки.

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

Задание 1: Помогите мне устранить эту ошибку.

9288dd6045369167.png

Введите сообщение об ошибке в подсказке.

Сообщение 2: Запрещено: Аутентифицированный субъект IAM, по-видимому, не авторизован для выполнения запроса к API. Убедитесь, что «Cloud SQL Admin API» включен в вашем проекте GCP и что роль «Cloud SQL Client» предоставлена ​​субъекту IAM.

f1e64fbdc435d31c.png

А потом.

Вопрос 3: Как назначить роль клиента Cloud SQL учетной записи службы Google с помощью gcloud?

bb8926b995a8875c.png

Назначьте роль Cloud SQL Client пользователю cloudsqlsa .

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
    --member="serviceAccount:cloudsqlsa@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/cloudsql.client"

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

export EXTERNAL_IP=$(kubectl get svc shipping --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://${EXTERNAL_IP}/packages/1

Результат будет примерно следующим:

{"depth":10.0,"height":10.0,"special_handling_instructions":"No special handling instructions.","weight":10.0,"width":10.0}

Вы успешно использовали Duet AI в Cloud Logging , Log Explorer и функцию Log Explainer для устранения проблемы.

8. Заключение

Поздравляем! Вы успешно завершили этот практический курс.

В ходе этой практической работы вы изучили следующее:

  1. Активируйте Duet AI в своем проекте GCP и настройте его для использования в IDE и облачной консоли.
  2. Используйте Duet AI для генерации кода, автозавершения и пояснения.
  3. Используйте Duet AI для объяснения и устранения неполадок в приложении.
  4. Функции Duet AI включают в себя чат в IDE и многоходовый чат, генерацию кода в чате и непосредственно в коде, интеллектуальные действия, такие как объяснение кода и подтверждение участия в обсуждении, и многое другое.

9. Приложение

package-service.yaml

swagger: "2.0"
info:
 title: Shipping and Package Information API
 description: This API provides information about shipping and packages.
 version: 1.0.0
host: shipping.googleapis.com
schemes:
 - https
produces:
 - application/json
paths:
 /packages/{product_id}:
   get:
     summary: Get information about a package
     description: This method returns information about a package, including its height, width, depth, weight, and any special handling instructions.
     parameters:
       - name: product_id
         in: path
         required: true
         type: integer
         format: int64
     responses:
       "200":
         description: A successful response
         schema:
           type: object
           properties:
             height:
               type: integer
               format: int64
             width:
               type: integer
               format: int64
             depth:
               type: integer
               format: int64
             weight:
               type: integer
               format: int64
             special_handling_instructions:
               type: string
       "404":
         description: The product_id was not found

data_model.py

from sqlalchemy import Column, Integer, String, Float
from sqlalchemy.ext.declarative import declarative_base

from connect_connector import engine

Base = declarative_base()

class Package(Base):
    __tablename__ = 'packages'

    id = Column(Integer, primary_key=True)
    product_id = Column(Integer, nullable=False)
    height = Column(Float, nullable=False)
    width = Column(Float, nullable=False)
    depth = Column(Float, nullable=False)
    weight = Column(Float, nullable=False)
    special_handling_instructions = Column(String, nullable=True)

def create_tables():
    Base.metadata.create_all(engine)

if __name__ == '__main__':
    create_tables()

    print('Tables created successfully.')

connect_connector.py

import os

from google.cloud.sql.connector import Connector, IPTypes
import sqlalchemy

# You may need to manually import pg8000 and Base as follows
import pg8000
from sqlalchemy.ext.declarative import declarative_base


def connect_with_connector() -> sqlalchemy.engine.base.Engine:
   """Initializes a connection pool for a Cloud SQL instance of Postgres."""
   # Note: Saving credentials in environment variables is convenient, but not
   # secure - consider a more secure solution such as
   # Cloud Secret Manager (https://cloud.google.com/secret-manager) to help
   # keep secrets safe.
   instance_connection_name = os.environ[
       "INSTANCE_CONNECTION_NAME"
   ]  # e.g. 'project:region:instance'
   db_user = os.environ["DB_USER"]  # e.g. 'my-database-user'
   db_pass = os.environ["DB_PASS"]  # e.g. 'my-database-password'
   db_name = os.environ["DB_NAME"]  # e.g. 'my-database'

   ip_type = IPTypes.PRIVATE if os.environ.get("PRIVATE_IP") else IPTypes.PUBLIC

   connector = Connector()

   def getconn() -> sqlalchemy.engine.base.Engine:
       conn: sqlalchemy.engine.base.Engine = connector.connect(
           instance_connection_name,
           "pg8000",
           user=db_user,
           password=db_pass,
           db=db_name,
           ip_type=ip_type,
       )
       return conn

   pool = sqlalchemy.create_engine(
       "postgresql+pg8000://",
       creator=getconn,
       # ...
   )
   return pool

# Create a connection pool
engine = connect_with_connector()

# Create a sessionmaker class to create new sessions
SessionMaker = sqlalchemy.orm.sessionmaker(bind=engine)

# Create a Base class for ORM
# You may need to manually fix the following
Base = declarative_base()

db_init.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from connect_connector import engine

from data_model import Package

def create_packages():
    # Create a session
    session = sessionmaker(bind=engine)()

    # Create 10 sample packages
    for i in range(10):
        package = Package(
            product_id=i,
            height=10.0,
            width=10.0,
            depth=10.0,
            weight=10.0,
            special_handling_instructions="No special handling instructions."
        )

        # Add the package to the session
        session.add(package)

    # Commit the changes
    session.commit()

if __name__ == '__main__':
    create_packages()

    print('Packages created successfully.')

main.py

from flask import Flask, request, jsonify

from data_model import Package
from connect_connector import SessionMaker

app = Flask(__name__)

session_maker = SessionMaker()

@app.route("/packages/<int:product_id>", methods=["GET"])
def get_package(product_id):
  """Get information about a package."""

  session = session_maker

  package = session.query(Package).filter(Package.product_id == product_id).first()

  if package is None:
    return jsonify({"message": "Package not found."}), 404

  return jsonify(
      {
          "height": package.height,
          "width": package.width,
          "depth": package.depth,
          "weight": package.weight,
          "special_handling_instructions": package.special_handling_instructions,
      }
  ), 200

if __name__ == "__main__":
  app.run(host="0.0.0.0")

тест.py

import unittest

from data_model import Package
from connect_connector import SessionMaker

from main import app

class TestPackage(unittest.TestCase):

    def setUp(self):
        self.session_maker = SessionMaker()

    def tearDown(self):
        self.session_maker.close()

    def test_get_package(self):
        """Test the `get_package()` function."""

        package = Package(
        product_id=11, # Ensure that the product_id different from the sample data
        height=10,
        width=10,
        depth=10,
        weight=10,
        special_handling_instructions="Fragile",
        )

        session = self.session_maker

        session.add(package)
        session.commit()

        response = app.test_client().get("/packages/11")

        self.assertEqual(response.status_code, 200)

        self.assertEqual(
            response.json,
            {
                "height": 10,
                "width": 10,
                "depth": 10,
                "weight": 10,
                "special_handling_instructions": "Fragile",
            },
        )

if __name__ == "__main__":
    unittest.main()

requirements.txt

cloud-sql-python-connector==1.2.4
sqlalchemy==1.4.36
pg8000==1.22.0
Flask==3.0.0
gunicorn==20.1.0
psycopg2-binary==2.9.3

Dockerfile

FROM python:3.10-slim

WORKDIR /app

COPY . ./

RUN pip install -r requirements.txt

# Add these manually for your project
ENV INSTANCE_CONNECTION_NAME=YOUR_INSTANCE_CONNECTION_NAME
ENV DB_USER=evolution
ENV DB_PASS=evolution
ENV DB_NAME=product_details

CMD ["python", "main.py"]