Guia do workshop prático sobre a Duet AI para desenvolvedores (link em inglês)

1. Objetivos

O objetivo deste workshop é oferecer treinamento prático sobre o Gemini para usuários e profissionais.

Neste codelab, você vai aprender o seguinte:

  1. Ative a Duet AI no seu projeto do GCP e configure-a para uso em um ambiente de desenvolvimento integrado e no console do Cloud.
  2. Use a Duet AI para geração, preenchimento e explicação de código.
  3. Use a Duet AI para explicar e resolver um problema de aplicativo.
  4. Recursos da Duet AI, como chat no ambiente de desenvolvimento integrado e chat com várias interações, chat vs. geração de código inline, ações inteligentes como explicação e confirmação de recitação e muito mais.

Narrativa

Para mostrar como a Duet AI para desenvolvedores é usada de maneira autêntica no desenvolvimento diário, as atividades deste workshop ocorrem em um contexto narrativo.

Um novo desenvolvedor entra em uma empresa de e-commerce. Ele tem a tarefa de adicionar um novo serviço (composto por vários serviços) ao aplicativo de e-commerce. O novo serviço fornece informações adicionais (dimensões, peso etc.) sobre os produtos do catálogo. Esse serviço permitirá custos de frete melhores/mais baratos com base nas dimensões e nos pesos do produto.

Como o desenvolvedor é novo na empresa, ele vai usar a Duet AI para geração de código, explicação e documentação.

Depois que o serviço for codificado, um administrador da plataforma vai usar a Duet AI (chat) para ajudar a criar o artefato (contêiner do Docker) e os recursos necessários para implantar o artefato no GCP (por exemplo, Artifact Registry, permissões do IAM, repositório de código, infraestrutura de computação, como GKE ou CloudRun etc.).

Depois que o aplicativo for implantado no GCP, um operador/SRE do aplicativo (SRE, na sigla em inglês) usará a Duet AI e as operações do Cloud para ajudar a solucionar um erro no novo serviço.

Perfil

O workshop aborda a seguinte persona:

  1. Desenvolvedor de aplicativos: é necessário ter algum conhecimento de programação e desenvolvimento de software.

Esta variação do workshop da Duet AI é apenas para desenvolvedores. Não é necessário ter conhecimento dos recursos de nuvem do GCP. Os scripts sobre como criar os recursos necessários do GCP para executar esse aplicativo podem ser encontrados aqui. Siga as instruções deste guia para implantar os recursos necessários do GCP.

2. Como preparar o ambiente

Como ativar a Duet AI

É possível ativar a Duet AI em um projeto do GCP usando a API (ferramentas gcloud ou IaC, como Terraform) ou a interface do console do Cloud.

Para ativar a Duet AI em um projeto do Google Cloud, ative a API Cloud AI Companion e conceda aos usuários os papéis de usuário do Cloud AI Companion e do Service Usage Viewer Identity and Access Management (IAM).

Pela gcloud

Ative o Cloud Shell:

Configure seu PROJECT_ID, USER e ative a 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}

A saída será assim:

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

Conceda os papéis de usuário do Cloud AI Companion e de visualizador do Service Usage e do Identity and Access Management (IAM) à conta USER. A API Cloud Companion fica por trás dos recursos do console e do ambiente de desenvolvimento integrado que vamos usar. A permissão de Leitor de uso do serviço é usada como uma verificação rápida antes de ativar a interface no console (para que a Duet UI só apareça em projetos em que a API está ativada).

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

A saída será assim:

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

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

Pelo console do Cloud

Para ativar a API, acesse a página da API Cloud AI Companion no console do Google Cloud.

No seletor de projetos, escolha um projeto.

Clique em Ativar.

A página é atualizada e mostra o status Ativado. A Duet AI já está disponível no projeto selecionado do Google Cloud para todos os usuários com os papéis do IAM necessários.

Para conceder os papéis do IAM necessários para usar a Duet AI, acesse a página IAM.

Na coluna Principal, encontre o USUÁRIO com acesso à Duet AI que você quer ativar e clique no ícone de lápis ✏️ Editar principal nessa linha.

No painel de acesso Editar, clique em Adicionar outro papel.

Em "Selecionar um papel", escolha Usuário do Cloud AI Companion.

