Acionador de push do Pub/Sub

Nesta página, descrevemos o uso de Pub/Sub para enviar mensagens ao endpoint do serviço do Cloud Run, em que as mensagens são entregues posteriormente aos contêineres como solicitações HTTP. Nesta página, mostramos como ativar o serviço para processar com segurança as mensagens enviadas de uma assinatura de um Pub/Sub no mesmo projeto do Google Cloud.

Ao utilizar as contas de serviço e as permissões do IAM, use o Pub/Sub com segurança e privacidade junto ao Cloud Run, sem precisar expor publicamente o serviço do Cloud Run. Somente a assinatura do Pub/Sub que você configurou pode invocar seu serviço.

Prazos de confirmação do Cloud Run

Defina o prazo de confirmação da assinatura do Pub/Sub (ackDeadlineSeconds) para o máximo permitido de 600 segundos. O serviço do Cloud Run precisa confirmar a mensagem do Pub/Sub retornando uma resposta em até 600 segundos. Caso contrário, o Pub/Sub reenviará a mensagem, causando um acionamento duplicado do serviço do Cloud Run.

Casos de uso

Os possíveis casos de uso incluem:

Visão geral da integração

Para integrar seu serviço ao Pub/Sub:

  • Crie um tópico do Pub/Sub.
  • Adicione o código no serviço do Cloud Run para responder às mensagens do Pub/Sub enviadas para o tópico que você criou.
  • Crie uma conta de serviço com as permissões necessárias.
  • Crie uma assinatura do Pub/Sub e associe-a à conta de serviço. Essa assinatura enviará ao seu serviço qualquer mensagem publicada no tópico.

Antes de começar

  • Configure o ambiente conforme descrito na página de configuração do Cloud Run, caso ainda não tenha feito isso.
  • Este guia pressupõe que você já tenha um serviço do Cloud Run e queira adicionar um código que o integre ao Pub/Sub. Se você não tiver um serviço assim, considere usar o tutorial do Cloud Run para Pub/Sub em vez de seguir esta página.

Como adicionar código para processar mensagens do Pub/Sub

Edite seu código de serviço existente para adicionar o código necessário para a compatibilidade com o Pub/Sub. Seu serviço precisa extrair a mensagem da solicitação e retornar um código de sucesso esperado. A seguir estão snippets para idiomas selecionados (ao seu critério) que mostram como fazer isso para uma simples mensagem Hello World:

Node.js

app.post('/', (req, res) => {
  if (!req.body) {
    const msg = 'no Pub/Sub message received';
    console.error(`error: ${msg}`);
    res.status(400).send(`Bad Request: ${msg}`);
    return;
  }
  if (!req.body.message) {
    const msg = 'invalid Pub/Sub message format';
    console.error(`error: ${msg}`);
    res.status(400).send(`Bad Request: ${msg}`);
    return;
  }

  const pubSubMessage = req.body.message;
  const name = pubSubMessage.data
    ? Buffer.from(pubSubMessage.data, 'base64').toString().trim()
    : 'World';

  console.log(`Hello ${name}!`);
  res.status(204).send();
});

Python

@app.route("/", methods=["POST"])
def index():
    """Receive and parse Pub/Sub messages."""
    envelope = request.get_json()
    if not envelope:
        msg = "no Pub/Sub message received"
        print(f"error: {msg}")
        return f"Bad Request: {msg}", 400

    if not isinstance(envelope, dict) or "message" not in envelope:
        msg = "invalid Pub/Sub message format"
        print(f"error: {msg}")
        return f"Bad Request: {msg}", 400

    pubsub_message = envelope["message"]

    name = "World"
    if isinstance(pubsub_message, dict) and "data" in pubsub_message:
        name = base64.b64decode(pubsub_message["data"]).decode("utf-8").strip()

    print(f"Hello {name}!")

    return ("", 204)

Go


