Codelab de la guía del taller práctico técnico de Duet AI para desarrolladores

1. Objetivos

El objetivo de este taller es brindar a los usuarios y profesionales una capacitación práctica sobre Duet AI.

En este codelab, aprenderás lo siguiente:

  1. Activa Duet AI en tu proyecto de GCP y configúralo para usarlo en un IDE y en Cloud Console.
  2. Usa Duet AI para generar, completar y explicar código.
  3. Usar Duet AI para explicar y solucionar un problema de la aplicación
  4. Funciones de Duet AI, como el chat del IDE y el chat de varios turnos, la generación de código intercalado y por chat, acciones inteligentes como la explicación de código y el reconocimiento de la recitación, y mucho más

Narración

Para mostrar cómo se usa Duet AI for Developers de forma auténtica en el desarrollo diario, las actividades de este taller se llevan a cabo en un contexto narrativo.

Un nuevo desarrollador se une a una empresa de comercio electrónico. Su tarea es agregar un servicio nuevo a la aplicación de comercio electrónico existente (que se compone de varios servicios). El nuevo servicio proporciona información adicional (dimensiones, peso, etc.) sobre los productos del catálogo de productos. Este servicio permitirá ofrecer costos de envío mejores o más económicos según las dimensiones y el peso de los productos.

Como el desarrollador es nuevo en la empresa, usará Duet AI para la generación, la explicación y la documentación de código.

Una vez que se codifica el servicio, un administrador de la plataforma usará Duet AI (chat) para ayudar a crear el artefacto (contenedor de Docker) y los recursos necesarios para implementarlo en GCP (por ejemplo, Artifact Registry, permisos de IAM, un repositorio de código, infraestructura de procesamiento, es decir, GKE o Cloud Run, etcétera).

Una vez que la aplicación se implemente en GCP, un operador de aplicaciones o SRE usará Duet AI (y Cloud Ops) para ayudar a solucionar un error en el nuevo servicio.

Personificación

En el taller, se aborda el siguiente arquetipo de usuario:

  1. Desarrollador de aplicaciones: Se requieren algunos conocimientos de programación y desarrollo de software.

Esta variación del taller de Duet AI es solo para desarrolladores. No se requieren conocimientos sobre los recursos de la nube de GCP. Los instructivos para compilar los recursos de GCP necesarios para ejecutar esta aplicación se encuentran aquí. Puedes seguir las instrucciones de esta guía para implementar los recursos de GCP necesarios.

2. Prepara el entorno

Cómo activar Duet AI

Puedes activar Duet AI en un proyecto de GCP a través de la API (gcloud o herramientas de IaC como Terraform) o de la IU de Cloud Console.

Para activar Duet AI en un proyecto de Google Cloud, debes habilitar la API de Cloud AI Companion y otorgarles a los usuarios los roles de Usuario de Cloud AI Companion y Visualizador de Service Usage de Identity and Access Management (IAM).

A través de gcloud

Activa Cloud Shell:

Configura tu PROJECT_ID y USER, y habilita la API de 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}

El resultado es similar al siguiente:

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

Otorga los roles de Identity and Access Management (IAM) de Usuario de Cloud AI Companion y Visualizador de Service Usage a la cuenta de USER. La API de Cloud Companion se encuentra detrás de las funciones del IDE y la consola que usaremos. El permiso de Service Usage Viewer se usa como una verificación rápida antes de habilitar la IU en la consola (para que la IU de Duet solo aparezca en los proyectos en los que la API esté habilitada).

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

El resultado es similar al siguiente:

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

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

A través de la consola de Cloud

Para habilitar la API, ve a la página de la API de Cloud AI Companion en la consola de Google Cloud.

En el selector de proyectos, elige un proyecto.

Haz clic en Habilitar.

La página se actualiza y muestra el estado Habilitado. Duet AI ahora está disponible en el proyecto de Google Cloud seleccionado para todos los usuarios que tienen los roles de IAM necesarios.

Para otorgar los roles de IAM necesarios para usar Duet AI, ve a la página IAM.

En la columna Principal, busca tu USUARIO para el que deseas habilitar el acceso a Duet AI y, luego, haz clic en el ícono de lápiz ✏️ Editar principal en esa fila.

En el panel Editar acceso, haz clic en Agregar otra función.

En Selecciona un rol, selecciona Usuario de Cloud AI Companion.

Haz clic en Agregar otro rol y selecciona Visualizador de Service Usage.

