Cloud Run에서 Pub/Sub 사용 튜토리얼

이 튜토리얼에서는 Cloud Run 서비스를 작성, 배포하고 Pub/Sub 푸시 구독으로부터 호출하는 방법을 보여줍니다.

gcloud 기본값 설정

Cloud Run 서비스의 기본값으로 gcloud를 구성하려면 다음 안내를 따르세요.

  1. 기본 프로젝트를 설정합니다.

    gcloud config set project PROJECT_ID

    PROJECT_ID를 이 튜토리얼용으로 만든 프로젝트 이름으로 바꿉니다.

  2. 선택한 리전에 맞게 gcloud를 구성합니다.

    gcloud config set run/region REGION

    REGION을 지원되는 Cloud Run 리전 중 원하는 리전으로 바꿉니다.

Cloud Run 위치

Cloud Run은 리전을 기반으로 합니다. 즉, Cloud Run 서비스를 실행하는 인프라가 특정 리전에 위치해 있으며 해당 리전 내의 모든 영역에서 중복으로 사용할 수 있도록 Google이 관리합니다.

Cloud Run 서비스를 실행하는 리전을 선택하는 데 있어 중요한 기준은 지연 시간, 가용성 또는 내구성 요구사항입니다. 일반적으로 사용자와 가장 가까운 리전을 선택할 수 있지만 Cloud Run 서비스에서 사용하는 다른 Google Cloud제품 위치도 고려해야 합니다. 여러 위치에서 Google Cloud 제품을 함께 사용하면 서비스 지연 시간과 비용이 영향을 받을 수 있습니다.

Cloud Run은 다음 리전에서 사용할 수 있습니다.

등급 1 가격 적용

  • asia-east1(타이완)
  • asia-northeast1(도쿄)
  • asia-northeast2(오사카)
  • asia-south1(인도 뭄바이)
  • europe-north1(핀란드) 잎 아이콘 낮은 CO2
  • europe-north2(스톡홀름) 잎 아이콘 낮은 CO2
  • europe-southwest1 (마드리드) 잎 아이콘 낮은 CO2
  • europe-west1 (벨기에) 잎 아이콘 낮은 CO2
  • europe-west4(네덜란드) 잎 아이콘 낮은 CO2
  • europe-west8 (밀라노)
  • europe-west9 (파리) 잎 아이콘 낮은 CO2
  • me-west1 (텔아비브)
  • northamerica-south1 (멕시코)
  • us-central1 (아이오와) 잎 아이콘 낮은 CO2
  • us-east1(사우스캐롤라이나)
  • us-east4(북 버지니아)
  • us-east5 (콜럼버스)
  • us-south1 (댈러스) 잎 아이콘 낮은 CO2
  • us-west1 (오리건) 잎 아이콘 낮은 CO2

등급 2 가격 적용

  • africa-south1 (요하네스버그)
  • asia-east2(홍콩)
  • asia-northeast3(대한민국 서울)
  • asia-southeast1(싱가포르)
  • asia-southeast2 (자카르타)
  • asia-south2(인도 델리)
  • australia-southeast1(시드니)
  • australia-southeast2(멜버른)
  • europe-central2(폴란드 바르샤바)
  • europe-west10 (베를린)
  • europe-west12 (토리노)
  • europe-west2(영국 런던) 잎 아이콘 낮은 CO2
  • europe-west3(독일 프랑크푸르트)
  • europe-west6 (스위스 취리히) 잎 아이콘 낮은 CO2
  • me-central1 (도하)
  • me-central2 (담맘)
  • northamerica-northeast1 (몬트리올) 잎 아이콘 낮은 CO2
  • northamerica-northeast2 (토론토) 잎 아이콘 낮은 CO2
  • southamerica-east1 (브라질 상파울루) 잎 아이콘 낮은 CO2
  • southamerica-west1 (칠레 산티아고) 잎 아이콘 낮은 CO2
  • us-west2(로스앤젤레스)
  • us-west3(솔트레이크시티)
  • us-west4(라스베이거스)

Cloud Run 서비스를 이미 만들었다면 Google Cloud 콘솔의 Cloud Run 대시보드에서 리전을 확인할 수 있습니다.

Artifact Registry 표준 저장소 만들기

컨테이너 이미지를 저장할 Artifact Registry 표준 저장소를 만듭니다.

gcloud artifacts repositories create REPOSITORY \
    --repository-format=docker \
    --location=REGION

다음과 같이 바꿉니다.

  • REPOSITORY를 저장소의 고유한 이름으로 바꿉니다.
  • REGION: Artifact Registry 저장소에 사용할 Google Cloud 리전