// PubSubMessage is the payload of a Pub/Sub event.
// See the documentation for more details:
// https://cloud.google.com/pubsub/docs/reference/rest/v1/PubsubMessage
type PubSubMessage struct {
	Message struct {
		Data []byte `json:"data,omitempty"`
		ID   string `json:"id"`
	} `json:"message"`
	Subscription string `json:"subscription"`
}

// HelloPubSub receives and processes a Pub/Sub push message.
func HelloPubSub(w http.ResponseWriter, r *http.Request) {
	var m PubSubMessage
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("ioutil.ReadAll: %v", err)
		http.Error(w, "Bad Request", http.StatusBadRequest)
		return
	}
	// byte slice unmarshalling handles base64 decoding.
	if err := json.Unmarshal(body, &m); err != nil {
		log.Printf("json.Unmarshal: %v", err)
		http.Error(w, "Bad Request", http.StatusBadRequest)
		return
	}

	name := string(m.Message.Data)
	if name == "" {
		name = "World"
	}
	log.Printf("Hello %s!", name)
}

Java

import com.example.cloudrun.Body;
import java.util.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

// PubsubController consumes a Pub/Sub message.
@RestController
public class PubSubController {
  @RequestMapping(value = "/", method = RequestMethod.POST)
  public ResponseEntity<String> receiveMessage(@RequestBody Body body) {
    // Get PubSub message from request body.
    Body.Message message = body.getMessage();
    if (message == null) {
      String msg = "Bad Request: invalid Pub/Sub message format";
      System.out.println(msg);
      return new ResponseEntity<>(msg, HttpStatus.BAD_REQUEST);
    }

    String data = message.getData();
    String target =
        !StringUtils.isEmpty(data) ? new String(Base64.getDecoder().decode(data)) : "World";
    String msg = "Hello " + target + "!";

    System.out.println(msg);
    return new ResponseEntity<>(msg, HttpStatus.OK);
  }
}

É necessário codificar o serviço para retornar um código de resposta HTTP preciso. Códigos de êxito, como HTTP 200 ou 204, confirmam o processamento completo da mensagem do Pub/Sub. Códigos de erro, como HTTP 400 ou 500, indicam que a mensagem será repetida, conforme descrito em Como receber mensagens usando o Push.

Crie e implante o serviço do Cloud Run depois de atualizá-lo com o código do Pub/Sub acima.

Criar uma conta de serviço para assinatura

Você precisa criar uma conta de serviço para associar à sua assinatura do Pub/Sub e conceder a ela a permissão para invocar o serviço do Cloud Run. As mensagens do Pub/Sub enviadas para o serviço do Cloud Run terão a identidade dessa conta de serviço.

Use uma conta de serviço atual para representar a identidade da assinatura de Pub/Sub ou criar uma nova.

Para criar uma nova conta de serviço e conceder a ela permissão para invocar o serviço do Cloud Run:

Console

  1. No console do Google Cloud, acesse a página Contas de serviço.

    Acessar a página "Contas de serviço"

  2. Selecione um projeto.

  3. Insira um nome de conta de serviço a ser exibido no console do Google Cloud.

    O console do Google Cloud gerará um ID de conta de serviço com base nesse nome. Edite o ID se for necessário. Não será possível alterar o ID depois.

  4. Opcional: digite uma descrição da conta de serviço.

  5. Clique em Criar e continuar.

  6. Opcional: clique no campo Selecionar um papel.

  7. Selecione Cloud Run > Chamador do Cloud Run.

  8. Clique em Concluído.

