Migrazione delle applicazioni Node.js da Heroku a Cloud Run

Questo tutorial descrive come eseguire la migrazione delle app web Node.js in esecuzione su Heroku su Cloud Run su Google Cloud. Questo tutorial è destinato ad architetti e proprietari di prodotti che vogliono eseguire la migrazione delle proprie app da Heroku ai servizi gestiti su Google Cloud.

Cloud Run è una piattaforma di computing gestita che consente di eseguire container stateless richiamabili tramite richieste HTTP. Si basa su Knative open source, che consente la portabilità su più piattaforme e supporta i flussi di lavoro e gli standard dei container per la distribuzione continua. La piattaforma Cloud Run è perfettamente integrata con la suite di prodotti Google Cloud e ti consente di progettare e sviluppare più facilmente app portatili, scalabili e resilienti.

In questo tutorial, imparerai come eseguire la migrazione di un'app a Google Cloud, scritta in Node.js e che utilizza Heroku Postgres come servizio di supporto su Heroku. L'app web è containerizzata e ospitata in Cloud Run. Inoltre, utilizza Cloud SQL per PostgreSQL come livello di persistenza.

Nel tutorial puoi utilizzare una semplice app chiamata Tasks per visualizzare e creare attività. Queste attività sono archiviate in Heroku Postgres nell'attuale deployment dell'app su Heroku.

Questo tutorial presuppone che tu conosca le funzionalità di base di Heroku e che tu possieda un account Heroku (o abbia accesso a uno di questi). Presupponendo anche la tua familiarità con Cloud Run, Cloud SQL, Docker e Node.js.

Obiettivi

  • Crea un'immagine Docker per eseguire il deployment dell'app in Cloud Run.
  • Crea un'istanza Cloud SQL per PostgreSQL da utilizzare come backend dopo la migrazione in Google Cloud.
  • Esamina il codice Node.js per capire come Cloud Run si connette a Cloud SQL e per vedere le modifiche al codice (se presenti) necessarie per eseguire la migrazione a Cloud Run da Heroku.
  • Esegui la migrazione dei dati da Heroku Postgres a Cloud SQL per PostgreSQL.
  • Esegui il deployment dell'app su Cloud Run.
  • Testare l'app di cui hai eseguito il deployment.

Costi

Questo tutorial utilizza i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il Calcolatore prezzi. I nuovi utenti di Google Cloud possono beneficiare di una prova gratuita.

È possibile che ti vengano addebitati dei costi per le risorse che utilizzi su Heroku.

Prima di iniziare

  1. Accedi al tuo account Google Cloud. Se non conosci Google Cloud, crea un account per valutare le prestazioni dei nostri prodotti in scenari reali. I nuovi clienti ricevono anche 300 $di crediti gratuiti per l'esecuzione, il test e il deployment dei carichi di lavoro.
  2. Nella pagina del selettore dei progetti in Google Cloud Console, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  3. Assicurati che la fatturazione sia attivata per il tuo progetto Cloud. Scopri come verificare se la fatturazione è abilitata su un progetto.

  4. Abilita le API Cloud SQL and Cloud Run .

    Abilita le API

  5. Nella pagina del selettore dei progetti in Google Cloud Console, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  6. Assicurati che la fatturazione sia attivata per il tuo progetto Cloud. Scopri come verificare se la fatturazione è abilitata su un progetto.

  7. Abilita le API Cloud SQL and Cloud Run .

    Abilita le API

Configurazione dell'ambiente

  1. Apri Cloud Shell.

    APRI Cloud Shell

  2. In Cloud Shell, assegna le impostazioni predefinite ai valori utilizzati in tutto il tutorial, ad esempio area geografica e zona. In questo tutorial, utilizzi us-central1 come area geografica predefinita e us-central1-a come zona predefinita.

    gcloud config set compute/region us-central1
    gcloud config set compute/zone us-central1-a
    
  3. Configura l'interfaccia a riga di comando di Google Cloud per utilizzare us-central1 come area geografica predefinita per Cloud Run:

    gcloud config set run/region us-central1
    
  4. Crea una variabile di ambiente in cui inserire un nome di app predefinito per questo tutorial:

    export APP_NAME=tasks-web-app
    

Architettura

