Attivare le funzioni Cloud Run utilizzando Cloud Tasks


Questo tutorial mostra come utilizzare Cloud Tasks all'interno di un'applicazione App Engine per attivare una funzione Cloud Run e inviare un'email programmata.

Obiettivi

  • Comprendi il codice in ogni componente.
  • Crea un account SendGrid.
  • Scarica il codice sorgente.
  • Esegui il deployment di una funzione Cloud Run per ricevere richieste Cloud Tasks e inviare un'email tramite l'API SendGrid.
  • Crea una coda di Cloud Tasks.
  • Crea un account di servizio per autenticare le richieste di Cloud Tasks.
  • Esegui il deployment del codice client che consente a un utente di inviare un'email.

Costi

Cloud Tasks, le funzioni Cloud Run e App Engine hanno un livello gratuito, pertanto, a condizione che tu esegua il tutorial nel livello gratuito dei prodotti in questione, non dovrebbero esserci costi aggiuntivi. Per ulteriori informazioni, vedi Prezzi.

Prima di iniziare

  1. Seleziona o crea un Google Cloud progetto.

    Vai alla pagina App Engine

  2. Inizializza un'applicazione App Engine nel tuo progetto:

    1. Nella pagina Ti diamo il benvenuto in App Engine, fai clic su Crea applicazione.

    2. Seleziona una regione per l'applicazione. Questa posizione fungerà da parametro LOCATION_ID per le richieste di Cloud Tasks, quindi prendine nota. Tieni presente che due località, chiamate europe-west e us-central nei comandi di App Engine, si chiamano rispettivamente europe-west1 e us-central1 nei comandi di Cloud Tasks.

    3. Seleziona Node.js per il linguaggio e Standard per l'ambiente.

    4. Se viene visualizzata la finestra popup Attiva la fatturazione, seleziona il tuo account di fatturazione. Se al momento non disponi di un account di fatturazione, fai clic su Crea account di fatturazione e segui la procedura guidata.

    5. Nella pagina Inizia, fai clic su Avanti. Ti occuperai di questo problema in un secondo momento.

  3. Abilita le API Cloud Run e Cloud Tasks.

    Attiva le API

  4. Installa e inizializza la gcloud CLI.

Nozioni di base sul codice

Questa sezione illustra il codice dell'app e spiega come funziona.

Creazione dell'attività

La pagina di indice viene pubblicata utilizzando gli handler in app.yaml. Le variabili necessarie per la creazione delle attività vengono passate come variabili di ambiente.

runtime: nodejs16

env_variables:
  QUEUE_NAME: "my-queue"
  QUEUE_LOCATION: "us-central1"
  FUNCTION_URL: "https://<region>-<project_id>.cloudfunctions.net/sendEmail"
  SERVICE_ACCOUNT_EMAIL: "<member>@<project_id>.iam.gserviceaccount.com"

# Handlers for serving the index page.
handlers:
  - url: /static
    static_dir: static
  - url: /
    static_files: index.html
    upload: index.html

Questo codice crea l'endpoint /send-email. Questo endpoint gestisce l'invio dei moduli dalla pagina di indice e passa i dati al codice di creazione dell'attività.

app.post('/send-email', (req, res) => {
  // Set the task payload to the form submission.
  const {to_name, from_name, to_email, date} = req.body;
  const payload = {to_name, from_name, to_email};

  createHttpTaskWithToken(
    process.env.GOOGLE_CLOUD_PROJECT,
    QUEUE_NAME,
    QUEUE_LOCATION,
    FUNCTION_URL,
    SERVICE_ACCOUNT_EMAIL,
    payload,
    date
  );

  res.status(202).send('📫 Your postcard is in the mail! 💌');
});

Questo codice crea effettivamente l'attività e la invia alla coda di Cloud Tasks. Il codice crea l'attività:

  • Specifica il tipo di target come HTTP Request.

  • Specifica il HTTP method da utilizzare e il URL del target.

  • Imposta l'intestazione Content-Type su application/json in modo che le applicazioni a valle possano analizzare il payload strutturato.

  • Aggiunta di un'email dell'account di servizio in modo che Cloud Tasks possa fornire le credenziali al target della richiesta, che richiede l'autenticazione. L'account di servizio viene creato separatamente.

  • Verificare che la data inserita dall'utente rientri nel periodo massimo di 30 giorni e aggiungerla alla richiesta come campo scheduleTime.

const MAX_SCHEDULE_LIMIT = 30 * 60 * 60 * 24; // Represents 30 days in seconds.

