Desenvolvimento do InnerLoop com NodeJS

1. Visão geral

Este laboratório demonstra recursos e funcionalidades projetados para simplificar o fluxo de trabalho de desenvolvimento de engenheiros de software encarregados de desenvolver aplicativos NodeJS em um ambiente conteinerizado. O desenvolvimento típico de contêineres exige que o usuário entenda os detalhes dos contêineres e do processo de build deles. Além disso, os desenvolvedores geralmente precisam interromper o fluxo de trabalho, saindo do ambiente de desenvolvimento integrado para testar e depurar os aplicativos em ambientes remotos. Com as ferramentas e tecnologias mencionadas neste tutorial, os desenvolvedores podem trabalhar de forma eficaz com aplicativos contêinerizados sem sair do ambiente de desenvolvimento integrado.

O que você vai aprender

Neste laboratório, você vai aprender métodos para desenvolver com contêineres no GCP, incluindo:

  • Como criar um aplicativo Node.js inicial
  • Como configurar um aplicativo Node.js para desenvolvimento de contêineres
  • Como programar um serviço REST CRUD simples
  • Como implantar no GKE
  • Depuração de um estado de erro
  • Usar pontos de interrupção / registros
  • Implantação dinâmica de mudanças de volta para o GKE
  • Opcional: integrar o CloudSQL para persistência de back-end

2. Configuração e requisitos

Configuração de ambiente personalizada

  1. Faça login no Console do Google Cloud e crie um novo projeto ou reutilize um existente. Crie uma conta do Gmail ou do Google Workspace, se ainda não tiver uma.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • O Nome do projeto é o nome de exibição para os participantes do projeto. Ele é uma string de caracteres que não é usada pelas APIs do Google e pode ser atualizada a qualquer momento.
  • O ID do projeto precisa ser exclusivo em todos os projetos do Google Cloud e não pode ser alterado após a definição. O Console do Cloud gera automaticamente uma string única, geralmente não importa o que seja. Na maioria dos codelabs, você precisará fazer referência ao ID do projeto, que geralmente é identificado como PROJECT_ID. Então, se você não gostar dele, gere outro ID aleatório ou crie um próprio e veja se ele está disponível. Em seguida, ele fica "congelado" depois que o projeto é criado.
  • Há um terceiro valor, um Número de projeto, que algumas APIs usam. Saiba mais sobre esses três valores na documentação.
  1. Em seguida, você precisará ativar o faturamento no Console do Cloud para usar os recursos/APIs do Cloud. A execução deste codelab não será muito cara, se tiver algum custo. Para encerrar os recursos e não gerar cobranças além deste tutorial, siga as instruções de "limpeza" encontradas no final do codelab. Novos usuários do Google Cloud estão qualificados para o programa de US$ 300 de avaliação sem custos.

Iniciar o editor do Cloud Shell

Este laboratório foi projetado e testado para uso com o editor do Google Cloud Shell. Para acessar o editor,

  1. Acesse seu projeto do Google em https://console.cloud.google.com.
  2. No canto superior direito, clique no ícone do editor do Cloud Shell.

8560cc8d45e8c112.png

  1. Um novo painel será aberto na parte de baixo da janela
  2. Clique no botão "Abrir editor".

9e504cb98a6a8005.png

  1. O editor será aberto com um explorador à direita e o editor na área central.
  2. Um painel de terminal também vai estar disponível na parte de baixo da tela.
  3. Se o terminal NÃO estiver aberto, use a combinação de teclas "ctrl+`" para abrir uma nova janela do terminal.

Configurar a gcloud

No Cloud Shell, defina o ID do projeto e a região em que você quer implantar o aplicativo. Salve-as como variáveis PROJECT_ID e REGION.

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

Configurar o cluster do GKE e o banco de dados

  1. Faça o download do script de configuração e torne-o executável.
wget https://raw.githubusercontent.com/GoogleCloudPlatform/container-developer-workshop/main/labs/nodejs/setup.sh
chmod +x setup.sh

Provisionar a infraestrutura usada neste laboratório