Clique em Adicionar outro papel e selecione Leitor do Service Usage.

Clique em Salvar.

Como configurar o ambiente de desenvolvimento integrado

Os desenvolvedores podem escolher entre uma variedade de ambientes de desenvolvimento integrado que melhor atendem às necessidades deles. A assistência de código da Duet AI está disponível em vários ambientes de desenvolvimento integrado, como Visual Studio Code, ambientes de desenvolvimento integrado JetBrains (IntelliJ, PyCharm, GoLand, WebStorm e muito mais), Cloud Workstations e Editor do Cloud Shell.

Neste laboratório, é possível usar o Cloud Workstations ou o Cloud Shell Editor.

Este workshop usa o Cloud Shell Editor.

A configuração do Cloud Workstations pode levar de 20 a 30 minutos.

Para usar imediatamente, acesse o Editor do Cloud Shell.

Para abrir o editor do Cloud Shell, clique no ícone de lápis ✏️ na barra de menus da parte de cima.

O editor do Cloud Shell tem uma interface e UX muito semelhantes ao VSCode.

d6a6565f83576063.png

Clique em CTRL (no Windows)/CMD (no Mac) + , (vírgula) para entrar no painel "Configurações".

Na barra de pesquisa, digite "duet ai".

Verifique ou ative Cloudcode › Duet AI: ativar e Cloudcode › Duet AI › Sugestões inline: ativar automaticamente

111b8d587330ec74.png

Na barra de status da parte de baixo, clique em Cloud Code - Fazer login e siga o fluxo de trabalho de login.

Se você já tiver feito login, a barra de status mostrará Cloud Code - No project.

Clique em "Cloud Code - No project" e um painel suspenso de ações vai aparecer na parte de cima. Clique em Selecionar um projeto do Google Cloud.

3241a59811e3c84a.png

Comece a digitar o ID do PROJETO e seu projeto aparecerá na lista.

c5358fc837588fe.png

Selecione seu PROJECT_ID na lista de projetos.

A barra de status inferior será atualizada para mostrar o ID do projeto. Se isso não acontecer, talvez seja necessário atualizar a guia do editor do Cloud Shell.

Clique no ícone da Duet AI d97fc4e7b594c3af.png na barra de menus à esquerda para abrir a janela de chat da Duet AI. Se você receber a mensagem "Selecionar projeto do GCP". Clique e selecione o projeto novamente.

A janela de chat da Duet AI vai aparecer.

781f888360229ca6.png

3. Como configurar a infraestrutura

d3234d237f00fdbb.png

Para executar o novo serviço de frete no GCP, você precisa dos seguintes recursos do GCP:

  1. Uma instância do Cloud SQL com um banco de dados.
  2. Um cluster do GKE para executar o serviço conteinerizado.
  3. Um Artifact Registry para armazenar a imagem Docker.
  4. Um Cloud Source Repository para o código.

No terminal do Cloud Shell, clone o repositório a seguir e execute os comandos abaixo para configurar a infraestrutura no seu projeto do 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. Como desenvolver um serviço Python Flask

9745ba5c70782e76.png

O serviço que criaremos terá os seguintes arquivos. Você não precisa criar esses arquivos agora. Faça isso um de cada vez seguindo as instruções abaixo:

  1. package-service.yaml: uma especificação OpenAPI para o serviço de pacote que tem dados como altura, largura, peso e instruções especiais de manuseio.
  2. data_model.py: modelo de dados para a especificação da API package-service. Também cria a tabela packages no banco de dados product_details.
  3. connect_connector.py: conexão do CloudSQL (define o mecanismo, a sessão e o ORM básico).
  4. db_init.py: gera dados de amostra na tabela packages.
  5. main.py: um serviço Python Flask com um endpoint GET para recuperar detalhes do pacote dos dados do packages com base no product_id.
  6. test.py: teste de unidade
  7. requirement.txt: requisitos do Python
  8. Dockerfile: para conteinerizar o aplicativo.

Se você tiver algum problema durante os exercícios, os arquivos finais estão todos no APÊNDICE deste codelab para referência.

Na etapa anterior, você criou um Cloud Source Repository. Copie o repositório. Você vai criar os arquivos do aplicativo na pasta do repositório clonado.