Haz clic en Guardar.

Configura el IDE

Los desarrolladores pueden elegir entre una variedad de IDE que se adapten mejor a sus necesidades. La asistencia de código de Duet AI está disponible en varios IDE, como Visual Studio Code, IDE de JetBrains (IntelliJ, PyCharm, GoLand, WebStorm y muchos más), Cloud Workstations y el editor de Cloud Shell.

En este lab, puedes usar Cloud Workstations o el editor de Cloud Shell.

En este taller, se usa el Editor de Cloud Shell.

Ten en cuenta que la configuración de las estaciones de trabajo de Cloud puede tardar entre 20 y 30 minutos.

Para usarlo de inmediato, usa el editor de Cloud Shell.

Haz clic en el ícono de lápiz ✏️ en la barra de menú superior de Cloud Shell para abrir el editor de Cloud Shell.

El editor de Cloud Shell tiene una IU y una UX muy similares a las de VS Code.

d6a6565f83576063.png

Presiona CTRL (en Windows)/CMD (en Mac) + , (coma) para ingresar al panel de configuración.

En la barra de búsqueda, escribe "Duet AI".

Asegúrate de habilitar Cloudcode › Duet AI: Enable y Cloudcode › Duet AI › Inline Suggestions: Enable Auto.

111b8d587330ec74.png

En la barra de estado de la parte inferior, haz clic en Cloud Code - Acceder y sigue el flujo de trabajo de acceso.

Si ya accediste, la barra de estado mostrará Cloud Code - No project.

Haz clic en Cloud Code - No project y aparecerá un panel desplegable de acciones en la parte superior. Haz clic en Seleccionar un proyecto de Google Cloud.

3241a59811e3c84a.png

Comienza a escribir el ID de tu PROYECTO y este debería aparecer en la lista.

c5358fc837588fe.png

Selecciona tu PROJECT_ID en la lista de proyectos.

La barra de estado inferior se actualiza para mostrar el ID de tu proyecto. Si no es así, es posible que debas actualizar la pestaña del editor de Cloud Shell.

Haz clic en el ícono de Duet AI d97fc4e7b594c3af.png en la barra de menú de la izquierda y aparecerá la ventana de chat de Duet AI. Si recibes un mensaje que dice Select GCP Project. Haz clic en el proyecto y vuelve a seleccionarlo.

Ahora verás la ventana de chat de Duet AI

781f888360229ca6.png

3. Configurar la infraestructura

d3234d237f00fdbb.png

Para ejecutar el nuevo servicio de envío en GCP, necesitas los siguientes recursos de GCP:

  1. Una instancia de Cloud SQL, con una base de datos.
  2. Un clúster de GKE para ejecutar el servicio en contenedor
  3. Un registro de Artifact Registry para almacenar la imagen de Docker
  4. Un repositorio de Cloud Source para el código

En la terminal de Cloud Shell, clona el siguiente repo y ejecuta los siguientes comandos para configurar la infraestructura en tu proyecto de 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. Cómo desarrollar un servicio de Flask en Python

9745ba5c70782e76.png

El servicio que crearemos constará, en última instancia, de los siguientes archivos. No es necesario que crees estos archivos ahora. Los crearás de a uno siguiendo las instrucciones que se indican a continuación:

  1. package-service.yaml: Es una especificación de API abierta para el servicio de paquetes que tiene datos como la altura, el ancho, el peso y las instrucciones especiales de manipulación.
  2. data_model.py: Es el modelo de datos para la especificación de la API de package-service. También crea la tabla packages en la base de datos de product_details.
  3. connect_connector.py: Conexión de CloudSQL (define el motor, la sesión y el ORM base)
  4. db_init.py: Genera datos de muestra en la tabla packages.
  5. main.py: Es un servicio de Flask en Python con un extremo GET para recuperar detalles del paquete de los datos de packages según el product_id.
  6. test.py: Prueba de unidades
  7. requirement.txt: Requisitos de Python
  8. Dockerfile: Para organizar esta aplicación en contenedores

Si tienes algún problema durante los ejercicios, todos los archivos finales se encuentran en el APÉNDICE de este codelab para que los consultes.

En el paso anterior, creaste un repositorio de Cloud Source Repositories. Clona el repositorio. Compilarás los archivos de la aplicación en la carpeta del repositorio clonado.

En la terminal de Cloud Shell, ejecuta el siguiente comando para clonar el repositorio.

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

