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

1. Objetivos

O objetivo deste workshop é oferecer educação prática sobre a Duet AI para usuários e profissionais.

Neste codelab, você vai aprender o seguinte:

  1. Ative a Duet AI no seu projeto do GCP e configure para uso em um ambiente de desenvolvimento integrado e no Cloud Console.
  2. Use a Duet AI para geração, conclusão 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 do ambiente de desenvolvimento integrado e conversa multiturno, chat x geração de código inline, ações inteligentes, como explicação de código e confirmação de recitação, entre outros.

Narrativa

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

Um novo desenvolvedor entra em uma empresa de e-commerce. A tarefa é adicionar um novo serviço ao aplicativo de e-commerce atual, que é composto por vários serviços. O novo serviço fornece mais informações (dimensões, peso etc.) sobre os produtos no catálogo de produtos. Esse serviço vai permitir custos de frete melhores/mais baratos com base nas dimensões e pesos dos produtos.

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

Depois que o serviço é codificado, um administrador da plataforma usa 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, um repositório de código, infraestrutura de computação, ou seja, GKE ou CloudRun etc.).

Depois que o aplicativo for implantado no GCP, um operador de aplicativo/SRE vai usar a Duet AI (e o Cloud Ops) para ajudar a resolver um erro no novo serviço.

Persona

O workshop abrange 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 para criar os recursos necessários do GCP e executar esse aplicativo podem ser encontrados aqui. Siga as instruções neste guia para implantar os recursos necessários do GCP.

2. Como preparar o ambiente

Ativar a Duet AI

É possível ativar a Duet AI em um projeto do GCP usando a API (gcloud ou ferramentas de IaC, como o 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 de leitor do Service Usage Identity and Access Management (IAM).

Pela gcloud

Ative o Cloud Shell:

Configure seu PROJECT_ID, USER e ative a API IA do Google Cloud 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 é semelhante a esta:

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

Conceda os papéis de usuário do IA do Google Cloud Companion e leitor do Service Usage do Identity and Access Management (IAM) à conta de USUÁRIO. A API Cloud Companion está por trás dos recursos do IDE e do console 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. Assim, a interface da Duet AI só aparece 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 é semelhante a esta:

...
- 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 IA do Google Cloud 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 agora está disponível no projeto na nuvem selecionado do Google Cloud para todos os usuários que têm as funções de IAM necessárias.

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 para quem você quer ativar o acesso à Duet AI e clique no ícone de lápis ✏️ Editar principal nessa linha.

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

Em "Selecionar um papel", escolha Usuário da IA do Google Cloud Companion.

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

Clique em Salvar.

Configurar o ambiente de desenvolvimento integrado

Os desenvolvedores podem escolher entre uma variedade de IDEs que melhor atendam à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, JetBrains (IntelliJ, PyCharm, GoLand, WebStorm e outros), Cloud Workstations e Editor do Cloud Shell.

Neste laboratório, você pode usar as Cloud Workstations ou o editor do Cloud Shell.

Este workshop usa o editor do Cloud Shell.

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

Para usar imediatamente, use o editor do Cloud Shell.

Clique no ícone de lápis ✏️ na barra de menus superior do Cloud Shell para abrir o editor.

O editor do Cloud Shell tem uma interface e uma experiência do usuário muito semelhantes ao VSCode.

d6a6565f83576063.png

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

Na barra de pesquisa, digite "duet ai".

Confira ou ative Cloudcode > Duet AI: Ativar e Cloudcode > Duet AI > Sugestões inline: ativar automaticamente.

111b8d587330ec74.png

Na barra de status na 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 vai mostrar Cloud Code - Sem projeto.

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

3241a59811e3c84a.png

Comece a digitar o ID do projeto, e ele vai aparecer na lista.

c5358fc837588fe.png

Selecione seu PROJECT_ID na lista de projetos.

A barra de status na parte de baixo é atualizada para mostrar o ID do projeto. Se não, atualize a guia do editor do Cloud Shell.

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

Agora você vê a janela de chat da Duet AI

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 em contêiner.
  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 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 vamos criar vai consistir nos seguintes arquivos. Não é necessário criar esses arquivos agora. Eles serão criados um de cada vez seguindo as instruções abaixo:

  1. package-service.yaml: uma especificação de API aberta para o serviço de pacotes que tem dados como altura, largura, peso e instruções de manuseio especial.
  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 Cloud SQL (define o mecanismo, a sessão e o ORM de base)
  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 packages com base no product_id.
  6. test.py: teste de unidade
  7. requirement.txt: requisitos do Python
  8. Dockerfile: para colocar o aplicativo em um contêiner

Se você tiver problemas durante os exercícios, os arquivos finais estarão localizados no APÊNDICE deste codelab para referência.

Na etapa anterior, você criou um repositório do Cloud Source. 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 você pode usar a Duet AI para receber ajuda com códigos.

package-service.yaml

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

Comando 1: gere uma especificação OpenAPI YAML para um serviço que forneça informações de frete e pacote com base em um ID de produto numérico. O serviço precisa incluir informações sobre 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.png o código e COLAR em um arquivo.

É possível ADD df645de8c65607a.png o código para o arquivo aberto no momento no editor.

Ou OPEN a4c7ed6d845df343.png o código em um novo arquivo.

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

Clique em CTRL/CMD + s para salvar o arquivo e armazene-o 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. Se não for, faça as mudanças necessárias manualmente.

Você também pode testar vários comandos para conferir as respostas da Duet AI.

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

data_model.py

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

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

Comando 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 é uma assistente. Embora ela possa ajudar a criar código rapidamente, você ainda precisa revisar e entender o conteúdo gerado.

Primeiro, há uma classe chamada Package do tipoBase que define o modelo de dados para o banco de dados packages, como a seguir:

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 Cloud SQL, como esta:

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, é necessário mudar isso. Você vai fazer isso um pouco mais tarde.

Usando o OPEN a4c7ed6d845df343.png 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, não um traço).

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