Linha de comando

  1. Crie a conta de serviço:

    gcloud iam service-accounts create SERVICE_ACCOUNT_NAME \
       --display-name "DISPLAYED_SERVICE_ACCOUNT_NAME"

    Substituir

    • SERVICE_ACCOUNT_NAME por um nome em letras minúsculas exclusivo no projeto do Google Cloud, por exemplo, my-invoker-service-account-name.
    • DISPLAYED_SERVICE_ACCOUNT_NAME pelo nome que você quer exibir para essa conta de serviço, por exemplo, no console, My Invoker Service Account.
  2. Para o Cloud Run, conceda à conta de serviço permissão para invocar o serviço:

    gcloud run services add-iam-policy-binding SERVICE \
       --member=serviceAccount:SERVICE_ACCOUNT_NAME@PROJECT_ID.iam.gserviceaccount.com \
       --role=roles/run.invoker

    Substituir

    • SERVICE pelo nome do serviço que você quer que seja invocado pelo Pub/Sub;
    • SERVICE_ACCOUNT_NAME pelo nome da conta de serviço;
    • PROJECT_ID pelo ID de projeto do Google Cloud;
  3. Conceda à conta de serviço o acesso ao projeto a fim de que ela tenha permissão para concluir ações específicas nos recursos do projeto:

    gcloud projects add-iam-policy-binding RESOURCE_ID \
       --member=PRINCIPAL --role=roles/run.invoker

    Substituir

    • RESOURCE_ID pelo ID do projeto no Google Cloud.

    • PRINCIPAL: um identificador do principal ou do membro, que geralmente tem o seguinte formato: PRINCIPAL_TYPE:ID. Por exemplo, user:my-user@example.com. Para ver uma lista completa dos valores que PRINCIPAL pode ter, consulte a referência de vinculação de políticas.

Terraform

Para saber como aplicar ou remover uma configuração do Terraform, consulte Comandos básicos do Terraform.

  1. Para criar a conta de serviço, adicione o seguinte ao seu arquivo .tf existente:

    resource "google_service_account" "sa" {
      account_id   = "cloud-run-pubsub-invoker"
      display_name = "Cloud Run Pub/Sub Invoker"
    }
  2. Para conceder à sua conta de serviço permissão para invocar o serviço, adicione o seguinte ao arquivo .tf:

    resource "google_cloud_run_service_iam_binding" "binding" {
      location = google_cloud_run_v2_service.default.location
      service  = google_cloud_run_v2_service.default.name
      role     = "roles/run.invoker"
      members  = ["serviceAccount:${google_service_account.sa.email}"]
    }

Criar um tópico do Pub/Sub

As solicitações para seu serviço são acionadas por mensagens publicadas em um tópico do Pub/Sub. Portanto, você precisará criar um tópico:

Console

  1. Acesse a página de tópicos do Pub/Sub no console do Google Cloud.

    Página de tópicos do Pub/Sub

  2. Clique em Criar um tópico.

  3. Digite um Nome exclusivo para seu tópico, como "MyTopic", por exemplo.

Linha de comando

gcloud pubsub topics create TOPIC-NAME

Substitua TOPIC-NAME por um nome de tópico exclusivo no projeto do Google Cloud.

Terraform

Para saber como aplicar ou remover uma configuração do Terraform, consulte Comandos básicos do Terraform.

Veja nesta seção como usar o Terraform para definir seu serviço em uma configuração do Terraform usando o recurso google_pubsub_topic do provedor do Google Cloud Platform.

Adicione o seguinte conteúdo ao arquivo .tf:

resource "google_pubsub_topic" "default" {
  name = "pubsub_topic"
}

Criar uma assinatura push e associá-la à conta de serviço

Depois de criar o tópico do Pub/Sub, você precisa assinar o serviço para receber mensagens enviadas para um tópico e associar a assinatura à conta de serviço criada para o serviço. É possível usar o console do Google Cloud ou a linha de comando gcloud:

