Tutorial: Como depurar um serviço do Cloud Run usando o Eventarc

Neste tutorial, você verá como solucionar erros de ambiente de execução encontrados ao implantar eventos do Cloud Storage usando os registros de auditoria do Cloud em um serviço não autenticado do Cloud Run.

Objetivos

  • Criar um bucket do Cloud Storage para ser a origem do evento.
  • Crie, faça upload e implante uma imagem de contêiner no Cloud Run.
  • Criar gatilhos do Eventarc
  • Faça upload do arquivo para o bucket do Cloud Storage.
  • Solucione problemas e corrija erros de ambiente de execução.

Custos

Neste tutorial, usamos os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem ser qualificados para uma avaliação gratuita.

Ao concluir este tutorial, exclua os recursos criados para evitar o faturamento contínuo. Para mais informações, consulte Como fazer a limpeza.

Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Antes de começar

Siga os pré-requisitos em Como receber um evento de registros de auditoria do Cloud.

Crie um bucket do Cloud Storage

Crie dois buckets de armazenamento em duas regiões como a origem do evento para o serviço do Cloud Run:

us-east1

export BUCKET1="troubleshoot-bucket1-$(gcloud config get-value core/project)"
gsutil mb -l us-east1 gs://${BUCKET1}

us-west1

export BUCKET2="troubleshoot-bucket2-$(gcloud config get-value core/project)"
gsutil mb -l us-west1 gs://${BUCKET2}

Depois que a origem do evento for criada, implante o serviço de receptor de eventos no Cloud Run.

Como recuperar a amostra de código

Clone o repositório:

Go

git clone https://github.com/GoogleCloudPlatform/golang-samples.git
cd golang-samples/eventarc/audit_storage

Java

git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git
cd java-docs-samples/eventarc/audit-storage

.NET

git clone https://github.com/GoogleCloudPlatform/dotnet-docs-samples.git
cd dotnet-docs-samples/eventarc/audit-storage

Node.js

git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git
cd nodejs-docs-samples/eventarc/audit-storage

Python

git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git
cd python-docs-samples/eventarc/audit-storage

Como revisar o código