No terminal do Cloud Shell, execute o comando a seguir para clonar o repositório.

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

Abra a barra lateral de chat da Duet AI no menu à esquerda do editor do Cloud Shell. O ícone é parecido com 8b135a000b259175.png. Agora é possível usar a Duet AI para receber assistência com código.

package-service.yaml

Sem arquivos abertos, peça para a Duet AI gerar uma especificação OpenAPI para o serviço de frete.

Instrução 1: gere uma especificação OpenAPI yaml para um serviço que forneça informações de envio e pacote de acordo com um ID de produto numérico. O serviço precisa incluir informações sobre a altura, largura, profundidade, peso e instruções especiais de manuseio dos pacotes.

ba12626f491a1204.png

Há três opções listadas no canto superior direito da janela de código gerado.

Você pode COPY 71194556d8061dae.pngo código e COLAR em um arquivo.

Você pode ADD (em inglês) df645de8c65607a.png o código para o arquivo aberto no Editor.

Ou você pode OPEN (a4c7ed6d845df343.png) o código em um novo arquivo.

Clique em OPEN a4c7ed6d845df343.png no código em um novo arquivo.

Clique em CTRL/CMD + s para salvar o arquivo e armazená-lo na pasta do aplicativo com o nome package-service.yaml. Clique em OK.

f6ebd5b836949366.png

O arquivo final está na seção APÊNDICE deste codelab. Caso contrário, faça as alterações apropriadas manualmente.

Também é possível testar vários comandos para conferir as respostas da Duet AI.

Para redefinir o histórico de chat da Duet AI, clique no ícone de lixeira f574ca2c1e114856.png na parte de cima da barra lateral.

data_model.py

Em seguida, você cria o arquivo Python do modelo de dados para o serviço com base na especificação OpenAPI.

Com o arquivo package-service.yaml aberto, digite o seguinte comando.

Instrução 1: Usando o ORM sqlalchemy do Python, gere um modelo de dados para este serviço de API. Inclua também uma função separada e um ponto de entrada principal que crie as tabelas do banco de dados.

b873a6a28bd28ca1.png

Vamos analisar cada parte gerada. A Duet AI ainda é um assistente e, embora possa ajudar a criar códigos rapidamente, você ainda precisa revisar e entender o conteúdo gerado à medida que avança.

Primeiro, há uma Class chamada Package do tipo Base que define o modelo de dados para o banco de dados packages desta forma:

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

Em seguida, você precisa de uma função que crie a tabela no banco de dados, como esta:

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

Por fim, você precisa de uma função principal que execute a função create_tables para criar a tabela no banco de dados do CloudSQL, desta forma:

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

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

    print('Tables created successfully.')

A função main está criando um mecanismo usando um banco de dados sqlite local. Para usar o Cloud SQL, você precisará alterá-lo. Você fará isso um pouco mais tarde.

Use OPEN a4c7ed6d845df343.png para ver o código em um novo fluxo de trabalho de arquivo, como antes. Salve o código em um arquivo chamado data_model.py (observe o sublinhado no nome, e não um traço).

Para redefinir o histórico de chat da Duet AI, clique no ícone de lixeira f574ca2c1e114856.png na parte de cima da barra lateral.

connect-connector.py

Crie o conector do CloudSQL.

Com o arquivo data_model.py aberto, insira os comandos a seguir.

Instrução 1: use a biblioteca cloud-sql-python-connector, gere uma função que inicialize um pool de conexões para uma instância do Cloud SQL do Postgres.

ed05cb6ff85d34c5.png

Observe que a resposta não usa a biblioteca cloud-sql-python-connector. Você pode refinar os comandos e dar um toque na Duet AI adicionando detalhes na mesma conversa.

Vamos usar outro comando.

Prompt 2: é necessário usar a biblioteca cloud-sql-python-connector.

d09095b44dde35bf.png

Verifique se ele usa a biblioteca cloud-sql-python-connector.

Use OPEN a4c7ed6d845df343.png para ver o código em um novo fluxo de trabalho de arquivo, como antes. Salve o código em um arquivo chamado connect_conector.py. Talvez seja necessário importar manualmente a biblioteca pg8000. Consulte o arquivo abaixo.