const createHttpTaskWithToken = async function (
  project = 'my-project-id', // Your GCP Project id
  queue = 'my-queue', // Name of your Queue
  location = 'us-central1', // The GCP region of your queue
  url = 'https://example.com/taskhandler', // The full url path that the request will be sent to
  email = '<member>@<project-id>.iam.gserviceaccount.com', // Cloud IAM service account
  payload = 'Hello, World!', // The task HTTP request body
  date = new Date() // Intended date to schedule task
) {
  // Imports the Google Cloud Tasks library.
  const {v2beta3} = require('@google-cloud/tasks');

  // Instantiates a client.
  const client = new v2beta3.CloudTasksClient();

  // Construct the fully qualified queue name.
  const parent = client.queuePath(project, location, queue);

  // Convert message to buffer.
  const convertedPayload = JSON.stringify(payload);
  const body = Buffer.from(convertedPayload).toString('base64');

  const task = {
    httpRequest: {
      httpMethod: 'POST',
      url,
      oidcToken: {
        serviceAccountEmail: email,
        audience: url,
      },
      headers: {
        'Content-Type': 'application/json',
      },
      body,
    },
  };

  const convertedDate = new Date(date);
  const currentDate = new Date();

  // Schedule time can not be in the past.
  if (convertedDate < currentDate) {
    console.error('Scheduled date in the past.');
  } else if (convertedDate > currentDate) {
    const date_diff_in_seconds = (convertedDate - currentDate) / 1000;
    // Restrict schedule time to the 30 day maximum.
    if (date_diff_in_seconds > MAX_SCHEDULE_LIMIT) {
      console.error('Schedule time is over 30 day maximum.');
    }
    // Construct future date in Unix time.
    const date_in_seconds =
      Math.min(date_diff_in_seconds, MAX_SCHEDULE_LIMIT) + Date.now() / 1000;
    // Add schedule time to request in Unix time using Timestamp structure.
    // https://googleapis.dev/nodejs/tasks/latest/google.protobuf.html#.Timestamp
    task.scheduleTime = {
      seconds: date_in_seconds,
    };
  }

  try {
    // Send create task request.
    const [response] = await client.createTask({parent, task});
    console.log(`Created task ${response.name}`);
    return response.name;
  } catch (error) {
    // Construct error for Stackdriver Error Reporting
    console.error(Error(error.message));
  }
};

module.exports = createHttpTaskWithToken;

Creazione dell'email

Questo codice crea la funzione Cloud Run che è la destinazione della richiesta Cloud Tasks. Utilizza il corpo della richiesta per creare un'email e inviarla tramite l'API SendGrid.

const sendgrid = require('@sendgrid/mail');

/**
 * Responds to an HTTP request from Cloud Tasks and sends an email using data
 * from the request body.
 *
 * @param {object} req Cloud Function request context.
 * @param {object} req.body The request payload.
 * @param {string} req.body.to_email Email address of the recipient.
 * @param {string} req.body.to_name Name of the recipient.
 * @param {string} req.body.from_name Name of the sender.
 * @param {object} res Cloud Function response context.
 */
exports.sendEmail = async (req, res) => {
  // Get the SendGrid API key from the environment variable.
  const key = process.env.SENDGRID_API_KEY;
  if (!key) {
    const error = new Error(
      'SENDGRID_API_KEY was not provided as environment variable.'
    );
    error.code = 401;
    throw error;
  }
  sendgrid.setApiKey(key);

  // Get the body from the Cloud Task request.
  const {to_email, to_name, from_name} = req.body;
  if (!to_email) {
    const error = new Error('Email address not provided.');
    error.code = 400;
    throw error;
  } else if (!to_name) {
    const error = new Error('Recipient name not provided.');
    error.code = 400;
    throw error;
  } else if (!from_name) {
    const error = new Error('Sender name not provided.');
    error.code = 400;
    throw error;
  }

  // Construct the email request.
  const msg = {
    to: to_email,
    from: 'postcard@example.com',
    subject: 'A Postcard Just for You!',
    html: postcardHTML(to_name, from_name),
  };

  try {
    await sendgrid.send(msg);
    // Send OK to Cloud Task queue to delete task.
    res.status(200).send('Postcard Sent!');
  } catch (error) {
    // Any status code other than 2xx or 503 will trigger the task to retry.
    res.status(error.code).send(error.message);
  }
};

Preparazione della richiesta

Configurazione di SendGrid

  1. Crea un account SendGrid.

  2. Crea una chiave API SendGrid:

    1. Accedi al tuo account SendGrid.

    2. Nel riquadro di navigazione a sinistra, apri Impostazioni e fai clic su Chiavi API.

    3. Fai clic su Crea chiave API e seleziona l'accesso limitato. Sotto l'intestazione Invio email, seleziona Accesso completo.

    4. Copia la chiave API quando viene visualizzata (la vedrai una sola volta, assicurati di incollarla da qualche parte per poterla utilizzare in un secondo momento).

Download del codice sorgente

  1. Clona il repository dell'app di esempio sulla tua macchina locale:

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
    
  2. Passa alla directory che contiene il codice di esempio:

    cd cloud-tasks/
    