O código deste tutorial consiste em:

  • Um servidor que processa mensagens recebidas encapsuladas em um CloudEvent dentro da solicitação HTTP POST:

    Go

    
    // Sample audit_storage is a Cloud Run service which handles Cloud Audit Log events with Cloud Storage data.
    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    	"os"
    )
    
    // HelloEventsStorage receives and processes a Cloud Audit Log event with Cloud Storage data.
    func HelloEventsStorage(w http.ResponseWriter, r *http.Request) {
    	s := fmt.Sprintf("Detected change in Cloud Storage bucket: %s", string(r.Header.Get("Ce-Subject")))
    	log.Printf(s)
    	fmt.Fprintln(w, s)
    }
    

    Java

    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestHeader;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class EventController {
    
      private static final List<String> requiredFields =
          Arrays.asList("ce-id", "ce-source", "ce-type", "ce-specversion");
    
      @RequestMapping(value = "/", method = RequestMethod.POST)
      public ResponseEntity<String> receiveMessage(
          @RequestBody Map<String, Object> body, @RequestHeader Map<String, String> headers) {
        for (String field : requiredFields) {
          if (headers.get(field) == null) {
            String msg = String.format("Missing expected header: %s.", field);
            System.out.println(msg);
            return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
          }
        }
    
        if (headers.get("ce-subject") == null) {
          String msg = "Missing expected header: ce-subject.";
          System.out.println(msg);
          return new ResponseEntity<String>(msg, HttpStatus.BAD_REQUEST);
        }
    
        String ceSubject = headers.get("ce-subject");
        String msg = "Detected change in Cloud Storage bucket: " + ceSubject;
        System.out.println(msg);
        return new ResponseEntity<String>(msg, HttpStatus.OK);
      }
    }

    .NET

    
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;
    
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
        }
    
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
    
            logger.LogInformation("Service is starting...");
    
            app.UseRouting();
    
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapPost("/", async context =>
                {
                    logger.LogInformation("Handling HTTP POST");
    
                    var ceSubject = context.Request.Headers["ce-subject"];
                    logger.LogInformation($"ce-subject: {ceSubject}");
    
                    if (string.IsNullOrEmpty(ceSubject))
                    {
                        context.Response.StatusCode = 400;
                        await context.Response.WriteAsync("Bad Request: expected header Ce-Subject");
                        return;
                    }
    
                    await context.Response.WriteAsync($"GCS CloudEvent type: {ceSubject}");
                });
            });
        }
    }
    

    Node.js

    const express = require('express');
    const app = express();
    
    app.use(express.json());
    app.post('/', (req, res) => {
      if (!req.header('ce-subject')) {
        return res
          .status(400)
          .send('Bad Request: missing required header: ce-subject');
      }
    
      console.log(
        `Detected change in Cloud Storage bucket: ${req.header('ce-subject')}`
      );
      return res
        .status(200)
        .send(
          `Detected change in Cloud Storage bucket: ${req.header('ce-subject')}`
        );
    });
    
    module.exports = app;

    Python

    import os
    
    from flask import Flask, request
    
    app = Flask(__name__)
    if __name__ == "__main__":
        app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))
  • Um Dockerfile que define o ambiente operacional do serviço. O conteúdo do Dockerfile varia por linguagem.

    Go

    
    # Use the offical golang image to create a binary.
    # This is based on Debian and sets the GOPATH to /go.
    # https://hub.docker.com/_/golang
    FROM golang:1.16-buster as builder
    
    # Create and change to the app directory.
    WORKDIR /app
    
    # Retrieve application dependencies.
    # This allows the container build to reuse cached dependencies.
    # Expecting to copy go.mod and if present go.sum.
    COPY go.* ./
    RUN go mod download
    
    # Copy local code to the container image.
    COPY . ./
    
    # Build the binary.
    RUN go build -v -o server
    
    # Use the official Debian slim image for a lean production container.
    # https://hub.docker.com/_/debian
    # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
    FROM debian:buster-slim
    RUN set -x && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
        ca-certificates && \
        rm -rf /var/lib/apt/lists/*
    
    # Copy the binary to the production image from the builder stage.
    COPY --from=builder /app/server /server
    
    # Run the web service on container startup.
    CMD ["/server"]
    

    Java

    
    # Use the official maven/Java 8 image to create a build artifact.
    # https://hub.docker.com/_/maven
    FROM maven:3.8-jdk-11 as builder
    
    # Copy local code to the container image.
    WORKDIR /app
    COPY pom.xml .
    COPY src ./src
    
    # Build a release artifact.
    RUN mvn package -DskipTests
    
    # Use AdoptOpenJDK for base image.
    # It's important to use OpenJDK 8u191 or above that has container support enabled.
    # https://hub.docker.com/r/adoptopenjdk/openjdk8
    # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
    FROM adoptopenjdk/openjdk11:alpine-slim
    
    # Copy the jar to the production image from the builder stage.
    COPY --from=builder /app/target/audit-storage-*.jar /audit-storage.jar
    
    # Run the web service on container startup.
    CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/audit-storage.jar"]
    

    .NET

    
    # Use Microsoft's official build .NET image.
    # https://hub.docker.com/_/microsoft-dotnet-core-sdk/
    FROM mcr.microsoft.com/dotnet/core/sdk:3.1-alpine AS build
    WORKDIR /app
    
    # Install production dependencies.
    # Copy csproj and restore as distinct layers.
    COPY *.csproj ./
    RUN dotnet restore
    
    # Copy local code to the container image.
    COPY . ./
    WORKDIR /app
    
    # Build a release artifact.
    RUN dotnet publish -c Release -o out
    
    # Use Microsoft's official runtime .NET image.
    # https://hub.docker.com/_/microsoft-dotnet-core-aspnet/
    FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine AS runtime
    WORKDIR /app
    COPY --from=build /app/out ./
    
    # Run the web service on container startup.
    ENTRYPOINT ["dotnet", "AuditStorage.dll"]

    Node.js

    
    # Use the official lightweight Node.js 10 image.
    # https://hub.docker.com/_/node
    FROM node:12-slim
    
    # Create and change to the app directory.
    WORKDIR /usr/src/app
    
    # Copy application dependency manifests to the container image.
    # A wildcard is used to ensure both package.json AND package-lock.json are copied.
    # Copying this separately prevents re-running npm install on every code change.
    COPY package*.json ./
    
    # Install dependencies.
    # If you add a package-lock.json speed your build by switching to 'npm ci'.
    # RUN npm ci --only=production
    RUN npm install --production
    
    # Copy local code to the container image.
    COPY . .
    
    # Run the web service on container startup.
    CMD [ "npm", "start" ]
    

    Python

    
    # Use the official Python image.
    # https://hub.docker.com/_/python
    FROM python:3.9-slim
    
    # Allow statements and log messages to immediately appear in the Cloud Run logs
    ENV PYTHONUNBUFFERED True
    
    # Copy application dependency manifests to the container image.
    # Copying this separately prevents re-running pip install on every code change.
    COPY requirements.txt ./
    
    # Install production dependencies.
    RUN pip install -r requirements.txt
    
    # Copy local code to the container image.
    ENV APP_HOME /app
    WORKDIR $APP_HOME
    COPY . ./
    
    # Run the web service on container startup.
    # Use gunicorn webserver with one worker process and 8 threads.
    # For environments with multiple CPU cores, increase the number of workers
    # to be equal to the cores available.
    CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app

Como enviar o código

Para enviar o código, siga estas etapas:

  1. Crie a imagem do contêiner com o Cloud Build e faça o upload para o Container Registry:

    Go

     gcloud builds submit --tag gcr.io/PROJECT_ID/audit-storage

    Substitua PROJECT_ID pelo ID do projeto do Google Cloud. Verifique o ID do projeto atual com gcloud config get-value project.

    Após a conclusão, uma mensagem de "SUCESSO" é exibida com o ID, o horário de criação e o nome da imagem. A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

    Java

    1. Use o auxiliar de credencial gcloud para autorizar o Docker a enviar o contêiner para o Container Registry.
      gcloud auth configure-docker
    2. Use o plug-in do Maven do Jib para criar e enviar por push o contêiner ao Container Registry.

      mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/audit-storage

      Substitua PROJECT_ID pelo ID do projeto do Google Cloud.

      audit-storage é o nome do contêiner. Verifique o ID do projeto atual com gcloud config get-value project. Após a conclusão, uma mensagem de "SUCESSO" é exibida. A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

    .NET

     gcloud builds submit --tag gcr.io/PROJECT_ID/audit-storage

    Substitua PROJECT_ID pelo ID do projeto do Google Cloud. Verifique o ID do projeto atual com gcloud config get-value project.

    Após a conclusão, uma mensagem de "SUCESSO" é exibida com o ID, o horário de criação e o nome da imagem. A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

    Node.js

     gcloud builds submit --tag gcr.io/PROJECT_ID/audit-storage

    Substitua PROJECT_ID pelo ID do projeto do Google Cloud. Verifique o ID do projeto atual com gcloud config get-value project.

    Após a conclusão, uma mensagem de "SUCESSO" é exibida com o ID, o horário de criação e o nome da imagem. A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

    Python

     gcloud builds submit --tag gcr.io/PROJECT_ID/audit-storage

    Substitua PROJECT_ID pelo ID do projeto do Google Cloud. Verifique o ID do projeto atual com gcloud config get-value project.

    Após a conclusão, uma mensagem de "SUCESSO" é exibida com o ID, o horário de criação e o nome da imagem. A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

  2. Implante a imagem do contêiner no Cloud Run:

    gcloud run deploy troubleshoot-service --image gcr.io/PROJECT_ID/audit-storage \
    --allow-unauthenticated
    

    Substitua PROJECT_ID pelo ID do projeto do GCP.
    audit-storage é o nome do contêiner e troubleshoot-service é o nome do serviço do Cloud Run.

    Observe que a imagem do contêiner é implantada no serviço e no cluster que você configurou anteriormente em Como configurar os padrões do gcloud.

  3. Ao implantar no Cloud Run, responda y, "Sim", ao prompt permitir não autenticado. Para mais detalhes sobre a autenticação baseada em IAM, consulte Papéis e permissões do Eventarc.

    Quando a implantação for bem-sucedida, a linha de comando exibirá o URL de serviço.

Como criar um gatilho

Agora que você implantou um serviço do Cloud Run, configure um gatilho para detectar eventos do Cloud Storage por meio de registros de auditoria.

  1. Crie um gatilho de registro de auditoria para detectar eventos do Cloud Storage:

    gcloud eventarc triggers create troubleshoot-trigger \
      --destination-run-service=troubleshoot-service \
      --event-filters="type=google.cloud.audit.log.v1.written" \
      --event-filters="serviceName=storage.googleapis.com" \
      --event-filters="methodName=storage.objects.create" \
      --service-account=${PROJECT_NUMBER}-compute@developer.gserviceaccount.com
    

    Isso cria um gatilho chamado troubleshoot-trigger.

  2. Para confirmar se troubleshoot-trigger foi criado, execute:

    gcloud eventarc triggers list
    

    troubleshoot-trigger é exibido listado com um destino de troubleshoot-service.

Como gerar e ver um evento

Para confirmar que você implantou o serviço e pode receber eventos do Cloud Storage:

  1. Crie e faça upload de um arquivo no primeiro bucket de armazenamento:

     echo "Hello World" > random.txt
     gsutil cp random.txt gs://${BUCKET1}/random.txt
    
  2. Monitore os registros para verificar se o serviço recebeu um evento. Os registros de serviço mostram que o serviço está detectando eventos, indicando um problema na configuração:

    Now listening on: http://0.0.0.0:8080

Como investigar o problema

Agora é possível passar pelo processo de investigação do motivo pelo qual o serviço não está recebendo eventos.

Registros de auditoria

Neste tutorial, os eventos do Cloud Storage são emitidos por meio de registros de auditoria e enviados ao Cloud Run. Verifique se os registros de auditoria estão ativados para o Cloud Storage.

  1. Acesse IAM e administrador > Registros de auditoria e marque a caixa de seleção Google Cloud Storage. Acessar o console de registros de auditoria do Cloud
  2. Verifique se os tipos de registro de leitura, leitura e gravação dos registros de auditoria do Cloud estão selecionados.

Depois de ativar os registros de auditoria do Cloud, faça upload do arquivo novamente e verifique os registros. O serviço ainda não recebe eventos e isso pode estar relacionado à localização do gatilho.

Local do gatilho

Pode haver vários recursos em locais diferentes, e você precisa filtrar eventos de origens que estão na mesma região que o destino do Cloud Run. Para mais informações, consulte Locais compatíveis com o Eventarc.

Neste tutorial, você implantou o serviço do Cloud Run em us-central1. Você também criou um gatilho no mesmo local porque eventarc/location estava definido como us-central1.

Para listar o local do gatilho, descreva o gatilho:

gcloud eventarc triggers describe troubleshoot-trigger

No entanto, você criou dois buckets em locais us-east1 e us-west1. Para receber eventos desses locais, é necessário criar acionadores neles. Vamos criar um gatilho em us-east1.

  1. Exclua o gatilho existente no local us-central1:

       gcloud eventarc triggers delete troubleshoot-trigger
    
  2. Defina o local e a região como us-east1:

      gcloud config set eventarc/location us-east1
      gcloud config set run/region us-east1
    
  3. Envie o código novamente.

  4. Crie um novo gatilho no local us-east1:

      gcloud eventarc triggers create troubleshoot-trigger \
        --destination-run-service=troubleshoot-service \
        --event-filters="type=google.cloud.audit.log.v1.written" \
        --event-filters="serviceName=storage.googleapis.com" \
        --event-filters="methodName=storage.objects.create" \
        --service-account=${PROJECT_NUMBER}-compute@developer.gserviceaccount.com
    
  5. Verifique se o gatilho foi criado:

       gcloud eventarc triggers list
    

Um gatilho pode levar até 10 minutos para ser inicializado antes de começar a entregar eventos.

Tempo de inicialização

Depois que os gatilhos são criados, há até 10 minutos de tempo de inicialização antes do início dos eventos. Execute o seguinte comando para confirmar se os gatilhos estão ativos:

   gcloud eventarc triggers list

Uma resposta semelhante é exibida para indicar o status do acionador:

    NAME                   TYPE                               DESTINATION_RUN_SERVICE  DESTINATION_RUN_PATH  ACTIVE
    troubleshoot-trigger3  google.cloud.audit.log.v1.written  troubleshoot-service2                          By 14:16:56
    troubleshoot-trigger   google.cloud.audit.log.v1.written  troubleshoot-service                           Yes

Após 10 minutos, faça upload de um arquivo novamente para cada bucket. Os eventos de cada arquivo são gravados nos registros de serviço do Cloud Run. Se o serviço não receber eventos, ele poderá estar relacionado ao tamanho dos eventos.

Tamanho do evento

Os eventos enviados não podem exceder os limites de tamanho do evento (512 KB).

Um gatilho que enviou eventos anteriormente parou de funcionar

  1. Verifique se a origem está gerando eventos. Verifique os registros de auditoria do Cloud e se o serviço monitorado está emitindo registros. Se os registros forem gravados, mas os eventos não forem entregues, entre em contato com o suporte.

  2. Verifique se existe um tópico do Pub/Sub com o mesmo nome de gatilho.

    1. Para listar acionadores, consulte a lista de gatilhos do gcloud eventarc.
    2. Para listar os tópicos do Pub/Sub, execute:

        gcloud pubsub topics list
      

    Verifique se o nome do tópico do Pub/Sub inclui o nome do gatilho criado. Se o tópico do Pub/Sub estiver ausente, crie um tópico ao criar o gatilho.

  3. Verifique a integridade do tópico do Pub/Sub:

    1. Verifique se há uma marca de seleção na guia Gatilhos. No Console do Cloud, acesse Cloud Run e selecione o serviço criado. e acesse a guia Gatilhos.

    2. Acesse Pub/Sub > Tópicos e clique no tópico do Pub/Sub.

      Acessar os tópicos do Pub/Sub

    3. Monitore se as mensagens forem publicadas no tópico com a métrica: topic/send_message_operation_count. Se as mensagens não forem publicadas no tópico, verifique os Registros de auditoria do Cloud e se o serviço monitorado está emitindo registros. Se os registros forem gravados, mas os eventos não forem entregues, entre em contato com o suporte.

      Métrica de tópico

    4. Monitore se as mensagens forem enviadas por push para o Cloud Run com a métrica: subscription/push_request_count por response_code.

      1. No Console do Cloud, acesse o Cloud Monitoring.

        Acessar o Monitoring

      2. Adicione seu projeto a um novo espaço de trabalho.

      3. Clique em Painéis e selecione o painel Cloud Pub/Sub.

      4. No painel Cloud Pub/Sub, clique no tópico do Pub/Sub que você criou.

      5. Na seção Incidents, clique em Create Policy.

      6. Na página Criar política de alertas, clique em Adicionar condição.

      7. Na guia Métrica, forneça as seguintes condições:

        • Assinatura do Cloud Pub/Sub como o Tipo de recurso.
        • Solicitações de envio como a Métrica.
        • response_code como o Agrupar por.
        • 0 como o Limite de configuração. Métrica de assinatura Para mais informações sobre métricas de uso do Pub/Sub, consulte Como monitorar assinaturas de push.
      8. Clique em Add para acessar a página Create alerting policy.

      9. Na seção Quais são as etapas para corrigir o problema?, forneça um nome de alerta, por exemplo, samplealert, e clique em Salvar.

      10. Para ver o alerta, acesse Monitoring > Dashboard > Cloud Pub/Sub. Clique no tópico Pub/Sub e na guia Assinatura.

      Se os erros de push forem informados, verifique os registros de serviço do Cloud Run. Se o endpoint de recebimento retornar um código de status não OK, isso indica que o código do Cloud Run não está funcionando como esperado e você precisa entrar em contato com o suporte.

Limpeza

Se você criou um novo projeto para este tutorial, exclua o projeto. Se você usou um projeto atual e quer mantê-lo sem as alterações incluídas neste tutorial, exclua os recursos criados para o tutorial.

Como 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. No Console do Cloud, acesse a página Gerenciar recursos:

    Acessar "Gerenciar recursos"

  2. Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir .
  3. Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.

Como excluir recursos do tutorial

  1. Exclua o serviço do Cloud Run que você implantou neste tutorial:

    gcloud run services delete SERVICE-NAME

    SERVICE-NAME é o nome escolhido do serviço.

    Também é possível excluir os serviços do Cloud Run no Console do Google Cloud.

  2. Remova as configurações padrão da gcloud que você adicionou durante a configuração do tutorial.

    Remova a configuração da região:

     gcloud config unset run/region
    
  3. Remova a configuração do projeto:

     gcloud config unset project
    
  4. Exclua outros recursos do Google Cloud criados neste tutorial:

    • Exclua o gatilho:
      gcloud eventarc triggers delete TRIGGER_NAME
      
      Substitua TRIGGER_NAME pelo nome da sua Entrada.

    • Exclua a imagem de contêiner denominada gcr.io/PROJECT_ID/audit-storage do Container Registry. Substitua PROJECT_ID pelo ID do projeto do Google Cloud.

A seguir

  • Para resolver outros problemas que você pode encontrar ao usar o Eventarc, consulte Solução de problemas.