Limpe o histórico de chat da Duet AI e, com o arquivo connect_connector.py aberto, gere o ORM DB engine, sessionmaker e base para usar no aplicativo.

Instrução 1: criar um mecanismo, uma classe sessionmaker e um ORM básico usando o método connect_with_connector

6e4214b72ab13a63.png

A resposta pode anexar engine, Session e Base ao arquivo connect_connector.py.

O arquivo final está na seção APÊNDICE deste codelab. Caso contrário, faça as alterações apropriadas manualmente.

Também é possível testar vários comandos para conferir a possível variação das respostas da Duet AI.

Para redefinir o histórico de chat da Duet AI, clique no ícone de lixeira f574ca2c1e114856.png na parte de cima da barra lateral.

Como atualizar data_model.py

Você precisa usar o mecanismo criado na etapa anterior (no arquivo connect_connector.py) para criar uma tabela no banco de dados do CloudSQL.

Limpar o histórico de chat da Duet AI. Abra o arquivo data_model.py. Tente o seguinte comando.

Instrução 1: na função principal, importar e usar o mecanismo de connect_connector.py

2e768c9b6c523b9a.png

Você verá a resposta importando engine de connect_connector (para Cloud SQL). O create_table usa esse mecanismo em vez do banco de dados local sqlite padrão.

Atualize o arquivo data_model.py.

O arquivo final está na seção APÊNDICE deste codelab. Caso contrário, faça as alterações apropriadas manualmente.

Também é possível testar vários comandos para conferir as respostas da Duet AI.

Para redefinir o histórico de chat da Duet AI, clique no ícone de lixeira f574ca2c1e114856.png na parte de cima da barra lateral.

requirements.txt

Crie um arquivo requirements.txt para o aplicativo.

Abra connect_connector.py e o arquivo data_model.py e digite o seguinte comando.

Instrução 1: gerar um arquivo de requisitos de pip para este modelo de dados e serviço

Instrução 2: gerar um arquivo de requisitos de pip para esse modelo de dados e serviço usando as versões mais recentes

69fae373bc5c6a18.png

Verifique se os nomes e as versões estão corretos. Por exemplo, na resposta acima, o nome e a versão da google-cloud-sql-connecter estão incorretos. Corrija manualmente as versões e crie um arquivo requirements.txt como este:

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

No terminal de comando, execute o seguinte:

pip3 install -r requirements.txt

Para redefinir o histórico de chat da Duet AI, clique no ícone de lixeira f574ca2c1e114856.png na parte de cima da barra lateral.

Como criar a tabela de pacotes no CloudSQL

Defina as variáveis de ambiente para o conector de banco de dados do 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

Agora execute data_model.py.

python data_model.py

A saída será semelhante a esta (verifique o código para saber o que é realmente esperado):

Tables created successfully.

Conecte-se à instância do Cloud SQL e verifique se o banco de dados foi criado.

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

Depois de inserir a senha (também evolution), acesse as tabelas.

product_details=> \dt

O resultado será assim:

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

Você também pode verificar o modelo de dados e os detalhes da tabela.

product_details=> \d+ packages

O resultado será assim:

                                                                        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

Digite \q para sair do CloudSQL.

db_init.py

Em seguida, vamos adicionar alguns dados de amostra à tabela packages.

Limpar o histórico de chat da Duet AI. Com o arquivo data_model.py aberto, tente os comandos a seguir.

Instrução 1: gerar uma função que cria 10 linhas de pacotes de amostra e as envia para a tabela de pacotes

Instrução 2: usando a sessão de connect_connector, gera uma função que cria 10 linhas de pacotes de amostra e as confirma na tabela de pacotes

34a9afc5f04ba5.png

Use OPEN a4c7ed6d845df343.png para ver o código em um novo fluxo de trabalho de arquivo, como antes. Salve o código em um arquivo chamado db_init.py.

O arquivo final está na seção APÊNDICE deste codelab. Caso contrário, faça as alterações apropriadas manualmente.

Também é possível testar vários comandos para conferir as respostas da Duet AI.

Para redefinir o histórico de chat da Duet AI, clique no ícone de lixeira f574ca2c1e114856.png na parte de cima da barra lateral.

Como criar dados de pacotes de amostra

Execute o db_init.py na linha de comando.

python db_init.py

