Neste tutorial, descrevemos como migrar apps da Web Node.js em execução no Heroku para o Cloud Run no Google Cloud. Este tutorial é destinado a arquitetos e proprietários de produtos que querem migrar os apps do Heroku para serviços gerenciados no Google Cloud.
O Cloud Run é uma plataforma de computação gerenciada que permite executar contêineres sem estado que podem ser invocados por solicitações HTTP. Ele é baseado no Knative de código aberto, que permite a portabilidade entre plataformas e é compatível com fluxos de trabalho e padrões de entrega contínua. A plataforma do Cloud Run está bem integrada ao pacote de produtos do Google Cloud e facilita o design e o desenvolvimento de apps portáteis, escalonáveis e resilientes.
Neste tutorial, você aprende a migrar um app para Google Cloud que está escrito em Node.js e usa Heroku Postgres como um serviço de apoio no Heroku. O app da Web é colocado em contêiner e hospedado no Cloud Run e usa o Cloud SQL para PostgreSQL como camada de persistência.
No tutorial, você usa um app simples denominado Tarefas, que permite visualizar e criar tarefas. Essas tarefas são armazenadas no Heroku Postgres na implantação atual do app no Heroku.
Neste tutorial, presumimos que você está familiarizado com a funcionalidade básica do Heroku (em inglês) e que tenha uma conta do Heroku (ou acesso a uma). Também presumimos que você esteja familiarizado com o Cloud Run, o Cloud SQL, o Docker e o Node.js (em inglês).
Como configurar o ambiente
- Abra o Cloud Shell. 
- No Cloud Shell, defina variáveis de ambiente e valores padrão para a Google Cloud CLI usada neste tutorial. - gcloud config set project PROJECT_ID gcloud config set run/region us-central1 - Substitua - PROJECT_IDpela ID do seu projeto.
Arquitetura
Nas figuras a seguir, descrevemos a arquitetura do app da Web no Heroku (assim como está) e o layout de arquitetura dele no Google Cloud (que você vai criar).
O app Tarefas atualmente implantado no Heroku consiste em um ou mais dynos da Web. Os dynos da Web são capazes de receber e responder ao tráfego HTTP, ao contrário dos dynos de worker, que são mais adequados a jobs em segundo plano e tarefas cronometradas. O app exibe uma página de índice que mostra tarefas armazenadas em um banco de dados do Postgres, usando a biblioteca de modelos Mustache para Node.js.
É possível acessar o app em um URL HTTPS. Uma rota /tasks nesse URL permite criar novas tarefas.
No Google Cloud, o Cloud Run é usado como a plataforma sem servidor para implantar o app Tarefas. O Cloud Run foi projetado para executar contêineres sem estado e orientados por solicitações. Ele é adequado para quando você precisa que seu serviço gerenciado seja compatível com apps contentorizados que fazem escalonamento automático, podendo chegar a zero quando não estão veiculando tráfego.
Como mapear componentes usados no Heroku para Google Cloud
Na tabela a seguir, correlacionamos os componentes da plataforma Heroku com o Google Cloud. Isso ajuda a traduzir a arquitetura descrita neste tutorial do Heroku para o Google Cloud.
| Componente | Plataforma Heroku | Google Cloud | 
|---|---|---|
| Contêineres | Dynos (em inglês): o Heroku usa o modelo de contêiner para criar e escalonar apps do Heroku. Esses contêineres do Linux são chamados de dynos e podem ser escalonados para um número especificado para atender às demandas de recursos do app Heroku. É possível selecionar entre vários tipos de dyno com base nos requisitos de memória e CPU do app. | Contêineres do Cloud Run: Google Cloud permite executar cargas de trabalho em contêineres sem estado que podem ser executados em um ambiente totalmente gerenciado ou em clusters do Google Kubernetes Engine (GKE). | 
| App da Web | App Heroku: os dynos são os blocos de construção dos apps Heroku. Os app geralmente consistem em um ou mais tipos de dyno, geralmente uma combinação de dynos da Web e do worker. | Serviço do Cloud Run: um app da Web pode ser modelado como um serviço do Cloud Run. Cada serviço recebe seu próprio endpoint HTTPS e pode aumentar ou diminuir automaticamente de 0 para N baseado no tráfego para seu endpoint de serviço. | 
| Banco de dados | Heroku Postgres é o banco de dados como um serviço (DaaS, na sigla em inglês) do Heroku com base no PostgreSQL. | Cloud SQL é um serviço de banco de dados gerenciado para bancos de dados relacionais no Google Cloud. | 
Como implantar o app da Web de exemplo Tarefas no Heroku
Nas próximas seções, mostramos como configurar a interface de linha de comando (CLI, na sigla em inglês) para o Heroku, como clonar o repositório de origem do GitHub e implantar o app no Heroku.
Configurar a interface de linha de comando do Heroku
Neste tutorial, a CLI do Heroku no Cloud Shell é executada e é necessário fazer a autenticação usando uma chave de API Heroku. Quando executada no Cloud Shell, a CLI do Heroku não pode autenticar usando uma senha ou autenticação baseada na Web.
Como alternativa, se você executar o exemplo em um terminal local, poderá usar qualquer método de autenticação da CLI do Heroku. Ao executar o tutorial em um terminal local, você também precisa instalar o Google Cloud CLI, o git e o Docker.
- Faça login no console da Web do Heroku e, na página de configurações da conta, copie o valor da chave de API. 
- No Cloud Shell, instale a CLI do Heroku 
- No Cloud Shell, autentique a CLI do Heroku Quando sua senha for solicitada, insira o valor da chave de API que você copiou do console do Heroku, não a senha que você usa para fazer login no console. - heroku login --interactive 
Clonar o repositório de origem
- No Cloud Shell, clone o repositório do GitHub do app Tarefas: - git clone https://github.com/GoogleCloudPlatform/migrate-webapp-heroku-to-cloudrun-node.git 
- Altere os diretórios para o diretório criado ao clonar o repositório: - cd migrate-webapp-heroku-to-cloudrun-node - O diretório contém os seguintes arquivos: - Um script Node.js denominado index.jscom o código das rotas veiculadas pelo app da Web.
- package.jsone- package-lock.jsonque descrevem as dependências do app da Web. É preciso instalar essas dependências para que o app seja executado.
- Um arquivo Procfileque especifica o comando que o app executa na inicialização. Você cria um arquivoProcfilepara implantar o app no Heroku.
- Um diretório views, com o conteúdo HTML veiculado pelo app da Web na rota "/".
- Um arquivo .gitignore
 