Abre la barra lateral del chat de Duet AI desde el menú de la izquierda del editor de Cloud Shell. El ícono se ve así: 8b135a000b259175.png. Ahora puedes usar Duet AI para obtener asistencia con el código.

package-service.yaml

Sin ningún archivo abierto, pídele a Duet que genere una especificación de OpenAPI para el servicio de envío.

Instrucción 1: Genera una especificación de OpenAPI en formato YAML para un servicio que proporcione información de envío y paquetes a partir de un ID de producto numérico. El servicio debe incluir información sobre la altura, el ancho, la profundidad y el peso de los paquetes, así como instrucciones especiales de manipulación.

ba12626f491a1204.png

Hay tres opciones en la parte superior derecha de la ventana de código generado.

Puedes COPY 71194556d8061dae.pngel código y PEGARLO en un archivo.

Puedes ADD df645de8c65607a.png el código al archivo que está abierto en el editor.

También puedes OPEN a4c7ed6d845df343.png el código en un archivo nuevo.

Haz clic en OPEN a4c7ed6d845df343.png el código en un archivo nuevo.

Haz clic en CTRL/CMD + s para guardar el archivo y almacénalo en la carpeta de la aplicación con el nombre de archivo package-service.yaml. Haz clic en Aceptar.

f6ebd5b836949366.png

El archivo final se encuentra en la sección APÉNDICE de este codelab. Si no es así, realiza los cambios correspondientes de forma manual.

También puedes probar varias instrucciones para ver las respuestas de Duet AI.

Para restablecer el historial del chat de Duet AI, haz clic en el ícono de papelera f574ca2c1e114856.png en la parte superior de la barra lateral de Duet AI.

data_model.py

A continuación, crearás el archivo de Python del modelo de datos para el servicio basado en la especificación de OpenAPI.

Con el archivo package-service.yaml abierto, ingresa la siguiente instrucción.

Instrucción 1: Con el ORM de SQLAlchemy de Python, genera un modelo de datos para este servicio de API. También incluye una función separada y un punto de entrada principal que crea las tablas de la base de datos.

b873a6a28bd28ca1.png

Veamos cada parte que se generó. Duet AI sigue siendo un asistente y, si bien puede ayudarte a crear código rápidamente, debes revisar el contenido generado y comprenderlo a medida que avanzas.

Primero, hay una clase llamada Package de tipoBase que define el modelo de datos para la base de datos packages de la siguiente manera:

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))

A continuación, necesitas una función que cree la tabla en la base de datos, como la siguiente:

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

Por último, necesitas una función principal que ejecute la función create_tables para compilar la tabla en la base de datos de Cloud SQL, como se muestra a continuación:

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

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

    print('Tables created successfully.')

Ten en cuenta que la función main crea un motor con una base de datos sqlite local. Para usar CloudSQL, deberás cambiarlo. Lo harás un poco más adelante.

Usa el flujo de trabajo OPEN a4c7ed6d845df343.png el código en un archivo nuevo como antes. Guarda el código en un archivo llamado data_model.py (ten en cuenta el guion bajo en el nombre y no un guion).

Para restablecer el historial del chat de Duet AI, haz clic en el ícono de papelera f574ca2c1e114856.png en la parte superior de la barra lateral de Duet AI.

connect-connector.py

Crea el conector de Cloud SQL.

Con el archivo data_model.py abierto, ingresa las siguientes instrucciones.

Instrucción 1: Con la biblioteca cloud-sql-python-connector, genera una función que inicialice un grupo de conexiones para una instancia de Cloud SQL de Postgres.

ed05cb6ff85d34c5.png

Ten en cuenta que la respuesta no usa la biblioteca cloud-sql-python-connector. Puedes agregar detalles a la misma conversación para definir mejor las instrucciones y ayudar a Duet.

Usemos otra instrucción.

Instrucción 2: Debe usar la biblioteca cloud-sql-python-connector.

d09095b44dde35bf.png

Asegúrate de que use la biblioteca cloud-sql-python-connector.

Usa el flujo de trabajo OPEN a4c7ed6d845df343.png el código en un archivo nuevo como antes. Guarda el código en un archivo llamado connect_conector.py. Es posible que debas importar manualmente la biblioteca pg8000. Consulta el archivo a continuación.

Borra el historial de chat de Duet AI y, con el archivo connect_connector.py abierto, genera los ORM DB engine, sessionmaker y base para usarlos en la aplicación.

