Este tutorial descreve como migrar apps Web Node.js que estão a ser executadas no Heroku para o Cloud Run no Google Cloud. Este tutorial destina-se a arquitetos e proprietários de produtos que querem migrar as respetivas apps do Heroku para serviços geridos no Google Cloud.
O Cloud Run é uma plataforma de computação gerida que lhe permite executar contentores sem estado invocáveis através de pedidos HTTP. É criada com base no código aberto Knative, o que permite a portabilidade entre plataformas e suporta fluxos de trabalho de contentores e normas para a entrega contínua. A plataforma Cloud Run está bem integrada com o conjunto de produtos e facilita a conceção e o desenvolvimento de apps portáteis, escaláveis e resilientes. Google Cloud
Neste tutorial, vai aprender a migrar uma app Google Cloud escrita em Node.js e que usa o Heroku Postgres como um serviço de apoio no Heroku. A app Web está contentorizada e alojada no Cloud Run e usa o Cloud SQL para PostgreSQL como camada de persistência.
No tutorial, usa uma app simples denominada Tasks que lhe permite ver e criar tarefas. Estas tarefas são armazenadas no Heroku Postgres na implementação atual da app no Heroku.
Este tutorial pressupõe que conhece a funcionalidade básica do Heroku e que tem uma conta do Heroku (ou acesso a uma). Também pressupõe que tem conhecimentos sobre o Cloud Run, o Cloud SQL, o Docker e o Node.js.
Configurar o seu ambiente
Abra o Cloud Shell.
No Cloud Shell, defina as variáveis de ambiente e os valores predefinidos para a CLI do Google Cloud usada neste tutorial.
gcloud config set project PROJECT_ID gcloud config set run/region us-central1
Substitua
PROJECT_ID
pelo ID do seu projeto.
Arquitetura
As figuras seguintes descrevem a arquitetura da app Web no Heroku (tal como está) e o esquema arquitetónico no Google Cloud (que vai criar).
A app Tasks atualmente implementada no Heroku consiste num ou mais web dynos. Os dynos Web podem receber e responder ao tráfego HTTP, ao contrário dos dynos de trabalho, que são mais adequados para tarefas em segundo plano e tarefas programadas. A app apresenta uma página de índice que apresenta tarefas armazenadas numa base de dados Postgres, usando a biblioteca de modelos Mustache para Node.js.
Pode aceder à app através de um URL HTTPS. Uma rota /tasks
nesse URL permite-lhe
criar novas tarefas.
No Google Cloud, o Cloud Run é usado como a plataforma sem servidor para implementar a app Tasks. O Cloud Run foi concebido para executar contentores sem estado e baseados em pedidos. É adequado quando precisa que o seu serviço gerido suporte apps contentorizadas com escala automática e também escala para zero quando não estão a publicar tráfego.
Mapeamento de componentes usados no Heroku para o Google Cloud
A tabela seguinte mapeia os componentes na plataforma Heroku para Google Cloud. Este mapeamento ajuda a traduzir a arquitetura descrita neste tutorial do Heroku para o Google Cloud.
Componente | Plataforma Heroku | Google Cloud |
---|---|---|
Contentores | Dynos: O Heroku usa o modelo de contentor para criar e dimensionar apps Heroku. Estes contentores Linux são denominados dynos e podem ser dimensionados para um número que especificar para suportar as exigências de recursos da sua app Heroku. Pode selecionar entre uma variedade de tipos de dynos com base nos requisitos de memória e CPU da sua app. | Contentores do Cloud Run: Google Cloud suporta a execução de cargas de trabalho em contentores sem estado que podem ser executados num ambiente totalmente gerido ou em clusters do Google Kubernetes Engine (GKE). |
App Web | App Heroku: os dynos são os elementos básicos das apps Heroku. Normalmente, as apps consistem num ou mais tipos de dynos, geralmente uma combinação de dynos web e worker. | Serviço do Cloud Run: uma app Web pode ser modelada como um serviço do Cloud Run. Cada serviço tem o seu próprio ponto final HTTPS e pode ser dimensionado automaticamente para cima ou para baixo de 0 a N com base no tráfego para o ponto final do serviço. |
Bases de dados | O Heroku Postgres é a base de dados como serviço (DaaS) da Heroku baseada no PostgreSQL. | O Cloud SQL é um serviço de base de dados gerido para bases de dados relacionais no Google Cloud. |
Implementar a app Web Tasks de exemplo no Heroku
As secções seguintes mostram como configurar a interface de linhas de comando (CLI) para o Heroku, clonar o repositório de origem do GitHub e implementar a app no Heroku.
Configure a interface de linhas de comando para o Heroku
Este tutorial executa a CLI do Heroku no Cloud Shell e tem de ser autenticado através de uma chave da API do Heroku. Quando executado no Cloud Shell, a CLI do Heroku não pode fazer a autenticação através de uma palavra-passe ou da autenticação baseada na Web.
Em alternativa, se executar o exemplo num terminal local, pode usar qualquer método de autenticação da CLI do Heroku. Quando executar o tutorial num terminal local, também tem de instalar a CLI do Google Cloud, o git e o Docker.
Inicie sessão na consola Web do Heroku e, de seguida, na página de definições da conta, copie o valor da chave da API.
No Cloud Shell, instale a CLI do Heroku
No Cloud Shell, autentique a CLI do Heroku. Quando lhe for pedida a palavra-passe, introduza o valor da chave da API que copiou da consola do Heroku e não a palavra-passe que usa para iniciar sessão na consola.
heroku login --interactive
Clone o repositório de origem
No Cloud Shell, clone o repositório do GitHub da app Tasks de exemplo:
git clone https://github.com/GoogleCloudPlatform/migrate-webapp-heroku-to-cloudrun-node.git
Altere os diretórios para o diretório criado através da clonagem do repositório:
cd migrate-webapp-heroku-to-cloudrun-node
O diretório contém os seguintes ficheiros:
- Um script Node.js denominado
index.js
com o código para as rotas publicadas pela app Web. package.json
epackage-lock.json
que descrevem as dependências da app Web. Tem de instalar estas dependências para que a app seja executada.- Um ficheiro
Procfile
que especifica o comando que a app executa no arranque. Crie um ficheiroProcfile
para implementar a sua app no Heroku. - Um diretório
views
com o conteúdo HTML publicado pela app Web na rota "/". - Um ficheiro
.gitignore
.
- Um script Node.js denominado
Implemente uma app no Heroku
No Cloud Shell, crie uma app Heroku:
heroku create
Tome nota do nome criado para a app. Precisa deste valor no passo seguinte.
Crie uma variável de ambiente para o nome da app Heroku:
export APP_NAME=APP_NAME
Substitua
APP_NAME
pelo nome da app devolvido pelo comandoheroku create
.Adicione o suplemento Heroku Postgres para aprovisionar uma base de dados PostgreSQL:
heroku addons:create heroku-postgresql:mini
Certifique-se de que o suplemento foi adicionado com êxito:
heroku addons
Se o suplemento Postgres tiver sido adicionado com êxito, é apresentada uma mensagem semelhante à seguinte:
Add-on Plan Price State ----------------- ----- -------- ----- heroku-postgresql mini 5$/month created
Implemente a app no Heroku:
git push heroku master
Execute o seguinte comando para confirmar o valor de DATABASE_URL.
heroku config
Anote o valor obtido para DATABASE_URL. Precisa deste valor no passo seguinte.
Execute um contentor Docker.
docker run -it --rm postgres psql "DATABASE_URL"
Substitua
DATABASE_URL
pelo URL do Heroku Postgres que anotou no passo anterior.No contentor do Docker, crie a tabela
TASKS
com o seguinte comando:CREATE TABLE TASKS (DESCRIPTION TEXT NOT NULL);
Saia do contentor:
exit
No Cloud Shell, execute o seguinte comando para obter o URL da Web da sua app Heroku:
heroku info
Abra o URL da Web numa janela do navegador. A app tem o seguinte aspeto: captura de ecrã (embora a sua versão não tenha as tarefas apresentadas):
Crie tarefas de exemplo na sua app a partir do navegador. Certifique-se de que as tarefas são obtidas a partir da base de dados e estão visíveis na IU.
Preparar o código da app Web para a migração para o Cloud Run
Esta secção detalha os passos que tem de concluir para preparar a sua app Web para a implementação no Cloud Run.
Crie e publique o seu contentor Docker no Container Registry
Precisa de uma imagem Docker para criar o contentor da app para que possa ser executado no Cloud Run. Pode criar o contentor manualmente ou através de Buildpacks.
Crie o contentor manualmente
No Cloud Shell, crie um Dockerfile no diretório criado pela clonagem do repositório para este 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 o contentor 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 guardar o nome da imagem do Docker que criou:
export IMAGE_NAME="gcr.io/PROJECT_ID/APP_NAME:1"
Crie um contentor com Buildpacks
No Cloud Shell, instale a CLI pack.
Defina a CLI pack para usar o criador do Heroku por predefinição:
pack config default-builder heroku/buildpacks:22
Crie uma variável de ambiente para guardar o nome da imagem do Docker:
export IMAGE_NAME=gcr.io/PROJECT_ID/APP_NAME:1
Crie a imagem com o comando
pack
e envie ou publique a imagem no Container Registry:pack build --publish $IMAGE_NAME
Crie uma instância do Cloud SQL para PostgreSQL
Cria uma instância do Cloud SQL para PostgreSQL para servir como back-end para a app Web. Neste tutorial, o PostgreSQL é mais adequado como a app de exemplo implementada no Heroku, que usa uma base de dados Postgres como back-end. Para efeitos desta app, a migração para o Cloud SQL para PostgreSQL a partir de um serviço Postgres gerido não requer alterações ao esquema.
Prepare a 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_NAME
para armazenar o nome da instância da base de dados que criar no passo seguinte:export CLOUDSQL_DB_NAME=tasks-db
Crie a base 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 inicialização da instância pode demorar alguns minutos.
Defina uma palavra-passe para o utilizador do Postgres:
gcloud sql users set-password postgres \ --instance=$CLOUDSQL_DB_NAME \ --password=POSTGRES_PASSWORD
Substitua
POSTGRES_PASSWORD
pela palavra-passe que quer usar para a base de dados do Postgres.
Importe dados para o Cloud SQL a partir do Heroku Postgres
Existem vários padrões de migração que pode usar para migrar dados para o Cloud SQL. Geralmente, a melhor abordagem que requer pouco ou nenhum tempo de inatividade é configurar o Cloud SQL como uma réplica da base de dados que está a ser migrada e tornar o Cloud SQL a instância principal após a migração. O Heroku Postgres não suporta réplicas externas (seguidores). Por isso, neste tutorial, usa ferramentas de código aberto para migrar o esquema da app.
Para a app Tasks neste tutorial, usa o utilitário pg_dump para exportar dados do Heroku Postgres para um contentor do Cloud Storage e, em seguida, importá-los para o Cloud SQL. Esta utilidade pode transferir dados entre versões homogéneas ou quando a versão da base de dados de destino é mais recente do que a da base de dados de origem.
No Cloud Shell, obtenha as credenciais da base de dados para a sua base de dados Heroku Postgres associada à app de exemplo. Precisa destas credenciais no passo seguinte.
heroku pg:credentials:url
Este comando devolve a string de informações de ligação e o URL de ligação para a sua aplicação. A string de informações de ligação tem o seguinte formato:
"dbname=DATABASE_NAME host=FQDN port=5432 user=USER_NAME password=PASSWORD_STRING sslmode=require"
Precisa dos valores apresentados na string de ligação no passo seguinte.
Para ver um exemplo de um valor FQDN (nome de domínio totalmente qualificado) numa string de informações de ligação, consulte a documentação do Heroku.
Defina variáveis de ambiente para conter valores do Heroku que usa nos passos 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 o seguinte:
DATABASE_NAME
: o nome da base de dados apresentado na string de informações.FQDN
: o FQDN apresentado na string de informações.USER_NAME
: o nome do utilizador apresentado na string de informações.PASSWORD_STRING
: a string da palavra-passe apresentada na string de informações.
Crie uma cópia de segurança em formato SQL da sua base de dados 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 armazenar o nome do seu contentor do Cloud Storage:
export PG_BACKUP_BUCKET=gs://PROJECT_ID-pg-backup-bucket
Crie um contentor do Cloud Storage:
gcloud storage buckets create $PG_BACKUP_BUCKET \ --location=us-central1 \ --public-access-prevention \ --uniform-bucket-level-access
Carregue o ficheiro SQL para este contentor:
gcloud storage cp herokudump.sql $PG_BACKUP_BUCKET/herokudump.sql
Autorize a sua instância do Cloud SQL com as funções necessárias para importar o ficheiro SQL do contentor 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 ficheiro 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 lhe for pedido
do you want to continue (y/n)
, introduza "y".
Como o Cloud Run acede à base de dados do Cloud SQL
Tal como a app Web implementada no Heroku tem de se ligar à instância gerida do Heroku Postgres, o Cloud Run requer acesso ao Cloud SQL para poder ler e escrever dados.
O Cloud Run comunica com o Cloud SQL através do proxy do Cloud SQL que é ativado e configurado automaticamente quando implementa o contentor no Cloud Run. A base de dados não precisa de ter endereços IP externos aprovados porque toda a comunicação que recebe é do proxy através de TCP seguro.
O seu código tem de invocar operações de base de dados (como obter dados da base de dados ou escrever nela) invocando o proxy através de um socket UNIX.
Uma vez que esta app Web está escrita em Node.js, usa a biblioteca pg-connection-string
para analisar um URL da base de dados e criar um objeto config
. A vantagem desta abordagem é que torna a ligação à base de dados de back-end integrada no Heroku e no Cloud Run.
No passo seguinte, transmite o URL da base de dados como uma variável de ambiente quando implementa a app Web.
Implemente a app de exemplo no Cloud Run
No Cloud Shell, configure o acesso VPC sem servidor para permitir o tráfego privado do Cloud Run para o Cloud 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 de ligação da instância do Cloud SQL que criou:
export DB_CONN_NAME=$(gcloud sql instances describe $CLOUDSQL_DB_NAME --format='value(connectionName)')
Crie uma variável de ambiente denominada
DATABASE_URL
para conter a string de ligação para ligar ao proxy do Cloud SQL através de 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 uma função do IAM para ligar à base 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
Implemente a app 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 associa o seu contentor do Cloud Run à instância da base de dados do Cloud SQL que criou. O comando define uma variável de ambiente para o Cloud Run apontar para a string
DATABASE_URL
que criou no passo anterior.
Testar a aplicação
No Cloud Shell, obtenha o URL no qual o Cloud Run serve tráfego:
gcloud run services list
Também pode rever o serviço do Cloud Run na Google Cloud consola.
Certifique-se de que a sua app Web está a aceitar pedidos HTTP navegando para o URL do serviço do Cloud Run.
O Cloud Run cria ou inicia um novo contentor quando um pedido HTTP é enviado para o ponto final de publicação e se ainda não estiver a ser executado um contentor. Isto significa que o pedido que faz com que um novo contentor seja iniciado pode demorar um pouco mais a ser processado. Tendo em conta esse tempo adicional, considere o número de pedidos simultâneos que a sua app pode suportar e quaisquer requisitos de memória específicos que possa ter.
Para esta app, usa as predefinições de simultaneidade, que permitem que um serviço do Cloud Run processe 80 pedidos em simultâneo a partir de um único contentor.