- Um script Node.js denominado 
Implantar um app no Heroku
- No Cloud Shell, crie um app Heroku: - heroku create - Anote o nome criado para o aplicativo. Você vai precisar desse valor na próxima etapa. 
- Crie uma variável de ambiente para o nome do app Heroku: - export APP_NAME=APP_NAME - Substitua - APP_NAMEpelo nome do app retornado pelo comando- heroku create.
- Adicione o complemento Heroku Postgres para aprovisionar um banco de dados PostgreSQL: - heroku addons:create heroku-postgresql:mini 
- Verifique se o complemento foi adicionado: - heroku addons - Se o complemento Postgres tiver sido adicionado, você verá uma mensagem semelhante a esta: - Add-on Plan Price State ----------------- ----- -------- ----- heroku-postgresql mini 5$/month created 
- Implante o app no Heroku: - git push heroku master 
- Execute o seguinte comando para confirmar o valor de DATABASE_URL. - heroku config - Anote o valor recuperado para DATABASE_URL. Ele será necessário na próxima etapa. 
- Execute um contêiner do Docker. - docker run -it --rm postgres psql "DATABASE_URL" - Substitua - DATABASE_URLpelo URL do Heroku Postgres que você anotou na etapa anterior.
- No contêiner do Docker, crie a tabela - TASKSusando o seguinte comando:- CREATE TABLE TASKS (DESCRIPTION TEXT NOT NULL); 
- Saia do contêiner: - exit
- No Cloud Shell, execute o seguinte comando para encontrar o URL da Web do seu app Heroku: - heroku info 
- Abra o URL da Web em uma janela do navegador. O app vai ficar parecido com a seguinte captura de tela, embora sua versão não tenha as tarefas listadas:   
- Crie tarefas de amostra no app pelo navegador. Verifique se as tarefas são recuperadas do banco de dados e visíveis na IU. 
Como preparar o código do app da Web para migração para o Cloud Run
Nesta seção, detalhamos as etapas necessárias para preparar seu app da Web para implantação no Cloud Run.
Criar e publicar seu contêiner do Docker no Container Registry
Você precisa de uma imagem do Docker para criar o contêiner do app para que seja executado no Cloud Run. Crie o contêiner manualmente ou usando o Buildpack.
Criar o contêiner manualmente
- No Cloud Shell, crie um Dockerfile no diretório criado clonando o repositório deste tutorial: - cat <<"EOF" > Dockerfile # Use the official Node image. # https://hub.docker.com/_/node FROM node:10-alpine # Create and change to the app directory. WORKDIR /app # Copying this separately prevents re-running npm install on every code change. COPY package*.json ./ RUN npm install # Copy local code to the container image. COPY . /app # Configure and document the service HTTP port. ENV PORT 8080 EXPOSE $PORT # Run the web service on container startup. CMD ["npm", "start"] EOF 
- Crie seu contêiner com o Cloud Build e publique a imagem no Container Registry: - gcloud builds submit --tag gcr.io/PROJECT_ID/APP_NAME:1 \ --gcs-log-dir=gs://PROJECT_ID_cloudbuild 
- Crie uma variável de ambiente para manter o nome da imagem do Docker que você criou: - export IMAGE_NAME="gcr.io/PROJECT_ID/APP_NAME:1" 
Criar contêiner com o Buildpacks
- No Cloud Shell, instale a CLI do pacote (em inglês): 
- Defina a CLI do pacote para usar o builder do Heroku por padrão: - pack config default-builder heroku/buildpacks:22 
- Crie uma variável de ambiente para manter o nome da imagem do Docker: - export IMAGE_NAME=gcr.io/PROJECT_ID/APP_NAME:1 
- Crie a imagem usando o comando - packe envie ou publique a imagem para o Container Registry:- pack build --publish $IMAGE_NAME 
Crie uma instância do Cloud SQL para PostgreSQL
Crie uma instância do Cloud SQL para PostgreSQL para servir como o back-end do app da Web. Neste tutorial, o PostgreSQL é mais adequado como app de exemplo implantado no Heroku, que usa um banco de dados do Postgres como back-end. Para os fins deste app, a migração para o Cloud SQL para PostgreSQL de um serviço Postgres gerenciado não requer alterações no esquema.
- Prepare sua rede para o Cloud SQL com um endereço IP privado. - gcloud compute addresses create google-managed-services-default \ --global \ --purpose=VPC_PEERING \ --prefix-length=16 \ --description="peering range for CloudSQL Private Service Access" \ --network=default gcloud services vpc-peerings connect \ --service=servicenetworking.googleapis.com \ --ranges=google-managed-services-default \ --network=default \ --project=PROJECT_ID 
- Crie uma variável de ambiente denominada - CLOUDSQL_DB_NAMEpara manter o nome da instância de banco de dados criada na próxima etapa:- export CLOUDSQL_DB_NAME=tasks-db 
- Crie o banco de dados: - gcloud sql instances create $CLOUDSQL_DB_NAME \ --cpu=1 \ --memory=4352Mib \ --database-version=POSTGRES_15 \ --region=us-central1 \ --network default \ --no-assign-ip - A instância pode levar alguns minutos para ser inicializada. 
- Defina uma senha para o usuário do Postgres: - gcloud sql users set-password postgres \ --instance=$CLOUDSQL_DB_NAME \ --password=POSTGRES_PASSWORD- Substitua - POSTGRES_PASSWORDpela senha que você quer usar para o banco de dados do Postgres.
Importar dados para o Cloud SQL do Heroku Postgres
Há vários padrões de migração que podem ser utilizados para migrar dados para o Cloud SQL. A melhor abordagem, que requer pouca ou nenhuma inatividade, é configurar o Cloud SQL como uma réplica para o banco de dados que está sendo migrado e fazer do Cloud SQL a instância principal após a migração. O Heroku Postgres não é compatível com réplicas externas (seguidores). Nesse tutorial, você usa ferramentas de código aberto para migrar o esquema do app.
Para o app Tarefas neste tutorial, use o utilitário pg_dump (em inglês) para exportar dados do Heroku Postgres para um bucket do Cloud Storage e importá-los para o Cloud SQL. Esse utilitário pode transferir dados em versões homogêneas ou quando a versão do banco de dados de destino é mais recente que o banco de dados de origem.
- No Cloud Shell, receba as credenciais do banco de dados do Heroku Postgres anexado ao app de exemplo. Você precisará dessas credenciais na próxima etapa. - heroku pg:credentials:url - Esse comando retorna a string de informações de conexão e o URL de conexão do app. A string de informações de conexão tem o seguinte formato: - "dbname=DATABASE_NAME host=FQDN port=5432 user=USER_NAME password=PASSWORD_STRING sslmode=require" - Você vai precisar dos valores mostrados na string de conexão na próxima etapa. - Para um exemplo de um valor FQDN (nome de domínio totalmente qualificado) em uma string de informações de conexão, consulte a documentação do Heroku. 
- Defina variáveis de ambiente para manter os valores do Heroku que você usará nas etapas subsequentes: - export HEROKU_PG_DBNAME=DATABASE_NAME export HEROKU_PG_HOST=FQDN export HEROKU_PG_USER=USER_NAME export HEROKU_PG_PASSWORD=PASSWORD_STRING - Substitua: - DATABASE_NAME: o nome do banco de dados mostrado na string de informações.
- FQDN: o FQDN mostrado na string de informações.
- USER_NAME: o nome do usuário mostrado na string de informações.
- PASSWORD_STRING: a string de senha mostrada na string de informações.
 