Neste laboratório, você vai implantar código no GKE e acessar dados armazenados em um banco de dados do Spanner. O script de configuração abaixo prepara essa infraestrutura para você.

  1. Abra o arquivo setup.sh e edite os valores das senhas que estão definidas como CHANGEME.
  2. Execute o script de configuração para criar um cluster do GKE e um banco de dados do Cloud SQL que você vai usar neste laboratório.
./setup.sh
  1. No Cloud Shell, crie um diretório chamado mynodejsapp.
mkdir mynodejsapp
  1. Mude para esse diretório e abra-o como um espaço de trabalho. Isso vai recarregar o editor criando uma configuração de espaço de trabalho na pasta recém-criada.
cd mynodejsapp && cloudshell workspace .
  1. Instale o Node e o NPM usando o NVM.
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
        
        # This loads nvm bash_completion
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  

nvm install stable

nvm alias default stable

3. Criar um novo aplicativo inicial

  1. Inicializar o aplicativo

Crie um arquivo package.json executando o seguinte comando:

npm init
    Choose the entry point: (index.js) src/index.js and default values for the rest of the parameters. This will create the file with following contents
{
  "name": "mynodejsapp",
  "version": "1.0.0",
  "description": "",
  "main": "src/index.js",,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
  1. Adicionar um ponto de entrada

Edite esse arquivo para incluir o comando de início no script "start": "node src/index.js",. Depois da mudança, os scripts vão ficar parecidos com o snippet de código abaixo:

"scripts": {
    "start": "node src/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  1. Adicionar a dependência do Express

O código que vamos adicionar também usa express. Portanto, vamos adicionar essa dependência ao arquivo package.json. Depois de todas as mudanças, o arquivo package.json vai ficar assim:

​​{
  "name": "mynodejsapp",
  "version": "1.0.0",
  "description": "",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Your Name",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.4"
  }
}
  1. Criar o arquivo index.js

Crie um diretório de origem chamado src

Crie src/index.js com o seguinte código:

const express = require('express');
const app = express();
const PORT = 8080;

app.get('/', (req, res) => {
    var message="Greetings from Node";
    res.send({ message: message });
  });

app.listen(PORT, () => {
  console.log(`Server running at: http://localhost:${PORT}/`);

});

Observe que a PORTA está definida como o valor 8080.

Gerar manifestos

O Skaffold oferece ferramentas integradas para simplificar o desenvolvimento de contêineres. Nesta etapa, você vai inicializar o skaffold, que cria automaticamente arquivos YAML básicos do Kubernetes. Execute o comando abaixo para iniciar o processo.

Execute o comando a seguir no terminal:

skaffold init --generate-manifests

Quando solicitado:

  • Insira 8080 para a porta
  • Digite y para salvar a configuração.

Dois arquivos são adicionados à visualização do espaço de trabalho: skaffold.yaml e deployment.yaml.

Atualizar nome do app

Os valores padrão incluídos na configuração não correspondem ao nome do seu aplicativo. Atualize os arquivos para fazer referência ao nome do aplicativo em vez dos valores padrão.

  1. Mudar entradas na configuração do Skaffold
  • Abrir skaffold.yaml
  • Selecione o nome da imagem definido como package-json-image.
  • Clique com o botão direito do mouse e escolha "Mudar todas as ocorrências".
  • Digite o novo nome como mynodejsapp
  1. Mudar entradas na configuração do Kubernetes
  • Abrir arquivo deployment.yaml
  • Selecione o nome da imagem definido como package-json-image.
  • Clique com o botão direito do mouse e escolha "Mudar todas as ocorrências".
  • Digite o novo nome como mynodejsapp

No arquivo skaffold.yaml, a seção build usa buildpacks para colocar o aplicativo em contêineres. Esse código não tem Dockerfile, e o desenvolvedor não precisa de nenhum conhecimento sobre Docker para conteinerizar o aplicativo.

Além disso, a sincronização dinâmica é ativada automaticamente entre o editor e o contêiner em execução por essa configuração do Skaffold. Nenhuma configuração adicional é necessária para ativar a sincronização dinâmica.

4. Como percorrer o processo de desenvolvimento

Nesta seção, você vai seguir algumas etapas usando o plug-in Cloud Code para aprender os processos básicos e validar a configuração e a instalação do aplicativo inicial.

O Cloud Code se integra ao Skaffold para simplificar seu processo de desenvolvimento. Ao implantar no GKE nas etapas a seguir, o Cloud Code e o Skaffold vão criar automaticamente a imagem do contêiner, enviá-la para um Container Registry e implantar o aplicativo no GKE. Isso acontece nos bastidores, abstraindo os detalhes do fluxo do desenvolvedor. O Cloud Code também melhora seu processo de desenvolvimento ao oferecer recursos tradicionais de depuração e hotsync para desenvolvimento baseado em contêineres.

Implantar no Kubernetes

  1. No painel na parte de baixo do editor do Cloud Shell, selecione Cloud Code 

fdc797a769040839.png

  1. No painel que aparece na parte de cima, selecione Executar no Kubernetes. Se solicitado, selecione "Sim" para usar o contexto atual do Kubernetes.

cfce0d11ef307087.png

  1. Na primeira vez que você executar o comando, uma solicitação vai aparecer na parte de cima da tela perguntando se você quer o contexto atual do Kubernetes. Selecione "Sim" para aceitar e usar o contexto atual.

817ee33b5b412ff8.png

  1. Em seguida, uma solicitação vai aparecer perguntando qual registro de contêiner usar. Pressione "Enter" para aceitar o valor padrão fornecido.

eb4469aed97a25f6.png

  1. Selecione a guia "Saída" no painel de baixo para conferir o progresso e as notificações.

f95b620569ba96c5.png

  1. Selecione "Kubernetes: Run/Debug - Detailed" no menu suspenso do canal à direita para conferir mais detalhes e registros transmitidos ao vivo dos contêineres.

94acdcdda6d2108.png

  1. Para voltar à visualização simplificada, selecione "Kubernetes: Run/Debug" no menu suspenso.
  2. Quando o build e os testes forem concluídos, a guia "Saída" vai mostrar: Resource deployment/mynodejsapp status completed successfully, e um URL será listado: "URL encaminhado do app de demonstração do serviço: http://localhost:8080"
  3. No terminal do Cloud Code, passe o cursor sobre o URL na saída (http://localhost:8080) e, na dica de ferramenta que aparece, selecione "Abrir visualização na Web".

A resposta será:

{"message":"Greetings from Node"}

Recarga automática

  1. Navegue para src/index.js. Edite o código da mensagem de saudação para 'Hello from Node'

Na janela Output, na visualização Kubernetes: Run/Debug, o observador sincroniza os arquivos atualizados com o contêiner no Kubernetes.

Update initiated
File sync started for 1 files for gcr.io/myproject/mynodejsapp:latest@sha256:f554756b3b4d6c301c4b26ef96102227cfa2833270db56241248ae42baa1971a
File sync succeeded for 1 files for gcr.io/myproject/mynodejsapp:latest@sha256:f554756b3b4d6c301c4b26ef96102227cfa2833270db56241248ae42baa1971a
Update succeeded
  1. Se você mudar para a visualização Kubernetes: Run/Debug - Detailed, vai notar que ela reconhece as mudanças no arquivo e reinicia o nó.
files modified: [src/index.js]
Copying files:map[src/index.js:[/workspace/src/index.js]]togcr.io/myproject/mynodejsapp:latest@sha256:f554756b3b4d6c301c4b26ef96102227cfa2833270db56241248ae42baa1971a
Syncing 1 files for gcr.io/myproject/mynodejsapp:latest@sha256:f554756b3b4d6c301c4b26ef96102227cfa2833270db56241248ae42baa1971a
Watching for changes...
[mynodejsapp]
[mynodejsapp]> mynodejsapp@1.0.0 start /workspace
[mynodejsapp]> node src/index.js
[mynodejsapp]
[mynodejsapp]Server running at: http://localhost:8080/
  1. Atualize o navegador para ver os resultados atualizados.

Depuração

  1. Acesse a visualização de depuração e pare a linha de execução atual 647213126d7a4c7b.png.
  2. Clique em Cloud Code no menu da parte de baixo e selecione Debug on Kubernetes para executar o aplicativo no modo debug.
  • Na visualização Kubernetes Run/Debug - Detailed da janela Output, observe que o Skaffold vai implantar esse aplicativo no modo de depuração.
  • A criação e a implantação do aplicativo levam alguns minutos. Você vai notar um depurador anexado desta vez.
Port forwarding pod/mynodejsapp-6bbcf847cd-vqr6v in namespace default, remote port 9229 -> http://127.0.0.1:9229
[mynodejsapp]Debugger attached.
  1. A barra de status na parte de baixo muda de azul para laranja, indicando que está no modo de depuração.
  2. Na visualização Kubernetes Run/Debug, observe que um contêiner depurável é iniciado.
**************URLs*****************
Forwarded URL from service mynodejsapp-service: http://localhost:8080
Debuggable container started pod/mynodejsapp-deployment-6bc7598798-xl9kj:mynodejsapp (default)
Update succeeded
***********************************

Usar pontos de interrupção

  1. Abra o src/index.js
  2. Localize a instrução que diz var message="Greetings from Node";.
  3. Adicione um ponto de interrupção a essa linha clicando no espaço em branco à esquerda do número da linha. Um indicador vermelho vai aparecer para mostrar que o ponto de interrupção foi definido.
  4. Recarregue o navegador e observe que o depurador interrompe o processo no ponto de interrupção e permite investigar as variáveis e o estado do aplicativo que está sendo executado remotamente no GKE.
  5. Clique na seção de variáveis até encontrar a variável "message".
  6. Execute a linha pressionando "Step over" 7cfdee4fd6ef5c3a.png.
  7. Observe o valor atual da variável "message" mudar para "Greetings from Node".
  8. Clique duas vezes no nome da variável "target" e, no pop-up, mude o valor para algo diferente, como "Hello from Node".
  9. Clique no botão "Continuar" no painel de controle de depuração.
  10. Revise a resposta no navegador, que agora mostra o valor atualizado que você acabou de inserir.
  11. Pare o modo "Debug" pressionando o botão de parada 647213126d7a4c7b.png e remova o ponto de interrupção clicando nele novamente.

5. Como desenvolver um serviço REST CRUD simples

Neste ponto, seu aplicativo está totalmente configurado para desenvolvimento em contêineres, e você já passou pelo fluxo de trabalho de desenvolvimento básico com o Cloud Code. Nas seções a seguir, você vai praticar o que aprendeu adicionando endpoints de serviço REST que se conectam a um banco de dados gerenciado no Google Cloud.

Configurar dependências

O código do aplicativo usa um banco de dados para manter os dados do serviço REST. Adicione o seguinte ao arquivo package.json para garantir que as dependências estejam disponíveis:

  1. Adicione mais duas dependências, pg e sequelize, ao arquivo package.json para criar um aplicativo CRUD do Postgres. Depois de publicar as alterações, a seção de dependências ficaria assim:
    "dependencies": {
    "express": "^4.16.4",
    "pg": "^8.7.3",
    "sequelize": "^6.17.0"
  }

Codificar o serviço REST

  1. Adicione o código do aplicativo CRUD a ele.
wget -O app.zip https://github.com/GoogleCloudPlatform/container-developer-workshop/raw/main/labs/nodejs/app.zip

unzip app.zip

Esse código tem

  • Pasta models com o modelo de entidade para item
  • Pasta controllers com o código que faz operações CRUD
  • Pasta routes que roteia padrões de URL específicos para diferentes chamadas
  • Pasta config com detalhes de conectividade do banco de dados
  1. Observe que a configuração do banco de dados no arquivo db.config.js se refere às variáveis de ambiente que precisam ser fornecidas para se conectar ao banco de dados. Você também precisa analisar a solicitação recebida para codificação de URL.
  2. Adicione o snippet de código a seguir em src/index.js para se conectar ao código CRUD do seu arquivo JavaScript principal logo antes da última seção que começa com app.listen(PORT, () => {.
const bodyParser = require('body-parser')
app.use(bodyParser.json())
app.use(
 bodyParser.urlencoded({
   extended: true,
 })
)
const db = require("../app/models");
db.sequelize.sync();
require("../app/routes/item.routes")(app);
  1. Edite a implantação no arquivo deployment.yaml para adicionar as variáveis de ambiente e fornecer as informações de conectividade do banco de dados.

Atualize a entrada de especificação no final do arquivo para corresponder à seguinte definição:

    spec:
      containers:
      - name: mynodejsapp
        image: mynodejsapp
        env:
        - name: DB_HOST
          value: ${DB_INSTANCE_IP}        
        - name: DB_PORT
          value: "5432"  
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: gke-cloud-sql-secrets
              key: username
        - name: DB_PASS
          valueFrom:
            secretKeyRef:
              name: gke-cloud-sql-secrets
              key: password
        - name: DB_NAME
          valueFrom:
            secretKeyRef:
              name: gke-cloud-sql-secrets
              key: database
  1. Substitua o valor DB_HOST pelo endereço do seu banco de dados.
export DB_INSTANCE_IP=$(gcloud sql instances describe mytest-instance \
    --format=json | jq \
    --raw-output ".ipAddresses[].ipAddress")

envsubst < deployment.yaml > deployment.new && mv deployment.new deployment.yaml

Implantar e validar o aplicativo

  1. No painel na parte de baixo do editor do Cloud Shell, selecione Cloud Code e depois Debug on Kubernetes na parte de cima da tela.
  2. Quando o build e os testes forem concluídos, a guia "Saída" vai mostrar: Resource deployment/mynodejsapp status completed successfully, e um URL será listado: "URL encaminhado do serviço mynodejsapp: http://localhost:8080"
  3. Adicione alguns itens.

No terminal do Cloud Shell, execute os comandos abaixo:

URL=localhost:8080
curl -X POST $URL/items -d '{"itemName":"Body Spray", "itemPrice":3.2}' -H "Content-Type: application/json"
curl -X POST $URL/items -d '{"itemName":"Nail Cutter", "itemPrice":2.5}' -H "Content-Type: application/json"
  1. Teste o GET executando $URL/items no navegador. Também é possível executar o curl na linha de comando
curl -X GET $URL/items
  1. Teste de exclusão: agora tente excluir um item executando. Mude o valor de item-id, se necessário.
curl -X DELETE $URL/items/1
    This throws an error message
{"message":"Could not delete Item with id=[object Object]"}

Identificar e corrigir o problema

  1. Reinicie o aplicativo no modo de depuração e encontre o problema. Veja algumas dicas:
  • Sabemos que há algo errado com o DELETE, já que ele não está retornando o resultado desejado. Portanto, você definiria o ponto de interrupção no método itemcontroller.js->exports.delete.
  • Execute a execução passo a passo e observe as variáveis em cada passo para ver os valores das variáveis locais na janela à esquerda.
  • Para observar valores específicos, como request.params, adicione essa variável à janela "Watch".
  1. O valor atribuído a id é undefined. Mude o código para corrigir o problema.

O snippet de código corrigido ficaria assim.

// Delete a Item with the specified id in the request
exports.delete = (req, res) => {
    const id = req.params.id;
  1. Depois que o aplicativo for reiniciado, tente excluir de novo.
  2. Clique no quadrado vermelho na barra de ferramentas de depuração 647213126d7a4c7b.png para interromper a sessão.

6. Limpeza

Parabéns! Neste laboratório, você criou um novo aplicativo Node.js do zero e o configurou para funcionar no modo de implantação dinâmica com contêineres. Em seguida, você implantou e depurou o aplicativo em um cluster do GKE remoto seguindo o mesmo fluxo de desenvolvedor encontrado em stacks de aplicativos tradicionais.

Para fazer a limpeza depois de concluir o laboratório:

  1. Excluir os arquivos usados no laboratório
cd ~ && rm -rf mynodejsapp && rm -f setup.sh
  1. Exclua o projeto para remover toda a infraestrutura e os recursos relacionados.