Migre apps Node.js do Heroku para o Cloud Run

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

  1. Abra o Cloud Shell.

    ABRIR Cloud Shell

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

Arquitetura tal como está no Heroku.
Figura 1. Arquitetura tal como está no Heroku

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.

Arquitetura tal como está no Heroku.
Figura 2. Arquitetura na qual cria Google Cloud

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.

  1. 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.

  2. No Cloud Shell, instale a CLI do Heroku

  3. 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

  1. 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
    
  2. 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 e package-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 ficheiro Procfile 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.

Implemente uma app no Heroku

  1. No Cloud Shell, crie uma app Heroku:

    heroku create
    

    Tome nota do nome criado para a app. Precisa deste valor no passo seguinte.

  2. 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 comando heroku create.

  3. Adicione o suplemento Heroku Postgres para aprovisionar uma base de dados PostgreSQL:

    heroku addons:create heroku-postgresql:mini
    
  4. 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
    
  5. Implemente a app no Heroku:

    git push heroku master
    
  6. 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.

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

  8. No contentor do Docker, crie a tabela TASKS com o seguinte comando:

    CREATE TABLE TASKS
    (DESCRIPTION TEXT NOT NULL);
    
  9. Saia do contentor:

    exit
    
  10. No Cloud Shell, execute o seguinte comando para obter o URL da Web da sua app Heroku:

    heroku info
    
  11. 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):

    App de tarefas no navegador de Internet.

  12. 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

  1. 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
    
  2. 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
    
  3. 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

  1. No Cloud Shell, instale a CLI pack.

  2. Defina a CLI pack para usar o criador do Heroku por predefinição:

    pack config default-builder heroku/buildpacks:22
    
  3. Crie uma variável de ambiente para guardar o nome da imagem do Docker:

    export IMAGE_NAME=gcr.io/PROJECT_ID/APP_NAME:1
    
  4. 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.

  1. 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
    
  2. 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
    
  3. 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.

  4. 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.

  1. 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.

  2. 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.
  3. 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
    
  4. 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
    
  5. Crie um contentor do Cloud Storage:

    gcloud storage buckets create $PG_BACKUP_BUCKET \
      --location=us-central1 \
      --public-access-prevention \
      --uniform-bucket-level-access
    
  6. Carregue o ficheiro SQL para este contentor:

    gcloud storage cp herokudump.sql $PG_BACKUP_BUCKET/herokudump.sql
    
  7. 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
    
  8. 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

  1. 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
    
  2. 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)')
    
  3. 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"
    
  4. 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
    
  5. 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

  1. 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.

  2. 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.