Acionar funções do Cloud Run usando o Cloud Tasks


Neste tutorial, mostramos como usar o Cloud Tasks em um aplicativo do App Engine para acionar uma função do Cloud Run e enviar um e-mail programado.

Objetivos

  • Entender o código em cada um dos componentes.
  • Crie uma conta do SendGrid.
  • Fazer o download do código-fonte.
  • Implante uma função do Cloud Run para receber solicitações do Cloud Tasks e enviar um e-mail usando a API SendGrid.
  • Criar uma fila do Cloud Tasks.
  • Criar uma conta de serviço para autenticar suas solicitações do Cloud Tasks.
  • Implantar o código do cliente que permite que um usuário envie um e-mail.

Custos

O Cloud Tasks, o Cloud Run e o App Engine têm um nível gratuito. Portanto, desde que você execute o tutorial dentro desse nível dos produtos determinados, isso não resultará em custos adicionais. Para mais informações, consulte Preços.

Antes de começar

  1. Selecione ou crie um projeto do Google Cloud.

    Acessar a página do App Engine

  2. Inicialize um aplicativo do App Engine no seu projeto:

    1. Na página Bem-vindo ao App Engine, clique em Criar aplicativo.

    2. Selecione uma região para o aplicativo. Esse local servirá como o parâmetro LOCATION_ID para suas solicitações do Cloud Tasks. Portanto, anote-o. Observe que dois locais, denominados europe-west e us-central nos comandos do App Engine, são chamados, respectivamente, europe-west1 e us-central1 nos comandos do Cloud Tasks.

    3. Selecione Node.js para a linguagem e Padrão para o ambiente.

    4. Se o pop-up Ativar faturamento for exibido, selecione sua conta de faturamento. Se você não tiver uma conta de faturamento atualmente, clique em Criar conta de faturamento e siga o assistente.

    5. Na página Primeiros passos, clique em Avançar. Você cuidará disso mais tarde.

  3. Ative as funções do Cloud Run e as APIs do Cloud Tasks.

    Ativar as APIs

  4. Instale e inicialize a gcloud CLI.

Noções básicas sobre o código

Esta seção analisa o código do aplicativo e explica como ele funciona.

Como criar a tarefa

A página de índice é veiculada usando gerenciadores no app.yaml. As variáveis necessárias para a criação de tarefas são passadas como variáveis de 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

Esse código cria o endpoint /send-email. Esse endpoint processa os envios de formulário da página de índice e passa esses dados para o código de criação de tarefas.

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! 💌');
});

Na verdade, esse código cria a tarefa e a envia para a fila do Cloud Tasks. O código cria a tarefa por:

  • Especificando o tipo de segmentação como HTTP Request.

  • Especificando o HTTP method a ser usado e o URL do destino.

  • Definir o cabeçalho Content-Type como application/json para que os aplicativos downstream possam analisar o payload estruturado.

  • Adicionar um e-mail da conta de serviço para que o Cloud Tasks possa fornecer credenciais à segmentação da solicitação, o que requer autenticação. A conta de serviço é criada separadamente.

  • Verifique se a entrada do usuário para data está dentro do máximo de 30 dias e adicione-a à solicitação como 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;

Como criar o e-mail

Esse código cria a função do Cloud Run que é o destino da solicitação do Cloud Tasks. Ele usa o corpo da solicitação para construir um e-mail e enviá-lo por meio da 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);
  }
};

Como preparar o aplicativo

Como configurar o SendGrid

  1. Crie uma conta do SendGrid.

  2. Crie uma chave de API do SendGrid:

    1. Faça login na sua conta do SendGrid.

    2. Na barra de navegação esquerda, abra Configurações e clique em Chaves de API.

    3. Clique em Criar chave de API e selecione acesso restrito. No cabeçalho Enviar e-mail, selecione Acesso total.

    4. Copie a chave de API quando ela for exibida (você verá essa opção apenas uma vez, cole-a em algum lugar para usá-la mais tarde).