connect-connector.py

Crie o conector do Cloud SQL.

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

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

ed05cb6ff85d34c5.png

A resposta não usa a biblioteca cloud-sql-python-connector. Você pode refinar os comandos para dar uma ajuda à Duet AI adicionando detalhes à mesma conversa.

Vamos usar outro comando.

Comando 2: precisa usar a biblioteca cloud-sql-python-connector.

d09095b44dde35bf.png

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

Usando o OPEN a4c7ed6d845df343.png 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 ser usado no aplicativo.

Comando 1: crie um mecanismo, uma classe sessionmaker e um ORM de base 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. Se não for, faça as mudanças necessárias manualmente.

Você também pode testar vários comandos para conferir a variação potencial das respostas da Duet AI.

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

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

Limpe o histórico de chat da Duet AI. Abra o arquivo data_model.py. Tente o comando a seguir.

Comando 1: na função principal, importe e use o mecanismo de connect_connector.py

2e768c9b6c523b9a.png

Você vai notar a resposta importando engine de connect_connector (para o 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. Se não for, faça as mudanças necessárias manualmente.

Você também pode testar vários comandos para conferir as respostas da Duet AI.

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

requirements.txt

Crie um arquivo requirements.txt para o aplicativo.

Abra os arquivos connect_connector.py e data_model.py e insira o seguinte comando.

Comando 1: gere um arquivo de requisitos do pip para esse modelo de dados e serviço

Comando 2: gere um arquivo de requisitos do 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 do google-cloud-sql-connecter estão incorretos. Corrija manualmente as versões e crie um arquivo requirements.txt parecido com este:

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

No terminal de comandos, execute o seguinte:

pip3 install -r requirements.txt

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

Como criar a tabela de pacotes no Cloud SQL

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

Agora execute data_model.py.

python data_model.py

A saída é semelhante a esta (confira o código para ver o que é 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), receba 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 Cloud SQL.

db_init.py

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

Limpe o histórico de chat da Duet AI. Com o arquivo data_model.py aberto, tente os seguintes comandos.

Comando 1: gere uma função que crie 10 linhas de pacotes de amostra e as confirme na tabela de pacotes

Comando 2: usando a sessão de connect_connector, gere uma função que crie 10 linhas de pacotes de amostra e as confirme na tabela de pacotes.

34a9afc5f04ba5.png

Usando o OPEN a4c7ed6d845df343.png 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. Se não for, faça as mudanças necessárias manualmente.

Você também pode testar vários comandos para conferir as respostas da Duet AI.

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

Como criar dados de pacotes de amostra

Execute 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 "packages".

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), receba 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 Cloud SQL.