Pub/Sub 주제 만들기

샘플 서비스가 Pub/Sub 주제에 게시된 메시지로 트리거되므로, Pub/Sub에 주제를 만들어야 합니다.

gcloud

새 Pub/Sub 주제를 만들려면 다음 명령어를 사용합니다.

gcloud pubsub topics create myRunTopic

myRunTopic을 사용하거나 Google Cloud 프로젝트 내에서 고유한 주제 이름으로 바꿀 수 있습니다.

Terraform

Terraform 구성을 적용하거나 삭제하는 방법은 기본 Terraform 명령어를 참조하세요.

Pub/Sub 주제를 만들려면 기존 main.tf 파일에 다음을 추가합니다.

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

클라우드 프로젝트 내에서 고유한 주제 이름을 사용할 수 있습니다.

코드 샘플 검색

사용할 코드 샘플을 검색하려면 다음 안내를 따르세요.

  1. 샘플 앱 저장소를 로컬 머신에 클론합니다.

    Node.js

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    또는 zip 파일로 샘플을 다운로드하고 압축을 풀 수 있습니다.

    Python

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    또는 zip 파일로 샘플을 다운로드하고 압축을 풀 수 있습니다.

    Go

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

    또는 zip 파일로 샘플을 다운로드하고 압축을 풀 수 있습니다.

    자바

    git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git

    또는 zip 파일로 샘플을 다운로드하고 압축을 풀 수 있습니다.

    C#

    git clone https://github.com/GoogleCloudPlatform/dotnet-docs-samples.git

    또는 zip 파일로 샘플을 다운로드하고 압축을 풀 수 있습니다.

  2. Cloud Run 샘플 코드가 포함된 디렉터리로 변경합니다.

    Node.js

    cd nodejs-docs-samples/run/pubsub/

    Python

    cd python-docs-samples/run/pubsub/

    Go

    cd golang-samples/run/pubsub/

    자바

    cd java-docs-samples/run/pubsub/

    C#

    cd dotnet-docs-samples/run/Run.Samples.Pubsub.MinimalApi/

코드 검토