Instrucción 1: Crea un motor, una clase sessionmaker y un ORM base con el método connect_with_connector

6e4214b72ab13a63.png

La respuesta puede agregar engine, Session y Base al archivo connect_connector.py.

El archivo final se encuentra en la sección APÉNDICE de este codelab. Si no es así, realiza los cambios correspondientes de forma manual.

También puedes probar varias instrucciones para ver la posible variación de las respuestas de Duet AI.

Para restablecer el historial del chat de Duet AI, haz clic en el ícono de papelera f574ca2c1e114856.png en la parte superior de la barra lateral de Duet AI.

Actualización de data_model.py

Debes usar el motor que creaste en el paso anterior (en el archivo connect_connector.py) para crear una tabla en la base de datos de CloudSQL.

Borra el historial del chat de Duet AI. Abre el archivo data_model.py. Prueba la siguiente instrucción.

Instrucción 1: En la función principal, importa y usa el motor de connect_connector.py

2e768c9b6c523b9a.png

Deberías ver la respuesta que importa engine desde connect_connector (para Cloud SQL). create_table usa ese motor (en lugar de la base de datos local sqlite predeterminada).

Actualiza el archivo data_model.py.

El archivo final se encuentra en la sección APÉNDICE de este codelab. Si no es así, realiza los cambios correspondientes de forma manual.

También puedes probar varias instrucciones para ver las diferentes respuestas de Duet AI.

Para restablecer el historial del chat de Duet AI, haz clic en el ícono de papelera f574ca2c1e114856.png en la parte superior de la barra lateral de Duet AI.

requirements.txt

Crea un archivo requirements.txt para la aplicación.

Abre connect_connector.py y el archivo data_model.py, y, luego, ingresa la siguiente instrucción.

Instrucción 1: Genera un archivo de requisitos de pip para este modelo de datos y servicio

Instrucción 2: Genera un archivo de requisitos de pip para este modelo de datos y servicio con las versiones más recientes

69fae373bc5c6a18.png

Verifica que los nombres y las versiones sean correctos. Por ejemplo, en la respuesta anterior, el nombre y la versión de google-cloud-sql-connecter son incorrectos. Corrige las versiones de forma manual y crea un archivo requirements.txt que se vea de la siguiente manera:

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

En la terminal de comandos, ejecuta lo siguiente:

pip3 install -r requirements.txt

Para restablecer el historial del chat de Duet AI, haz clic en el ícono de papelera f574ca2c1e114856.png en la parte superior de la barra lateral de Duet AI.

Crea una tabla de paquetes en Cloud SQL

Configura las variables de entorno para el conector de la base de datos de Cloud SQL.

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

Ahora, ejecuta data_model.py.

python data_model.py

El resultado es similar al siguiente (consulta el código para ver qué se espera realmente):

Tables created successfully.

Conéctate a la instancia de Cloud SQL y verifica que se haya creado la base de datos.

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

Después de ingresar la contraseña (también evolution), obtén las tablas.

product_details=> \dt

El resultado es similar a este:

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

También puedes consultar los detalles del modelo de datos y de la tabla.

product_details=> \d+ packages

El resultado es similar a este:

                                                                        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

Escribe \q para salir de Cloud SQL.

db_init.py

A continuación, agreguemos algunos datos de muestra a la tabla packages.

Borra el historial del chat de Duet AI. Con el archivo data_model.py abierto, prueba las siguientes instrucciones.

Instrucción 1: Genera una función que cree 10 filas de paquetes de muestra y las confirme en la tabla de paquetes

Instrucción 2: Con la sesión de connect_connector, genera una función que cree 10 filas de paquetes de muestra y las confirme en la tabla de paquetes

34a9afc5f04ba5.png

Usa el flujo de trabajo OPEN a4c7ed6d845df343.png el código en un archivo nuevo como antes. Guarda el código en un archivo llamado db_init.py.

El archivo final se encuentra en la sección APÉNDICE de este codelab. Si no es así, realiza los cambios correspondientes de forma manual.

También puedes probar varias instrucciones para ver las diferentes respuestas de Duet AI.

Para restablecer el historial del chat de Duet AI, haz clic en el ícono de papelera f574ca2c1e114856.png en la parte superior de la barra lateral de Duet AI.

Crea datos de paquetes de muestra

Ejecuta db_init.py desde la línea de comandos.

python db_init.py

El resultado es similar a este:

Packages created successfully.

Vuelve a conectarte a la instancia de Cloud SQL y verifica que los datos de muestra se hayan agregado a la tabla de paquetes.