main.py

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

Comando 1: usando a biblioteca python flask, crie uma implementação que use endpoints REST HTTP para esse serviço.

Comando 2: usando a biblioteca python flask, crie uma implementação que use endpoints http rest para esse serviço. Importe e use o SessionMaker de connect_conector.py para dados de pacotes.

Comando 3: usando a biblioteca python flask, crie uma implementação que use endpoints http rest para esse serviço. Importe e use o pacote de data_model.py e o SessionMaker de connect_conector.py para dados de pacotes.

Comando 4: usando a biblioteca python flask, crie uma implementação que use endpoints http rest para esse serviço. Importe e use o pacote de 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: crie um arquivo de requisitos para main.py

1cc0b318d2d4ca2f.png

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

Usando o OPEN a4c7ed6d845df343.png 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. Se não for, faça as mudanças necessárias manualmente.

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

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 de produto nos seus dados de amostra.

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

Gerar testes de unidade

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

Comando 1: gere testes de unidade.

e861e5b63e1b2657.png

Usando o OPEN a4c7ed6d845df343.png 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, um product_id precisa ser definido. É possível adicionar manualmente.

O arquivo final está na seção APÊNDICE deste codelab. Se não for, faça as mudanças necessárias manualmente.

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

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 da lixeira 1ecccfe10d6c540.png na barra de status superior.

Dockerfile

Crie um Dockerfile para esse aplicativo.

Abra main.py e tente os comandos a seguir.

Comando 1: gere um Dockerfile para este aplicativo.

Comando 2: gere um Dockerfile para este aplicativo. Copie todos os arquivos para o contêiner.

9c473caea437a5c3.png

Você também precisa definir o ENVARS para INSTANCE_CONNECTION_NAME, DB_USER, DB_PASS e DB_NAME. Você pode fazer isso manualmente. O Dockerfile precisa ter esta aparência:

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

Usando o OPEN a4c7ed6d845df343.png 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. Se não for, faça as mudanças necessárias manualmente.

Executar o aplicativo localmente

Com o Dockerfile aberto, tente o seguinte comando.

Comando 1: como executar um contêiner localmente 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 segunda janela de 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.

Como criar imagens de contêiner no Artifact Registry

Crie a imagem do contêiner e envie 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. Como implantar 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

Para 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: como solucionar problemas do aplicativo

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

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 depois na guia Registros.

1d0459141483d6a7.png

Clique no ícone Ver na Análise de registros df8b9d19a9fe4c73.png no lado direito da barra de status. Isso abre uma nova janela do Explorador de registros.

e86d1c265e176bc4.png

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

d6af045cf03008bc.png

Leia a explicação do erro.

Em seguida, vamos pedir ajuda à Duet AI para resolver o erro.

Tente o comando a seguir.

Comando 1: ajude-me a resolver este erro

9288dd6045369167.png

Insira a mensagem de erro no comando.

Solicitação 2: proibida. O principal autenticado do IAM não parece autorizado a fazer solicitações de API. Verifique se a API Cloud SQL Admin está ativada no seu projeto do GCP e se a função "Cliente do Cloud SQL" foi concedida ao principal do IAM

f1e64fbdc435d31c.png

E então.

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

bb8926b995a8875c.png

Atribua a função "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 momentos 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 a:

  1. Ative a Duet AI no seu projeto do GCP e configure para uso em um ambiente de desenvolvimento integrado e no Cloud Console.
  2. Use a Duet AI para geração, conclusão 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 do ambiente de desenvolvimento integrado e conversa multiturno, chat x geração de código inline, ações inteligentes, como explicação de código e confirmação de recitação, entre outros.

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