- Crie um backup em formato SQL do banco de dados do Heroku Postgres: - docker run \ -it --rm \ -e PGPASSWORD=$HEROKU_PG_PASSWORD \ -v $(pwd):/tmp \ --entrypoint "pg_dump" \ postgres \ -Fp \ --no-acl \ --no-owner \ -h $HEROKU_PG_HOST \ -U $HEROKU_PG_USER \ $HEROKU_PG_DBNAME > herokudump.sql 
- Crie uma variável de ambiente para manter o nome do bucket do Cloud Storage. - export PG_BACKUP_BUCKET=gs://PROJECT_ID-pg-backup-bucket 
- Crie um bucket do Cloud Storage: - gcloud storage buckets create $PG_BACKUP_BUCKET \ --location=us-central1 \ --public-access-prevention \ --uniform-bucket-level-access 
- Faça upload do arquivo SQL neste bucket: - gcloud storage cp herokudump.sql $PG_BACKUP_BUCKET/herokudump.sql 
- Autorize a instância do Cloud SQL com os papéis necessários para importar o arquivo SQL do bucket do Cloud Storage: - gcloud projects add-iam-policy-binding PROJECT_ID \ --member=serviceAccount:$(gcloud sql instances describe $CLOUDSQL_DB_NAME --format='get("serviceAccountEmailAddress")') \ --role=roles/storage.objectAdmin gcloud projects add-iam-policy-binding PROJECT_ID \ --member=serviceAccount:$(gcloud sql instances describe $CLOUDSQL_DB_NAME --format='get("serviceAccountEmailAddress")') \ --role=roles/cloudsql.editor
- Importe o arquivo SQL para a instância do Cloud SQL: - gcloud sql import sql $CLOUDSQL_DB_NAME $PG_BACKUP_BUCKET/herokudump.sql \ --database=postgres \ --user=postgres - Quando solicitado - do you want to continue (y/n), digite "y".
Como o Cloud Run acessa o banco de dados do Cloud SQL
Assim como o app da Web implantado no Heroku precisa se conectar à instância gerenciada do Heroku Postgres, o Cloud Run requer acesso ao Cloud SQL para poder ler e gravar dados.
O Cloud Run se comunica com o Cloud SQL usando o proxy do Cloud SQL que é ativado e configurado automaticamente quando você implanta o contêiner no Cloud Run. O banco de dados não precisa ter endereços IP externos aprovados porque toda a comunicação que ele recebe é do proxy usando TCP seguro.
Seu código precisa chamar operações do banco de dados (como a busca de dados do banco de dados ou gravações nele) invocando o proxy em um soquete UNIX.
Como esse app da Web é escrito em Node.js, use a biblioteca pg-connection-string para analisar um URL de banco de dados e criar um objeto config. A vantagem dessa abordagem é que ela facilita a conexão com o banco de dados de back-end no Heroku e no Cloud Run.
Na próxima etapa, você passará o URL do banco de dados como variável de ambiente ao implantar o app da Web.
Implantar o aplicativo de exemplo no Cloud Run
- No Cloud Shell, configure o acesso VPC sem servidor para permitir tráfego privado do Cloud Run ao Clodu SQL: - gcloud compute networks subnets create serverless-connector-subnet \ --network=default \ --range=10.0.0.0/28 \ --region=us-central1 gcloud compute networks vpc-access connectors create serverless-connector \ --region=us-central1 \ --subnet=serverless-connector-subnet 
- No Cloud Shell, crie uma variável de ambiente que contenha o nome da conexão da instância do Cloud SQL que você criou: - export DB_CONN_NAME=$(gcloud sql instances describe $CLOUDSQL_DB_NAME --format='value(connectionName)') 
- Crie uma variável de ambiente denominada - DATABASE_URLpara manter a string de conexão e se conectar ao Proxy do Cloud SQL por uma porta UNIX.- export DATABASE_URL="socket:/cloudsql/${DB_CONN_NAME}?db=postgres&user=postgres&password=POSTGRES_PASSWORD"
- Crie uma conta de serviço para o Cloud Run com um papel do IAM para se conectar ao banco de dados: - gcloud iam service-accounts create sa-run-db-client gcloud projects add-iam-policy-binding PROJECT_ID \ --member=serviceAccount:sa-run-db-client@PROJECT_ID.iam.gserviceaccount.com \ --role=roles/cloudsql.client
- Implante o app da Web no Cloud Run. - gcloud run deploy tasksapp-PROJECT_ID \ --image=$IMAGE_NAME \ --service-account=sa-run-db-client@PROJECT_ID.iam.gserviceaccount.com \ --set-env-vars=DATABASE_URL=$DATABASE_URL \ --add-cloudsql-instances $DB_CONN_NAME \ --vpc-connector serverless-connector \ --allow-unauthenticated- O comando anterior também vincula seu contêiner do Cloud Run à instância de banco de dados do Cloud SQL que você criou. O comando define uma variável de ambiente para o Cloud Run apontar para a string - DATABASE_URLque você criou na etapa anterior.
Teste o aplicativo
- No Cloud Shell, veja o URL em que o Cloud Run veicula tráfego: - gcloud run services list - Também é possível analisar o serviço do Cloud Run no Google Cloud console. 
- Verifique se o app da Web está aceitando solicitações HTTP navegando até o URL do serviço do Cloud Run. 
O Cloud Run cria ou ativa um novo contêiner quando uma solicitação HTTP é enviada ao endpoint de exibição e se um contêiner ainda não estiver em execução. Isso significa que a solicitação que gera um novo contêiner pode demorar um pouco mais para ser veiculada. Pensando nesse tempo extra, considere o número de solicitações simultâneas compatíveis com seu app e quaisquer requisitos de memória específicos que ele tenha.
Para este app, você usa as configurações de simultaneidade padrão, que permitem que um serviço do Cloud Run atenda 80 solicitações simultaneamente a partir de um único contêiner.