O resultado será assim:

Packages created successfully.

Conecte-se à instância do Cloud SQL novamente e verifique se os dados de amostra foram adicionados à tabela de pacotes.

Conecte-se à instância do Cloud SQL e verifique se o banco de dados foi criado.

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

Depois de inserir a senha (também evolution), acesse todos os dados da tabela de pacotes.

product_details=> SELECT * FROM packages;

O resultado será assim:

 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)

Digite \q para sair do CloudSQL.

main.py

Com os arquivos data_model.py, package-service.yaml e connect_connector.py abertos, crie um main.py para o aplicativo.

Prompt 1: Usar a biblioteca python flask: criar uma implementação que use endpoints http REST para este serviço

Prompt 2: Usar a biblioteca python flask – criar uma implementação que use endpoints http REST para esse serviço. importar e usar o SessionMaker de connect_conector.py para dados de pacotes.

Prompt 3: Usar a biblioteca python flask – criar uma implementação que use endpoints http REST para esse serviço. importar e usar o pacote do data_model.py e o SessionMaker de connect_conector.py para dados de pacotes.

Prompt 4: Usar a biblioteca python flask – criar uma implementação que use endpoints http REST para esse serviço. importar e usar o pacote do data_model.py e o SessionMaker de connect_conector.py para dados de pacotes. Usar o IP do host 0.0.0.0 para app.run

6d794fc52a90e6ae.png

Atualize os requisitos para main.py.

Comando: criar um arquivo de requisitos para main.py

1cc0b318d2d4ca2f.png

Anexe isso ao arquivo requirements.txt. Use o Flask versão 3.0.0.

Use OPEN a4c7ed6d845df343.png para ver o código em um novo fluxo de trabalho de arquivo, como antes. Salve o código em um arquivo chamado main.py.

O arquivo final está na seção APÊNDICE deste codelab. Caso contrário, faça as alterações apropriadas manualmente.

Para redefinir o histórico de chat da Duet AI, clique no ícone de lixeira f574ca2c1e114856.png na parte de cima da barra lateral.

5. Como testar e executar o aplicativo

Instale os requisitos.

pip3 install -r requirements.txt

Execute main.py.

python main.py

O resultado será assim:

 * 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

Em um segundo terminal, teste o endpoint /packages/<product_id>.

curl localhost:5000/packages/1

O resultado será assim:

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

Você também pode testar qualquer outro ID do produto nos seus dados de amostra.

Digite CTRL_C para sair do contêiner do Docker em execução no terminal.

Como gerar testes de unidade

Com o arquivo main.py aberto, gere testes de unidade.

Instrução 1: gerar testes de unidade.

e861e5b63e1b2657.png

Use OPEN a4c7ed6d845df343.png para ver o código em um novo fluxo de trabalho de arquivo, como antes. Salve o código em um arquivo chamado test.py.

Na função test_get_package, é preciso definir um product_id. É possível adicioná-lo manualmente.

O arquivo final está na seção APÊNDICE deste codelab. Caso contrário, faça as alterações apropriadas manualmente.

Para redefinir o histórico de chat da Duet AI, clique no ícone de lixeira f574ca2c1e114856.png na parte de cima da barra lateral.

Execução de testes de unidade

Execute o teste de unidade.

python test.py

O resultado será assim:

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

OK

Feche todos os arquivos no editor do Cloud Shell e limpe o histórico de chat clicando no ícone de lixeira 1eCef10d6c540.png na barra de status superior.

Dockerfile

Crie um Dockerfile para esse aplicativo.

Abra main.py e tente as seguintes instruções.

Instrução 1: gerar um Dockerfile para este aplicativo.

Instrução 2: gerar um Dockerfile para este aplicativo. Copie todos os arquivos para o contêiner.

9c473caea437a5c3.png

Também é necessário definir ENVARS para INSTANCE_CONNECTION_NAME, DB_USER, DB_PASS e DB_NAME. Você pode fazer isso manualmente. Seu Dockerfile deve ficar assim:

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

Use OPEN a4c7ed6d845df343.png para ver o código em um novo fluxo de trabalho de arquivo, como antes. Salve o código em um arquivo chamado Dockerfile.

O arquivo final está na seção APÊNDICE deste codelab. Caso contrário, faça as alterações apropriadas manualmente.