Console

  1. Acesse a página de tópicos do Cloud Pub/Sub.

    Página de tópicos do Pub/Sub

  2. Clique no tópico em que você quer se inscrever.

  3. Clique em Criar assinatura para exibir o formulário de inscrição:

    formulário de inscrição

    No formulário, siga estas etapas:

    1. Especifique o tipo de envio push.
    2. Para a URL de Endpoints, especifique a URL do seu serviço, que é exibida na página de detalhes do serviço.
    3. Na lista suspensa Conta de serviço, selecione a conta de serviço que você criou com as permissões necessárias.
    4. Definir a validade da assinatura e um prazo de confirmação de 600 segundos.
    5. Clique em Criar.
  4. A assinatura foi concluída. As mensagens postadas no tópico agora serão enviadas para seu serviço.

Linha de comando

  1. Permita que o Pub/Sub crie tokens de autenticação no seu projeto:

    gcloud projects add-iam-policy-binding PROJECT-ID \
         --member=serviceAccount:service-PROJECT-NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com \
         --role=roles/iam.serviceAccountTokenCreator

    Substituir

    • PROJECT-ID pelo ID do projeto do Google Cloud;
    • PROJECT-NUMBER pelo número do projeto do Google Cloud.

      O ID e o número do projeto estão listados no painel Informações do projeto no console do Google Cloud do seu projeto.

  2. Crie uma assinatura do Pub/Sub com a conta de serviço criada com as permissões necessárias:

    gcloud pubsub subscriptions create SUBSCRIPTION-ID --topic TOPIC-NAME \
       --ack-deadline=600 \
       --push-endpoint=SERVICE-URL/ \
       --push-auth-service-account=SERVICE-ACCOUNT-NAME@PROJECT-ID.iam.gserviceaccount.com

    Substituir

    • TOPIC-NAME pelo tópico que você criou anteriormente.
    • SERVICE-URL pelo URL de HTTPS fornecido quando você implantou o serviço. Para encontrá-lo, use o comando gcloud run services describe, especificando o nome do serviço: procure a linha de retorno iniciada por domain;
    • PROJECT-ID pelo ID de projeto do Google Cloud;

    A sinalização --push-auth-service-account ativa a funcionalidade de push do Pub/Sub para Autenticação e autorização.

    O prazo de confirmação está definido como o máximo de 600 segundos.

  3. A assinatura foi concluída. As mensagens postadas no tópico agora serão enviadas para seu serviço. É possível enviar uma mensagem de teste para o tópico usando o comando:

    gcloud pubsub topics publish TOPIC --message "hello"

    Substitua TOPIC pelo nome do tópico que você criou.

Terraform

Para saber como aplicar ou remover uma configuração do Terraform, consulte Comandos básicos do Terraform.

  1. Permita que o Pub/Sub crie tokens de autenticação no seu projeto: Adicione a instrução a seguir ao seu arquivo .tf:

    resource "google_project_service_identity" "pubsub_agent" {
      provider = google-beta
      project  = data.google_project.project.project_id
      service  = "pubsub.googleapis.com"
    }
    
    resource "google_project_iam_binding" "project_token_creator" {
      project = data.google_project.project.project_id
      role    = "roles/iam.serviceAccountTokenCreator"
      members = ["serviceAccount:${google_project_service_identity.pubsub_agent.email}"]
    }
  2. Crie uma assinatura do Pub/Sub com a conta de serviço criada com as permissões necessárias: Adicione a instrução a seguir ao seu arquivo .tf:

    resource "google_pubsub_subscription" "subscription" {
      name  = "pubsub_subscription"
      topic = google_pubsub_topic.default.name
      push_config {
        push_endpoint = google_cloud_run_v2_service.default.uri
        oidc_token {
          service_account_email = google_service_account.sa.email
        }
        attributes = {
          x-goog-version = "v1"
        }
      }
      depends_on = [google_cloud_run_v2_service.default]
    }
  3. A assinatura foi concluída. As mensagens postadas no tópico agora serão enviadas para seu serviço. É possível enviar uma mensagem de teste para o tópico usando o comando:

    gcloud pubsub topics publish TOPIC --message "hello"

    Substitua TOPIC pelo nome do tópico que você criou.

A seguir