이 튜토리얼의 코드는 다음과 같이 구성됩니다.

  • 새로 추가되는 요청을 처리하는 서버

    Node.js

    Node.js 서비스를 쉽게 테스트할 수 있도록 서버 구성이 서버 시작과 구분됩니다.

    Node.js 웹 서버가 app.js에 설정됩니다.

    const express = require('express');
    const app = express();
    
    // This middleware is available in Express v4.16.0 onwards
    app.use(express.json());

    웹 서버가 index.js에서 시작됩니다.

    const app = require('./app.js');
    const PORT = parseInt(parseInt(process.env.PORT)) || 8080;
    
    app.listen(PORT, () =>
      console.log(`nodejs-pubsub-tutorial listening on port ${PORT}`)
    );

    Python

    import base64
    
    from flask import Flask, request
    
    
    app = Flask(__name__)
    

    Go

    
    // Sample run-pubsub is a Cloud Run service which handles Pub/Sub messages.
    package main
    
    import (
    	"encoding/json"
    	"io"
    	"log"
    	"net/http"
    	"os"
    )
    
    func main() {
    	http.HandleFunc("/", HelloPubSub)
    	// Determine port for HTTP service.
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    		log.Printf("Defaulting to port %s", port)
    	}
    	// Start HTTP server.
    	log.Printf("Listening on port %s", port)
    	if err := http.ListenAndServe(":"+port, nil); err != nil {
    		log.Fatal(err)
    	}
    }
    

    자바

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class PubSubApplication {
      public static void main(String[] args) {
        SpringApplication.run(PubSubApplication.class, args);
      }
    }

    C#

    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    var port = Environment.GetEnvironmentVariable("PORT");
    if (port != null)
    {
        app.Urls.Add($"http://0.0.0.0:{port}");
    }
    

  • Pub/Sub 메시지를 처리하고 인사말을 로깅하는 핸들러입니다.

    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

    
    // WrappedMessage is the payload of a Pub/Sub event.
    //
    // For more information about receiving messages from a Pub/Sub event
    // see: https://cloud.google.com/pubsub/docs/push#receive_push
    type WrappedMessage 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 WrappedMessage
    	body, err := io.ReadAll(r.Body)
    	defer r.Body.Close()
    	if err != nil {
    		log.Printf("io.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)
    }
    

    자바

    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);
      }
    }

    C#

    app.MapPost("/", (Envelope envelope) =>
    {
        if (envelope?.Message?.Data == null)
        {
            app.Logger.LogWarning("Bad Request: Invalid Pub/Sub message format.");
            return Results.BadRequest();
        }
    
        var data = Convert.FromBase64String(envelope.Message.Data);
        var target = System.Text.Encoding.UTF8.GetString(data);
    
        app.Logger.LogInformation($"Hello {target}!");
    
        return Results.NoContent();
    });
    

    정확한 HTTP 응답 코드를 반환하도록 서비스를 코딩해야 합니다. HTTP 200 또는 204와 같은 성공 코드는 Pub/Sub 메시지 처리가 완료되었음을 나타냅니다. HTTP 400 또는 500과 같은 오류 코드는 푸시를 사용하여 메시지 수신 가이드에 설명된 것처럼 메시지가 재시도됨을 나타냅니다.

  • 서비스의 작동 환경을 정의하는 Dockerfile입니다. Dockerfile 콘텐츠는 언어별로 다릅니다.

    Node.js

    
    # Use the official lightweight Node.js image.
    # https://hub.docker.com/_/node
    FROM node:20-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 need a deterministic and repeatable build create a
    # package-lock.json file and use npm ci:
    # RUN npm ci --omit=dev
    # if you need to include development dependencies during development
    # of your application, use:
    # RUN npm install --dev
    
    RUN npm install --omit=dev
    
    # 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.11
    
    # 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.
    # Timeout is set to 0 to disable the timeouts of the workers to allow Cloud Run to handle instance scaling.
    CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app
    

    Go

    
    # Use the official Go image to create a binary.
    # This is based on Debian and sets the GOPATH to /go.
    # https://hub.docker.com/_/golang
    FROM golang:1.24-bookworm 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:bookworm-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"]
    

    자바

    이 샘플은 Jib를 사용해서 일반적인 자바 도구로 Docker 이미지를 빌드합니다. Jib는 Dockerfile을 사용하거나 Docker를 설치할 필요 없이 컨테이너 빌드를 최적화합니다. Jib로 자바 컨테이너 빌드에 대해 자세히 알아보세요.

    <plugin>
      <groupId>com.google.cloud.tools</groupId>
      <artifactId>jib-maven-plugin</artifactId>
      <version>3.4.0</version>
      <configuration>
        <to>
          <image>gcr.io/PROJECT_ID/pubsub</image>
        </to>
      </configuration>
    </plugin>
    

    C#

    # Build in SDK base image
    FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
    WORKDIR /app
    
    COPY *.csproj ./
    RUN dotnet restore
    
    COPY . ./
    RUN dotnet publish -r linux-x64 --no-self-contained -p:PublishReadyToRun=true -c Release -o out
    
    # Copy to runtime image
    FROM mcr.microsoft.com/dotnet/aspnet:6.0
    WORKDIR /app
    COPY --from=build-env /app/out .
    
    # Port passed in by Cloud Run via environment variable PORT.  Default 8080.
    ENV PORT=8080
    
    ENTRYPOINT ["dotnet", "Run.Samples.Pubsub.MinimalApi.dll"]

Pub/Sub 요청 원본을 인증하는 방법은 Pub/Sub와 통합을 참조하세요.

코드 제공

코드 제공은 Cloud Build로 컨테이너 이미지를 빌드하고, Artifact Registry에 컨테이너 이미지를 업로드하고, 컨테이너 이미지를 Cloud Run에 배포하는 세 단계로 구성됩니다.