Le figure che seguono descrivono l'architettura dell'app web su Heroku (così com'è) e il layout architetturale su Google Cloud (che creerai).

Architettura così com'è su Heroku.
Figura 1. Architettura così com'è su Heroku

L'app Tasks attualmente distribuita in Heroku consiste in uno o più dino web. I dino web sono in grado di ricevere e rispondere al traffico HTTP, a differenza dei dino worker che sono più adatti per i job in background e per le attività a tempo. L'app pubblica una pagina indice che mostra le attività memorizzate in un database Postgres tramite la libreria dei modelli di Mustache per Node.js.

Puoi accedere all'app tramite un URL HTTPS. Un percorso /tasks in corrispondenza dell'URL ti consente di creare nuove attività.

Architettura così com'è su Heroku.
Figura 2. Architettura che crei su Google Cloud

Su Google Cloud, Cloud Run è utilizzato come piattaforma serverless per eseguire il deployment dell'app Tasks. Cloud Run è progettato per eseguire container stateless basati su richiesta. È la soluzione ideale quando il servizio gestito deve supportare le app containerizzate che scalano automaticamente e scalano a zero quando non gestiscono il traffico.

Mappatura dei componenti utilizzati in Heroku a Google Cloud

La tabella seguente mappa i componenti della piattaforma Heroku a Google Cloud. Questa mappatura ti aiuta a tradurre l'architettura descritta in questo tutorial da Heroku a Google Cloud.

Componente Piattaforma Heroku Google Cloud
Container Dynos: Heroku utilizza il modello del container per creare e scalare app per Heroku. Questi container Linux sono chiamati dynos e possono scalare fino a un numero da te specificato per supportare le esigenze di risorse per l'app Heroku. Puoi scegliere tra un'ampia gamma di tipi di dino in base ai requisiti di memoria e CPU dell'app. Contenitori Cloud Run: Google Cloud supporta l'esecuzione di carichi di lavoro containerizzati in container stateless che possono essere eseguiti in un ambiente completamente gestito o in cluster Google Kubernetes Engine (GKE).
App web App hero: i dyno sono gli elementi costitutivi delle app Heroku. In genere le app sono composte da uno o più tipi di dino, di solito una combinazione di dino web e worker. Servizio Cloud Run: un'app web può essere modellata come servizio Cloud Run. Ogni servizio riceve il proprio endpoint HTTPS e può fare lo scale up o scale down da 0 a N, in base al traffico verso l'endpoint di servizio.
Database Heroku Postgres è un database as a Service (DaaS) basato su Heroku basato su PostgreSQL. Cloud SQL è un servizio di database gestito per i database relazionali su Google Cloud. Offre due varianti: PostgreSQL e MySQL.

Deployment dell'app web Tasks di esempio in Heroku

Le sezioni seguenti mostrano come configurare l'interfaccia a riga di comando (CLI) per Heroku, clonare il repository di codice sorgente di GitHub ed eseguire il deployment dell'app in Heroku.

Configurare l'interfaccia a riga di comando per Heroku

  1. In Cloud Shell, installa l'interfaccia a riga di comando di Heroku

  2. Accedi al tuo account Heroku:

    heroku login --interactive
    

Clona il repository di codice sorgente

  1. In Cloud Shell, clona il repository GitHub di esempio di Tasks:

    git clone https://github.com/GoogleCloudPlatform/migrate-webapp-heroku-to-cloudrun-node.git
    
  2. Modifica le directory nella directory creata clonando il repository:

    cd migrate-webapp-heroku-to-cloudrun-node
    

    La directory contiene i seguenti file:

    • Uno script Node.js chiamato index.js con il codice per i percorsi pubblicati dall'app web.
    • File package.json e package-lock.json che descrivono le dipendenze dell'app web. Devi installare queste dipendenze per far sì che l'app venga eseguita.
    • Un file Procfile che specifica il comando che l'app esegue all'avvio. Crei un file Procfile per eseguire il deployment dell'app su Heroku.
    • Una directory views con i contenuti HTML pubblicati dall'app web sul percorso "/".
    • Un file .gitignore.

Eseguire il deployment di un'app su Heroku

  1. In Cloud Shell, crea un'app Heroku:

    heroku create
    
  2. Aggiungi il componente aggiuntivo Heroku Postgres per eseguire il provisioning di un database PostgreSQL:

    heroku addons:create heroku-postgresql:hobby-dev
    
  3. Assicurati che il componente aggiuntivo sia stato aggiunto correttamente:

    heroku addons
    

    Se il componente aggiuntivo Postgres è stato aggiunto correttamente, viene visualizzato un messaggio simile al seguente:

    Owning-App               Add-on                      Plan                          Price    State
    ----------------------   ------------------------    --------------------------    -----    -----
    sample-nodejs-todo-app   postgresql-adjacent-95585   heroku-postgresql:hobby-dev   free     created
    
  4. Esegui il deployment dell'app su Heroku:

    git push heroku master
    
  5. Recupera l'URI Heroku Postgres dalla console Heroku. In alternativa, esegui il comando seguente per recuperare l'URI:

    heroku config
    

    Prendi nota dell'URI recuperato. Ti servirà nel passaggio successivo.

  6. Esegui un container Docker. Sostituisci database-uri con l'URI Heroku Postgres che hai annotato nel passaggio precedente.

    docker run -it --rm postgres psql "database-uri"
    
  7. Nel container Docker, crea la tabella TASKS utilizzando il seguente comando:

    CREATE TABLE TASKS
    (DESCRIPTION TEXT NOT NULL);
    
  8. Esci dal contenitore:

    exit
    
  9. In Cloud Shell, recupera l'URL dell'app Heroku:

    heroku info
    
  10. Apri l'URL dell'app in una finestra del browser. L'app ha il seguente aspetto (anche se la tua versione non conterrà le attività elencate):

    App Da fare nel browser web.

  11. Crea attività di esempio nella tua app dal browser. Assicurati che le attività siano recuperate dal database e siano visibili nella UI.

Preparazione del codice dell'app web per la migrazione a Cloud Run

Questa sezione descrive i passaggi da completare per preparare la tua app web al deployment in Cloud Run.

Crea e pubblica il tuo container Docker in Container Registry

Hai bisogno di un'immagine Docker per creare il container dell'app in modo che possa essere eseguita in Cloud Run. Puoi creare il container manualmente oppure utilizzando Buildpack.

Crea manualmente il container

  1. In Cloud Shell, crea un Dockerfile nella directory creata clonando il repository per questo 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. Crea il tuo container con Cloud Build e pubblica l'immagine in Container Registry:

    gcloud builds submit --tag gcr.io/${DEVSHELL_PROJECT_ID}/$APP_NAME:1
    
  3. Crea una variabile di ambiente in cui inserire il nome dell'immagine Docker creata:

    export IMAGE_NAME="gcr.io/${DEVSHELL_PROJECT_ID}/$APP_NAME:1"
    

Crea container con Buildpacks

  1. In Cloud Shell, installa l'interfaccia a riga di comando del pacchetto:

    wget https://github.com/buildpack/pack/releases/download/v0.2.1/pack-v0.2.1-linux.tgz
    tar xvf pack-v0.2.1-linux.tgz
    rm pack-v0.2.1-linux.tgz
    sudo mv pack /usr/local/bin/
    
  2. Imposta l'interfaccia a riga di comando del pacchetto per utilizzare il generatore di Heroku per impostazione predefinita:

    pack set-default-builder heroku/buildpacks
    
  3. Crea una variabile di ambiente in cui inserire il nome dell'immagine Docker:

    export IMAGE_NAME=gcr.io/${DEVSHELL_PROJECT_ID}/$APP_NAME:1
    
  4. Crea l'immagine utilizzando il comando pack ed esegui il push o pubblica l'immagine in Container Registry:

    pack build --publish $IMAGE_NAME
    

Creare un'istanza Cloud SQL per PostgreSQL

Crea un'istanza Cloud SQL per PostgreSQL da utilizzare come backend per l'app web. In questo tutorial, PostgreSQL è la più adatta come app di esempio di cui è stato eseguito il deployment su Heroku, che utilizza un database Postgres come backend. Ai fini di questa app, la migrazione a Cloud SQL per PostgreSQL da un servizio Postgres gestito non richiede alcuna modifica allo schema.

gcloud

  1. Crea una variabile di ambiente chiamata CLOUDSQL_DB_NAME per contenere il nome dell'istanza di database che crei nel passaggio successivo:

    export CLOUDSQL_DB_NAME=tasks-db
    
  2. Crea il database:

    gcloud sql instances create $CLOUDSQL_DB_NAME  \
        --cpu=1 \
        --memory=4352Mib \
        --database-version=POSTGRES_9_6 \
        --region=us-central1
    

    L'inizializzazione dell'istanza potrebbe richiedere alcuni minuti.

  3. Imposta una password per l'utente Postgres:

    gcloud sql users set-password postgres \
        --instance=$CLOUDSQL_DB_NAME  \
        --password=postgres-password
    

console

  1. Nella console, vai alla pagina Istanze Cloud SQL:

    VAI ALLA PAGINA ISTANZE SQL CLOUD

  2. Fai clic su Crea istanza.

  3. Seleziona PostgreSQL e fai clic su Avanti.

  4. Inserisci tasks-db come nome dell'istanza.

  5. Crea una password per l'utente postgres.

  6. Per l'area geografica, seleziona us-central1.

  7. Non modificare altri valori predefiniti.

Importare dati in Cloud SQL da Heroku Postgres

Esistono diversi pattern di migrazione che puoi utilizzare per eseguire la migrazione dei dati in Cloud SQL. In genere, l'approccio migliore che richiede tempi di inattività minimi o nulli è configurare Cloud SQL come replica del database di cui eseguire la migrazione e rendere Cloud SQL l'istanza principale dopo la migrazione. Heroku Postgres non supporta le repliche esterne (seguitori), quindi in questo tutorial utilizzerai gli strumenti open source per eseguire la migrazione dello schema dell'app.

Per l'app Tasks in questo tutorial, utilizzerai l'utilità pg_dump per esportare i dati da Heroku Postgres a un bucket Cloud Storage e poi importarli in Cloud SQL. Questa utilità può trasferire i dati tra versioni omogenee o quando la versione del database di destinazione è più recente del database di origine.

  1. In Cloud Shell, ottieni le credenziali del database per il database Heroku Postgres a cui è collegata l'app di esempio. Avrai bisogno di queste credenziali nel passaggio successivo.

    heroku pg:credentials:url
    

    Questo comando restituisce la stringa di informazioni sulla connessione e l'URL di connessione per l'applicazione. La stringa contenente le informazioni sulla connessione ha il seguente formato:

    "dbname=database-name host=FQDN port=5432 user=user-name password=password-string sslmode=require"
    

    Per un esempio di valore FQDN (nome di dominio completo) in una stringa di informazioni sulla connessione, consulta la documentazione di Heroku.

  2. Imposta le variabili di ambiente in cui inserire i valori Heroku da utilizzare nei passaggi successivi. Sostituisci i segnaposto FQDN, user-name, password-string e database-name con i rispettivi valori corrispondenti nella stringa delle informazioni di connessione.

    export HEROKU_PG_HOST=FQDN
    export HEROKU_PG_USER=user-name
    export HEROKU_PG_PASSWORD=password-string
    export HEROKU_PG_DBNAME=database-name
    
  3. Crea un backup in formato SQL del tuo database 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. Crea una variabile di ambiente in cui inserire il nome del tuo bucket Cloud Storage. Il nome deve essere univoco. La variabile di ambiente deve avere il formato gs://bucket-name.

    export PG_BACKUP_BUCKET=gs://bucket-name
    
  5. Crea un bucket Cloud Storage utilizzando lo strumento a riga di comando gsutil:

    gsutil mb -c regional -l us-central1 $PG_BACKUP_BUCKET
    
  6. Carica il file SQL in questo bucket:

    gsutil cp herokudump.sql $PG_BACKUP_BUCKET
    
  7. Nella console, vai alla pagina Istanze Cloud SQL:

    VAI ALLA PAGINA ISTANZE SQL CLOUD

  8. Seleziona l'istanza per aprire la relativa pagina Dettagli istanza.

  9. Nella barra dei pulsanti, fai clic su Importa.

    Importazione del file di dump SQL da Cloud Storage.

  10. Per il file Cloud Storage, inserisci il percorso del file di dump SQL che hai caricato in Cloud Storage.

  11. Per Formato importazione, seleziona SQL.

  12. Per Database, seleziona postgres.

  13. Espandi Opzioni avanzate e, per Utente, seleziona postgres.

  14. Fai clic su Importa per avviare l'importazione.

  15. Per verificare che l'importazione sia riuscita, controlla gli aggiornamenti nella scheda Operazioni dell'istanza.

In che modo Cloud Run accede al database Cloud SQL

Così come l'app web di cui è stato eseguito il deployment in Heroku deve connettersi all'istanza gestita di Heroku Postgres, Cloud Run richiede l'accesso a Cloud SQL per poter leggere e scrivere dati.

Cloud Run comunica con Cloud SQL utilizzando il proxy Cloud SQL che viene attivato e configurato automaticamente quando esegui il deployment del container su Cloud Run. Non è necessario approvare gli indirizzi IP esterni del database perché tutte le comunicazioni che riceve provengono dal proxy tramite TCP sicuro.

Il codice deve richiamare le operazioni di database (come il recupero dei dati dal database o la scrittura) richiamando il proxy su un socket UNIX.

Poiché questa app web è scritta in Node.js, utilizzi la libreria pg-connection-string per analizzare un URL del database e creare un oggetto config. Il vantaggio di questo approccio è che semplifica la connessione al database di backend tra Heroku e Cloud Run.

Nel passaggio successivo, trasmetti l'URL del database come variabile di ambiente quando esegui il deployment dell'app web.

Esegui il deployment dell'app di esempio in Cloud Run

  1. In Cloud Shell, crea una variabile di ambiente che contenga il nome della connessione dell'istanza Cloud SQL che hai creato:

    export DB_CONN_NAME=$(gcloud sql instances describe $CLOUDSQL_DB_NAME --format='value(connectionName)')
    
  2. Crea una variabile di ambiente chiamata DATABASE_URL per contenere la stringa di connessione per connetterti al proxy Cloud SQL su una porta UNIX. Sostituisci your-db-password con la password creata per l'istanza di database.

    export DATABASE_URL="socket:/cloudsql/${DB_CONN_NAME}?db=postgres&user=postgres&password=your-db-password"
    
  3. Esegui il deployment dell'app web in Cloud Run.

    gcloud run deploy tasksapp-$DEVSHELL_PROJECT_ID \
        --image=$IMAGE_NAME \
        --set-env-vars=DATABASE_URL=$DATABASE_URL \
        --add-cloudsql-instances $DB_CONN_NAME \
        --allow-unauthenticated
    

    Il comando precedente collega anche il container Cloud Run all'istanza del database Cloud SQL che hai creato. Il comando imposta una variabile di ambiente per Cloud Run in modo che rimandi alla stringa DATABASE_URL che hai creato nel passaggio precedente.

Testare l'applicazione

  1. In Cloud Shell, recupera l'URL a cui Cloud Run gestisce il traffico:

    gcloud run services list
    

    Puoi anche esaminare il servizio Cloud Run nella console.

  2. Assicurati che l'applicazione web accetti le richieste HTTP andando all'URL del servizio Cloud Run.

Cloud Run crea, o crea, un nuovo container quando viene inviata una richiesta HTTP all'endpoint di gestione e se un container non è già in esecuzione. Ciò significa che la richiesta che fa sì che un nuovo container venga avviato potrebbe richiedere più tempo per essere pubblicata. Considerato questo tempo aggiuntivo, tieni presente il numero di richieste in parallelo che la tua app può supportare e gli eventuali requisiti specifici per la memoria.

Per questa app utilizzi le impostazioni di contemporaneità predefinite, che consentono a un servizio Cloud Run di pubblicare 80 richieste contemporaneamente da un singolo container.

Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial. Potrebbe essere utile anche eliminare le risorse create in Heroku per questo tutorial.

Elimina il progetto Google Cloud

  1. Nella console, vai alla pagina Gestisci risorse.

    Vai a Gestisci risorse

  2. Nell'elenco dei progetti, seleziona il progetto da eliminare, quindi fai clic su Elimina.
  3. Nella finestra di dialogo, digita l'ID del progetto e fai clic su Chiudi per eliminare il progetto.

Elimina l'app Heroku

Per eliminare l'app di esempio di cui hai eseguito il deployment in Heroku e nel componente aggiuntivo PostgreSQL associato, esegui il comando seguente:

heroku apps:destroy -a $APP_NAME

Passaggi successivi