Como fazer o download do código-fonte

  1. Clone o repositório do app de amostra na máquina local:

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
    
  2. Acesse o diretório que contém o exemplo de código:

    cd cloud-tasks/
    

Como implantar a função do Cloud Run

  1. Navegue até o diretório function/:

    cd function/
    
  2. Implante a função:

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

    Substitua SENDGRID_API_KEY pela sua chave de API.

    Este comando usa sinalizações:

    • --trigger-http para especificar o tipo de gatilho das funções do Cloud Run.

    • --no-allow-unauthenticated para especificar a invocação da função requer autenticação.

    • --set-env-var para definir suas credenciais do SendGrid

  3. Defina o controle de acesso da função para permitir apenas usuários autenticados.

    1. Selecione a função sendEmail na IU do Cloud Run functions.

    2. Se você não vir informações de permissões para sendEmail, clique em MOSTRAR PAINEL DE INFORMAÇÕES no canto superior direito.

    3. Clique no botão Adicionar principais acima.

    4. Defina Novos principais como allAuthenticatedUsers.

    5. Defina o Papel.

      • Funções de primeira geração (1a geração): defina o papel como Cloud Function Invoker
      • Funções de 2ª geração: defina o papel como Cloud Run Invoker.
    6. Clique em SALVAR.

Como criar uma fila do Cloud Tasks

  1. Crie uma fila usando o seguinte comando gcloud:

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

    Substitua LOCATION pela opção desejada. location da fila, por exemplo, us-west2. Se você sem especificar o local, a CLI gcloud escolhe o padrão.

  2. Verifique se ele foi criado com sucesso:

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

    Substitua LOCATION pelo local da fila.

Como criar uma conta de serviço

A solicitação do Cloud Tasks precisa fornecer credenciais no cabeçalho Authorization para que a função do Cloud Run autentique a solicitação. Essa conta de serviço permite que o Cloud Tasks crie e adicione um token OIDC para essa finalidade.

  1. Na interface das contas de serviço, clique em + CRIAR CONTA DE SERVIÇO.

  2. Adicione um nome de conta de serviço (nome de exibição amigável) e selecione Criar.

  3. Defina o Papel e clique em Continuar.

    • Funções de 1ª geração: defina o papel como Cloud Function Invoker.
    • Funções de segunda geração (2a geração): defina o papel como Cloud Run Invoker
  4. Selecione Concluído.

Como implantar o endpoint e o criador de tarefas no App Engine

  1. Navegue até o diretório app/:

    cd ../app/
    
  2. Atualize as variáveis no app.yaml com seus valores:

    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"

    Para encontrar o local da fila, use o seguinte comando:

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

    Substitua LOCATION pelo local da fila.

    Para encontrar o URL da função, use o seguinte comando:

    gcloud functions describe sendEmail
  3. Implante o aplicativo no ambiente padrão do App Engine usando o seguinte comando:

    gcloud app deploy
  4. Abra o aplicativo para enviar um cartão-postal como um e-mail:

    gcloud app browse

Limpar

Depois de concluir o tutorial, você pode limpar os recursos que criou para que eles parem de usar a cota e gerar cobranças. Nas seções a seguir, você aprenderá a excluir e desativar esses recursos.

Como excluir recursos

É possível limpar os recursos criados no Google Cloud para que eles não ocupem a cota e você não receba cobranças por eles no futuro. As próximas seções descrevem como excluir ou desativar esses recursos.

Excluir a função do Cloud Run

  1. Acesse a página Funções do Cloud Run no console do Google Cloud.

    Acesse a página de funções do Cloud Run.

  2. Clique nas caixas de seleção ao lado das funções.

  3. Clique no botão Excluir na parte superior da página e confirme.

Excluir a fila do Cloud Tasks

  1. Abra a página de filas do Cloud Tasks no console.

    Acessar a página de filas do Cloud Tasks

  2. Selecione o nome da fila que você quer excluir e clique em Excluir fila.

  3. Confirme a ação.

Excluir o projeto

O jeito mais fácil de evitar cobranças é excluindo o projeto que você criou para o tutorial.

Para excluir o projeto:

  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.

A seguir