코드를 제공하려면 다음 안내를 따르세요.

  1. 컨테이너를 빌드하고 Artifact Registry에 게시합니다.

    Node.js

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/pubsub
    다음과 같이 바꿉니다.
    • PROJECT_ID를 Google Cloud 프로젝트 ID로 바꿉니다.
    • REPOSITORY를 Artifact Registry 저장소 이름으로 바꿉니다.
    • REGION: Artifact Registry 저장소에 사용할 Google Cloud 리전

    pubsub는 이미지 이름입니다.

    성공하면 ID, 생성 시간, 이미지 이름이 포함된 SUCCESS 메시지가 표시됩니다. 이미지는 Artifact Registry에 저장되며 필요한 경우 다시 사용될 수 있습니다.

    Python

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/pubsub
    다음과 같이 바꿉니다.
    • PROJECT_ID를 Google Cloud 프로젝트 ID로 바꿉니다.
    • REPOSITORY를 Artifact Registry 저장소 이름으로 바꿉니다.
    • REGION: Artifact Registry 저장소에 사용할 Google Cloud 리전

    pubsub는 이미지 이름입니다.

    성공하면 ID, 생성 시간, 이미지 이름이 포함된 SUCCESS 메시지가 표시됩니다. 이미지는 Artifact Registry에 저장되며 필요한 경우 다시 사용될 수 있습니다.

    Go

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/pubsub
    다음과 같이 바꿉니다.
    • PROJECT_ID를 Google Cloud 프로젝트 ID로 바꿉니다.
    • REPOSITORY를 Artifact Registry 저장소 이름으로 바꿉니다.
    • REGION: Artifact Registry 저장소에 사용할 Google Cloud 리전

    pubsub는 이미지 이름입니다.

    성공하면 ID, 생성 시간, 이미지 이름이 포함된 성공 메시지가 표시됩니다. 이미지는 Artifact Registry에 저장되며 필요한 경우 다시 사용될 수 있습니다.

    자바

    • gcloud CLI 사용자 인증 정보 도우미를 사용하여 Docker가 Artifact Registry로 내보내도록 승인합니다.
      gcloud auth configure-docker
    • Jib Maven 플러그인을 사용하여 컨테이너를 빌드하고 Artifact Registry로 내보냅니다.
      mvn compile jib:build -D image=REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/pubsub
      다음과 같이 바꿉니다.
      • PROJECT_ID를 Google Cloud 프로젝트 ID로 바꿉니다.
      • REPOSITORY를 Artifact Registry 저장소 이름으로 바꿉니다.
      • REGION: Artifact Registry 저장소에 사용할 Google Cloud 리전

      pubsub는 이미지 이름입니다.

      성공하면 BUILD SUCCESS 메시지가 표시됩니다. 이미지는 Artifact Registry에 저장되며 필요한 경우 다시 사용될 수 있습니다.

    C#

    gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/pubsub
    다음과 같이 바꿉니다.
    • PROJECT_ID를 Google Cloud 프로젝트 ID로 바꿉니다.
    • REPOSITORY를 Artifact Registry 저장소 이름으로 바꿉니다.
    • REGION: Artifact Registry 저장소에 사용할 Google Cloud 리전

    pubsub는 이미지 이름입니다.

    성공하면 ID, 생성 시간, 이미지 이름이 포함된 성공 메시지가 표시됩니다. 이미지는 Artifact Registry에 저장되며 필요한 경우 다시 사용될 수 있습니다.

  2. 애플리케이션 배포

    명령줄

    1. 다음 명령어를 실행하여 앱을 배포합니다.

      gcloud run deploy pubsub-tutorial --image REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/pubsub  --no-allow-unauthenticated
      다음과 같이 바꿉니다.
      • PROJECT_ID를 Google Cloud 프로젝트 ID로 바꿉니다.
      • REPOSITORY를 Artifact Registry 저장소 이름으로 바꿉니다.
      • REGION: Artifact Registry 저장소에 사용할 Google Cloud 리전

      pubsub는 이미지 이름이고 pubsub-tutorial은 서비스 이름입니다. 컨테이너 이미지는 이전에 gcloud 설정에서 구성한 서비스 및 리전에 배포됩니다.

      --no-allow-unauthenticated 플래그는 서비스에 대해 인증되지 않은 액세스를 제한합니다. 서비스를 비공개로 유지하면 Cloud Run의 자동 Pub/Sub 통합을 사용해 요청을 인증할 수 있습니다. 구성 방법에 대한 자세한 내용은 Pub/Sub와 통합을 참조하세요. Identity and Access Management(IAM)를 기반으로 한 인증에 대한 자세한 내용은 IAM을 사용하여 액세스 관리를 참조하세요.

      배포가 완료될 때까지 기다립니다. 이 작업은 30초 정도 걸릴 수 있습니다. 성공하면 명령줄에 서비스 URL이 표시됩니다. 이 URL은 Pub/Sub 구독을 구성하는 데 사용됩니다.

    2. 서비스에 코드 업데이트를 배포하려면 이전 단계를 반복합니다. 서비스에 배포할 때마다 새 버전이 생성되고 준비가 되면 자동으로 트래픽 제공이 시작됩니다.

    Terraform

    Cloud Run 서비스를 만들려면 다음을 기존 .tf 파일에 추가합니다.

    resource "google_project_service" "cloudrun_api" {
       service            = "run.googleapis.com"
       disable_on_destroy = false
    }
    
    resource "google_cloud_run_v2_service" "default" {
       name     = "pubsub-tutorial"
       location = "REGION"
    
       template {
          containers {
             image = "IMAGE_URL"
         }
       }
       depends_on = [google_project_service.cloudrun_api]
    }
             
    다음과 같이 바꿉니다.
    • IMAGE_URL을 이미지 URL(REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/pubsub)로 바꿉니다.
    • REGION을 Artifact Registry 저장소에 사용할 Google Cloud 리전으로 바꿉니다.