Esegui il deployment della funzione Cloud Run

  1. Vai alla directory function/:

    cd function/
    
  2. Esegui il deployment della funzione:

    gcloud functions deploy sendEmail --runtime nodejs14 --trigger-http \
      --no-allow-unauthenticated \
      --set-env-vars SENDGRID_API_KEY=SENDGRID_API_KEY \

    Sostituisci SENDGRID_API_KEY con la tua chiave API.

    Questo comando utilizza i flag:

    • --trigger-http per specificare il tipo di trigger delle funzioni Cloud Run.

    • --no-allow-unauthenticated per specificare che l'invocazione della funzione richiede l'autenticazione.

    • --set-env-var per impostare le credenziali SendGrid

  3. Imposta il controllo dell'accesso per la funzione in modo da consentire solo agli utenti autenticati di accedere.

    1. Seleziona la funzione sendEmail nell'interfaccia utente di Cloud Run Functions.

    2. Se non vedi le informazioni sulle autorizzazioni per sendEmail, fai clic su MOSTRA RIQUADRO INFORMAZIONI nell'angolo in alto a destra.

    3. Fai clic sul pulsante Aggiungi entità qui sopra.

    4. Imposta Nuove entità su allAuthenticatedUsers.

    5. Imposta il ruolo.

      • Funzioni di prima generazione (1ª gen.): imposta il ruolo su Cloud Function Invoker
      • Funzioni di seconda generazione (2ª gen.): imposta il ruolo su Cloud Run Invoker
    6. Fai clic su SALVA.

Creazione di una coda di Cloud Tasks

  1. Crea una coda utilizzando il seguente comando gcloud:

    gcloud tasks queues create my-queue --location=LOCATION

    Sostituisci LOCATION con la posizione preferita per la coda, ad esempio us-west2. Se non specifichi la posizione, l'interfaccia a riga di comando gcloud sceglie quella predefinita.

  2. Verifica che la creazione sia andata a buon fine:

    gcloud tasks queues describe my-queue --location=LOCATION

    Sostituisci LOCATION con la posizione della coda.

Creazione di un account di servizio

La richiesta Cloud Tasks deve fornire le credenziali nell'intestazione Authorization per consentire alla funzione Cloud Run di autenticare la richiesta. Questo account di servizio consente a Cloud Tasks di creare e aggiungere un token OIDC a questo scopo.

  1. Nell'interfaccia utente degli account di servizio, fai clic su + CREA ACCOUNT DI SERVIZIO.

  2. Aggiungi un nome account di servizio(nome visualizzato facile da ricordare) e seleziona Crea.

  3. Imposta il Ruolo e fai clic su Continua.

    • Funzioni di prima generazione (1ª gen.): imposta il ruolo su Cloud Function Invoker
    • Funzioni di seconda generazione (2ª gen.): imposta il ruolo su Cloud Run Invoker
  4. Seleziona Fine.

Eseguire il deployment dell'endpoint e del creatore di attività in App Engine

  1. Vai alla directory app/:

    cd ../app/
    
  2. Aggiorna le variabili in app.yaml con i tuoi valori:

    env_variables:
      QUEUE_NAME: "my-queue"
      QUEUE_LOCATION: "us-central1"
      FUNCTION_URL: "https://<region>-<project_id>.cloudfunctions.net/sendEmail"
      SERVICE_ACCOUNT_EMAIL: "<member>@<project_id>.iam.gserviceaccount.com"

    Per trovare la posizione della coda, utilizza il seguente comando:

    gcloud tasks queues describe my-queue --location=LOCATION

    Sostituisci LOCATION con la posizione della coda.

    Per trovare l'URL della funzione, utilizza il seguente comando:

    gcloud functions describe sendEmail
  3. Esegui il deployment dell'applicazione nell'ambiente standard di App Engine utilizzando il seguente comando:

    gcloud app deploy
  4. Apri l'applicazione per inviare una cartolina come email:

    gcloud app browse

Esegui la pulizia

Al termine del tutorial, puoi eliminare le risorse che hai creato in modo che smettano di utilizzare la quota e di generare addebiti. Le seguenti sezioni descrivono come eliminare o disattivare queste risorse.

Eliminazione delle risorse

Puoi eliminare le risorse che hai creato su Google Cloud in modo che non occupino quota e non ti vengano addebitate in futuro. Le seguenti sezioni descrivono come eliminare o disattivare queste risorse.

Elimina la funzione Cloud Run

  1. Vai alla pagina Funzioni Cloud Run nella console Google Cloud.

    Vai alla pagina Funzioni Cloud Run.

  2. Fai clic sulle caselle di controllo accanto alle funzioni.

  3. Fai clic sul pulsante Elimina nella parte superiore della pagina e conferma l'eliminazione.

Elimina la coda di Cloud Tasks

  1. Apri la pagina Code di Cloud Tasks nella console.

    Vai alla pagina Code di Cloud Tasks

  2. Seleziona il nome della coda che vuoi eliminare e fai clic su Elimina coda.

  3. Conferma l'azione.

Elimina il progetto

Il modo più semplice per eliminare la fatturazione è eliminare il progetto che hai creato per il tutorial.

Per eliminare il progetto:

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Passaggi successivi