Conéctate a la instancia de Cloud SQL y verifica que se haya creado la base de datos.

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

Después de ingresar la contraseña (también evolution), obtén todos los datos de la tabla de paquetes.

product_details=> SELECT * FROM packages;

El resultado es similar a este:

 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)

Escribe \q para salir de Cloud SQL.

main.py

Con los archivos data_model.py, package-service.yaml y connect_connector.py abiertos, crea un main.py para la aplicación.

Instrucción 1: Con la biblioteca de Flask de Python, crea una implementación que use extremos de REST HTTP para este servicio

Instrucción 2: Con la biblioteca Flask de Python, crea una implementación que use extremos REST HTTP para este servicio. Importa y usa SessionMaker desde connect_conector.py para los datos de paquetes.

Instrucción 3: Con la biblioteca Flask de Python, crea una implementación que use extremos REST HTTP para este servicio. Importa y usa Package de data_model.py y SessionMaker de connect_conector.py para los datos de paquetes.

Instrucción 4: Con la biblioteca Flask de Python, crea una implementación que use extremos de REST HTTP para este servicio. Importa y usa Package desde data_model.py y SessionMaker desde connect_conector.py para los datos de paquetes. Usa la IP del host 0.0.0.0 para app.run

6d794fc52a90e6ae.png

Actualiza los requisitos de main.py.

Instrucción: Crea un archivo de requisitos para main.py

1cc0b318d2d4ca2f.png

Agrega esto al archivo requirements.txt. Asegúrate de usar la versión 3.0.0 de Flask.

Usa el flujo de trabajo OPEN a4c7ed6d845df343.png el código en un archivo nuevo como antes. Guarda el código en un archivo llamado main.py.

El archivo final se encuentra en la sección APÉNDICE de este codelab. Si no es así, realiza los cambios correspondientes de forma manual.

Para restablecer el historial del chat de Duet AI, haz clic en el ícono de papelera f574ca2c1e114856.png en la parte superior de la barra lateral de Duet AI.

5. Prueba y ejecuta la aplicación

Instala los requisitos.

pip3 install -r requirements.txt

Ejecuta main.py.

python main.py

El resultado es similar a este:

 * 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

Desde una segunda terminal, prueba el extremo /packages/<product_id>.

curl localhost:5000/packages/1

El resultado es similar a este:

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

También puedes probar cualquier otro ID de producto en tus datos de muestra.

Ingresa CTRL_C para salir del contenedor de Docker en ejecución en la terminal.

Genera pruebas de unidades

Con el archivo main.py abierto, genera pruebas de unidades.

Instrucción 1: Genera pruebas de unidades.

e861e5b63e1b2657.png

Usa el flujo de trabajo OPEN a4c7ed6d845df343.png el código en un archivo nuevo como antes. Guarda el código en un archivo llamado test.py.

En la función test_get_package, se debe definir un product_id. Puedes agregarla de forma manual.

El archivo final se encuentra en la sección APÉNDICE de este codelab. Si no es así, realiza los cambios correspondientes de forma manual.

Para restablecer el historial del chat de Duet AI, haz clic en el ícono de papelera f574ca2c1e114856.png en la parte superior de la barra lateral de Duet AI.

Ejecutar pruebas de unidades

Ejecuta la prueba de unidades.

python test.py

El resultado es similar a este:

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

OK

Cierra todos los archivos en el editor de Cloud Shell y borra el historial de chat haciendo clic en el ícono de papelera 1ecccfe10d6c540.png en la barra de estado superior.

Dockerfile

Crea un objeto Dockerfile para esta aplicación.

Abre main.py y prueba las siguientes instrucciones.

Instrucción 1: Genera un Dockerfile para esta aplicación.

Instrucción 2: Genera un Dockerfile para esta aplicación. Copia todos los archivos en el contenedor.

9c473caea437a5c3.png

También debes establecer el ENVARS para INSTANCE_CONNECTION_NAME, DB_USER, DB_PASS y DB_NAME. Puedes hacerlo de forma manual. Tu Dockerfile debería verse de la siguiente manera:

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"]

Usa el flujo de trabajo OPEN a4c7ed6d845df343.png el código en un archivo nuevo como antes. Guarda el código en un archivo llamado Dockerfile.

El archivo final se encuentra en la sección APÉNDICE de este codelab. Si no es así, realiza los cambios correspondientes de forma manual.