Pub/Sub와 통합

Pub/Sub와 서비스를 통합하려면 다음 안내를 따르세요.

gcloud

  1. 서비스 계정을 만들거나 선택하여 Pub/Sub 구독 ID를 나타냅니다.

    gcloud iam service-accounts create cloud-run-pubsub-invoker \
        --display-name "Cloud Run Pub/Sub Invoker"

    cloud-run-pubsub-invoker를 사용하거나 Google Cloud 프로젝트 내에서 고유한 이름으로 바꿀 수 있습니다.

  2. 서비스 계정으로 Pub/Sub 구독을 만듭니다.

    1. 호출자 서비스 계정에 pubsub-tutorial 서비스 호출 권한을 부여합니다.

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

      IAM 변경사항을 적용하는 데 몇 분 정도 걸릴 수 있습니다. 그동안 서비스 로그에 HTTP 403 오류가 발생할 수 있습니다.

    2. 프로젝트에서 인증 토큰을 만들도록 Pub/Sub를 허용합니다.

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

      다음과 같이 바꿉니다.

      • PROJECT_ID를 Google Cloud 프로젝트 ID로 바꿉니다.
      • PROJECT_NUMBER를 Google Cloud 프로젝트 번호로 바꿉니다.

      프로젝트 ID 및 프로젝트 번호는 프로젝트의 Google Cloud 콘솔프로젝트 정보 패널에 나열됩니다.

    3. 서비스 계정으로 Pub/Sub 구독을 만듭니다.

      gcloud pubsub subscriptions create myRunSubscription --topic myRunTopic \
      --ack-deadline=600 \
      --push-endpoint=SERVICE-URL/ \
      --push-auth-service-account=cloud-run-pubsub-invoker@PROJECT_ID.iam.gserviceaccount.com

      다음과 같이 바꿉니다.

      • myRunTopic이전에 만든 주제로 바꿉니다.
      • SERVICE-URL을 서비스 배포 시 제공된 HTTPS URL로 바꿉니다. 이 URL은 도메인 매핑이 추가된 경우에도 작동합니다.
      • PROJECT_ID를 Google Cloud 프로젝트 ID로 바꿉니다.

      --push-auth-service-account 플래그는 인증 및 승인을 위한 Pub/Sub 푸시 기능을 활성화합니다.

      Cloud Run 서비스 도메인은 Pub/Sub 구독에 사용할 수 있도록 자동으로 등록됩니다.

      Cloud Run 전용의 경우, 토큰이 유효한지 확인하는 기본 제공되는 인증 검사와 서비스 계정에 Cloud Run 서비스 호출 권한이 있는지 확인하는 승인 검사가 있습니다.

이제 서비스가 Pub/Sub와 완전히 통합되었습니다.

Terraform

  1. 서비스 계정을 만들거나 선택하여 Pub/Sub 구독 ID를 나타냅니다.

    resource "google_service_account" "sa" {
      account_id   = "cloud-run-pubsub-invoker"
      display_name = "Cloud Run Pub/Sub Invoker"
    }
  2. 서비스 계정으로 Pub/Sub 구독을 만듭니다.

    1. 호출자 서비스 계정에 pubsub-tutorial 서비스 호출 권한을 부여합니다.

      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}"]
      }
    2. 프로젝트에서 인증 토큰을 만들도록 Pub/Sub를 허용합니다.

      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}"]
      }
    3. 서비스 계정으로 Pub/Sub 구독을 만듭니다.

      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]
      }

이제 서비스가 Pub/Sub와 완전히 통합되었습니다.

사용해 보기

엔드 투 엔드 솔루션을 테스트하려면 다음 안내를 따르세요.

  1. 주제에 Pub/Sub 메시지를 전송합니다.

    gcloud pubsub topics publish myRunTopic --message "Runner"

    또한 이 가이드에 표시된 것처럼 명령줄을 사용하는 대신 프로그래매틱 방식으로 메시지를 게시할 수도 있습니다. 자세한 내용은 메시지 게시를 참조하세요.

  2. 서비스 로그로 이동합니다.

    1. Google Cloud 콘솔로 이동합니다.
    2. pubsub-tutorial 서비스를 클릭합니다.
    3. 로그 탭을 선택합니다.

      로그가 나타나려면 시간이 약간 걸릴 수 있습니다. 즉시 표시되지 않으면 잠시 후 다시 확인하세요.

  3. 'Hello Runner!' 메시지를 찾습니다.