Como executar o aplicativo localmente

Com o Dockerfile aberto, tente o comando a seguir.

Prompt 1: Como executar localmente um contêiner usando este Dockerfile

570fd5c296ca8c83.png

Siga as instruções.

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

O resultado será assim:

 * 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

Em uma janela do segundo terminal, acesse o contêiner.

curl localhost:5000/packages/1

O resultado será assim:

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

O aplicativo conteinerizado está funcionando.

Digite CTRL_C para sair do contêiner do Docker em execução no terminal.

Criando imagem do contêiner no Artifact Registry

Crie a imagem do contêiner e envie-a para o 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

O contêiner do aplicativo agora está localizado em us-central1-docker.pkg.dev/${PROJECT_ID}/shipping/shipping, que pode ser implantado no GKE.

6. Implantando o aplicativo no cluster do GKE

Um cluster do GKE Autopilot foi criado quando você criou os recursos do GCP para este workshop. Conecte-se ao cluster do GKE.

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

Anotar a conta de serviço padrão do Kubernetes com a conta de serviço do Google.

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

O resultado será assim:

serviceaccount/default annotated

Prepare e aplique o arquivo 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

O resultado será assim:

deployment.apps/shipping created
service/shipping created

Aguarde até que os pods estejam em execução e o serviço tenha um endereço IP de balanceador de carga externo atribuído.

kubectl get pods
kubectl get service shipping

O resultado será assim:

# 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

No caso de clusters do GKE Autopilot, aguarde alguns instantes até que os recursos estejam prontos.

Acesse o serviço pelo endereço EXTERNAL-IP.

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

O resultado será assim:

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

7. Crédito extra: solução de problemas da inscrição

Remova o papel do IAM de cliente do CloudSQL da conta de serviço cloudsqlsa. Isso causa um erro ao se conectar ao banco de dados CloudSQL.

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

Reinicie o pod de envio.

kubectl rollout restart deployment shipping

Depois que o pod for reiniciado, tente acessar o serviço shipping novamente.

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

O resultado será assim:

...
<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 inspecionar os registros, acesse Kubernetes Engine > Cargas de trabalho

d225b1916c829167.png

Clique na implantação shipping e na guia Registros.

1d0459141483d6a7.png

Clique no ícone Ver na Análise de registros df8b9d19a9fe4c73.pngno lado direito da barra de status. Uma nova janela da Análise de registros será aberta.

e86d1c265e176bc4.png

Clique em uma das entradas de erro Traceback e depois em Explicar esta entrada de registro.

d6af045cf03008bc.png

Leia a explicação do erro.

A seguir, vamos usar a Duet AI para ajudar a resolver o erro.

Tente o seguinte comando.

Instrução 1: Ajude-me a resolver este erro

9288dd6045369167.png

Digite a mensagem de erro no prompt.

Instrução 2: Proibido: o principal do IAM autenticado não parece estar autorizado a fazer solicitações de API. Verifique a "API Cloud SQL Admin" ativado no projeto do GCP e no "Cliente do Cloud SQL" papel foi concedido ao principal do IAM

f1e64fbdc435d31c.png

E então.

Instrução 3: como atribuir o papel de cliente do Cloud SQL a uma conta de serviço do Google usando a gcloud?

bb8926b995a8875c.png

Atribua o papel de cliente do Cloud SQL ao cloudsqlsa.

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

Aguarde alguns instantes e tente acessar o aplicativo novamente.

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

O resultado será assim:

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

Você usou a Duet AI no Cloud Logging, na Análise de registros e no recurso Explicação de registros para resolver o problema.

8. Conclusão

Parabéns! Você concluiu este codelab.

Neste codelab, você aprendeu o seguinte:

  1. Ative a Duet AI no seu projeto do GCP e configure-a para uso em um ambiente de desenvolvimento integrado e no console do Cloud.
  2. Use a Duet AI para geração, preenchimento e explicação de código.
  3. Use a Duet AI para explicar e resolver um problema de aplicativo.
  4. Recursos da Duet AI, como chat no ambiente de desenvolvimento integrado e chat com várias interações, chat vs. geração de código inline, ações inteligentes como explicação e confirmação de recitação e muito mais.

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