Ejecuta la aplicación de forma local

Con Dockerfile abierto, prueba la siguiente instrucción.

Instrucción 1: ¿Cómo ejecuto un contenedor de forma local con este Dockerfile?

570fd5c296ca8c83.png

Sigue las instrucciones.

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

El resultado es similar a este:

 * 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

Desde una segunda ventana de terminal, accede al contenedor.

curl localhost:5000/packages/1

El resultado es similar a este:

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

La aplicación alojada en contenedores funciona.

Ingresa CTRL_C para salir del contenedor de Docker en ejecución en la terminal.

Compila la imagen de contenedor en Artifact Registry

Compila la imagen del contenedor y envíala a Artifact Registry.

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

El contenedor de la aplicación ahora se encuentra en us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping, que se puede implementar en GKE.

6. Implementa la aplicación en el clúster de GKE

Se creó un clúster de GKE Autopilot cuando compilaste los recursos de GCP para este taller. Conéctate al clúster de GKE.

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

Anota la cuenta de servicio predeterminada de Kubernetes con la cuenta de servicio de Google.

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

El resultado es similar a este:

serviceaccount/default annotated

Prepara y aplica el archivo 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

El resultado es similar a este:

deployment.apps/shipping created
service/shipping created

Espera hasta que los Pods se estén ejecutando y el Service tenga asignada una dirección IP de balanceador de cargas externo.

kubectl get pods
kubectl get service shipping

El resultado es similar a este:

# 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

En el caso de los clústeres de GKE Autopilot, espera unos momentos hasta que los recursos estén listos.

Accede al servicio a través de la dirección EXTERNAL-IP.

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

El resultado es similar a este:

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

7. Crédito adicional: Solución de problemas de la aplicación

Quita el rol de IAM de cliente de Cloud SQL de la cuenta de servicio cloudsqlsa. Esto provoca un error al conectarse a la base de datos de Cloud SQL.

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

Reinicia el Pod de envío.

kubectl rollout restart deployment shipping

Después de que se reinicie el Pod, intenta acceder al servicio de shipping nuevamente.

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

El resultado es similar a este:

...
<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>

Para inspeccionar los registros, navega a Kubernetes Engine > Cargas de trabajo.

d225b1916c829167.png

Haz clic en la implementación de shipping y, luego, en la pestaña Registros.

1d0459141483d6a7.png

Haz clic en el ícono de Ver en el Explorador de registros df8b9d19a9fe4c73.pngque se encuentra en el lado derecho de la barra de estado. Se abrirá una nueva ventana del Explorador de registros.

e86d1c265e176bc4.png

Haz clic en una de las entradas de error Traceback y, luego, en Explica esta entrada de registro.

d6af045cf03008bc.png

Puedes leer la explicación del error.

A continuación, pídele a Duet AI que te ayude a solucionar el error.

Prueba la siguiente instrucción.

Instrucción 1: Ayúdame a solucionar este error

9288dd6045369167.png

Ingresa el mensaje de error en la instrucción.

Mensaje 2: Prohibido: La principal de IAM autenticada no parece estar autorizada para realizar la solicitud a la API. Verifica que la API de Cloud SQL Admin esté habilitada en tu proyecto de GCP y que se haya otorgado el rol de "Cliente de Cloud SQL" a la principal de IAM.

f1e64fbdc435d31c.png

Y luego.

Instrucción 3: ¿Cómo asigno el rol de cliente de Cloud SQL a una cuenta de servicio de Google con gcloud?

bb8926b995a8875c.png

Asigna el rol de cliente de Cloud SQL a cloudsqlsa.

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

Espera unos instantes y vuelve a intentar acceder a la aplicación.

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

El resultado es similar a este:

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

Usaste correctamente Duet AI en Cloud Logging, el Explorador de registros y la función Explicador de registros para solucionar el problema.

8. Conclusión

¡Felicitaciones! Completaste este codelab correctamente.

En este codelab, aprendiste lo siguiente:

  1. Activa Duet AI en tu proyecto de GCP y configúralo para usarlo en un IDE y en Cloud Console.
  2. Usa Duet AI para generar, completar y explicar código.
  3. Usar Duet AI para explicar y solucionar un problema de la aplicación
  4. Funciones de Duet AI, como el chat del IDE y el chat de varios turnos, la generación de código intercalado y por chat, acciones inteligentes como la explicación de código y el reconocimiento de la recitación, y mucho más

9. Apéndice

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")

test.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"]