Pub/Sub 푸시에서 트리거

이 페이지에서는 Pub/Sub를 사용하여 Cloud Run 서비스의 엔드포인트로 메시지를 푸시하는 방법을 설명합니다. 그러면 메시지가 HTTP 요청으로 컨테이너에 전달됩니다. 이 페이지에서는 동일한 Google Cloud 프로젝트에 있는 Pub/Sub 구독으로부터 푸시된 메시지를 안전하게 처리하도록 서비스를 사용 설정하는 방법을 보여줍니다.

서비스 계정 및 IAM 권한을 사용하면 Cloud Run 서비스를 공개적으로 노출하지 않아도 Cloud Run에 Pub/Sub를 안전하게 비공개적으로 사용할 수 있습니다. 설정한 Pub/Sub 구독만 서비스를 호출할 수 있습니다.

Cloud Run의 확인 기한

Pub/Sub 구독 확인 기한(ackDeadlineSeconds)을 최대 600초로 설정해야 합니다. Cloud Run 서비스에서 600초 이내에 응답을 반환하여 Pub/Sub 메시지를 확인해야 합니다. 그렇지 않으면 Pub/Sub가 메시지를 다시 전송하므로 Cloud Run 서비스가 중복 트리거됩니다.

사용 사례

사용 가능한 사용 사례는 다음과 같습니다.

통합 개요

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

  • Pub/Sub 주제를 만듭니다.
  • Cloud Run 서비스에서 사용자가 만든 주제로 전송된 Pub/Sub 메시지에 응답하도록 코드를 추가합니다.
  • 필요한 권한이 있는 서비스 계정을 만듭니다.
  • Pub/Sub 구독을 만들고 이를 서비스 계정과 연결합니다. 이 구독은 주제에 게시되는 모든 메시지를 서비스로 전송합니다.

시작하기 전에

  • 아직 수행하지 않았으면 Cloud Run 설정 페이지의 설명대로 환경을 설정합니다.
  • 이 가이드에서는 Cloud Run 서비스가 이미 있고 Pub/Sub와 통합하는 코드를 추가한다고 가정합니다. 이러한 서비스가 없으면 이 페이지를 수행하는 대신 Pub/Sub에 대한 Cloud Run 튜토리얼을 사용하는 것이 좋습니다.

Pub/Sub의 메시지를 처리하도록 코드 추가

기존 서비스 코드를 수정하여 Pub/Sub를 지원하는 데 필요한 코드를 추가합니다. 서비스가 요청에서 메시지를 추출하고 예상된 성공 코드를 반환해야 합니다. 선택한 언어(모든 언어 사용 가능)에 대한 다음 스니펫은 간단한 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)
}

자바

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

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

위 Pub/Sub 코드로 업데이트한 후에 Cloud Run 서비스를 빌드한 후 배포합니다.

구독의 서비스 계정 만들기

Pub/Sub 구독과 연결할 서비스 계정을 만들고 여기에 Cloud Run 서비스 호출 권한을 부여해야 합니다. Cloud Run 서비스에 푸시되는 Pub/Sub 메시지에는 이 서비스 계정의 ID가 포함됩니다.

기존 서비스 계정을 사용하여 Pub/Sub 구독 ID를 나타내거나 새 항목을 만들 수 있습니다.

새 서비스 계정을 만들고 여기에 Cloud Run 서비스 호출 권한을 부여하려면 다음 안내를 따르세요.

콘솔

  1. Google Cloud 콘솔에서 서비스 계정 페이지로 이동합니다.

    서비스 계정으로 이동

  2. 프로젝트를 선택합니다.

  3. Google Cloud 콘솔에 표시할 서비스 계정 이름을 입력합니다.

    Google Cloud 콘솔에서 이 이름을 기반으로 서비스 계정 ID가 생성됩니다. 필요한 경우 ID를 수정합니다. 나중에 이 ID를 변경할 수 없습니다.

  4. 선택사항: 서비스 계정에 대한 설명을 입력합니다.

  5. 만들고 계속하기를 클릭합니다.

  6. 선택사항: 역할 선택 필드를 클릭합니다.

  7. Cloud Run > Cloud Run 호출자를 선택합니다.

  8. 완료를 클릭합니다.

명령줄

  1. 서비스 계정을 만듭니다.

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

    바꾸기

    • SERVICE_ACCOUNT_NAME을 Google Cloud 프로젝트 내에서 고유한 소문자 이름으로 바꿉니다(예: my-invoker-service-account-name).
    • DISPLAYED_SERVICE_ACCOUNT_NAME을 Console에서 이 서비스 계정에 표시하려는 이름으로 바꿉니다(예: My Invoker Service Account).
  2. Cloud Run의 경우 서비스를 호출할 수 있는 서비스 계정 권한을 부여합니다.

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

    다음과 같이 바꿉니다.

    • SERVICE를 Pub/Sub에서 호출할 서비스 이름으로 바꿉니다.
    • SERVICE_ACCOUNT_NAME을 서비스 계정의 이름으로 바꿉니다.
    • PROJECT_ID를 Google Cloud 프로젝트 ID로 바꿉니다.
  3. 프로젝트의 리소스에 대한 특정 작업을 완료할 수 있는 권한을 갖도록 이 서비스 계정에 프로젝트에 대한 액세스 권한을 부여합니다.

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

    바꾸기

    • RESOURCE_ID: Google Cloud 프로젝트 ID

    • PRINCIPAL: 일반적으로 PRINCIPAL_TYPE:ID 형식을 사용하는 주 구성원 또는 구성원의 식별자입니다. 예를 들면 user:my-user@example.com입니다. PRINCIPAL가 가질 수 있는 값의 전체 목록은 정책 binding 참조를 확인하세요.

Terraform

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

  1. 서비스 계정을 만들려면 기존 .tf 파일에 다음을 추가합니다.

    resource "google_service_account" "sa" {
      account_id   = "cloud-run-pubsub-invoker"
      display_name = "Cloud Run Pub/Sub Invoker"
    }
  2. 서비스 계정에 서비스 호출 권한을 부여하려면 기존 .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}"]
    }

Pub/Sub 주제 만들기

Pub/Sub 주제에 게시되는 메시지로 서비스 요청이 트리거되므로 주제를 만들어야 합니다.

콘솔

  1. Google Cloud 콘솔에서 Pub/Sub 주제 페이지로 이동합니다.

    Pub/Sub 주제 페이지

  2. 주제 만들기를 클릭합니다.

  3. 고유한 주제 이름을 입력합니다(예: MyTopic).

명령줄

gcloud pubsub topics create TOPIC-NAME

TOPIC-NAME을 Google Cloud 프로젝트 내에서 고유한 주제 이름으로 바꿉니다.

Terraform

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

이 섹션에서는 Terraform을 통해 Google Cloud Platform 제공업체google_pubsub_topic 리소스를 사용하여 Terraform 구성에서 서비스를 정의하는 방법을 설명합니다.

기존 .tf 파일에 다음 콘텐츠를 추가합니다.

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

푸시 구독을 만들고 서비스 계정과 연결

Pub/Sub 주제를 만든 후 주제로 전송되는 메시지를 수신하기 위해 서비스를 구독해야 하고, 서비스에 대해 만든 서비스 계정과 구독을 연결해야 합니다. 이를 위해서는 Google Cloud 콘솔 또는 gcloud 명령줄 모두 사용 가능합니다.

콘솔

  1. Pub/Sub 주제 페이지로 이동합니다.

    Pub/Sub 주제 페이지

  2. 구독하려는 주제를 클릭합니다.

  3. 구독 만들기를 클릭하여 구독 양식을 표시합니다.

    구독 양식

    다음 안내를 따라 양식을 작성하세요.

    1. 푸시 전송 유형을 지정합니다.
    2. 엔드포인트 URL에 대해 서비스 세부정보 페이지에 표시되는 서비스 URL을 지정합니다.
    3. 서비스 계정 드롭다운에서 필수 권한으로 만든 서비스 계정을 선택합니다.
    4. 구독 만료와 확인 기한을 600초로 설정합니다.
    5. 만들기를 클릭합니다.
  4. 구독이 완료되었습니다. 주제에 게시된 메시지가 이제 서비스에 푸시됩니다.

명령줄

  1. 프로젝트에서 인증 토큰을 만들도록 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 콘솔에서 프로젝트의 프로젝트 정보 패널에 나열됩니다.

  2. 필수 권한으로 만든 서비스 계정으로 Pub/Sub 구독을 만듭니다.

    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

    다음과 같이 바꿉니다.

    • TOPIC-NAME이전에 만든 주제로 바꿉니다.
    • SERVICE-URL을 서비스를 배포할 때 제공된 HTTPS URL로 바꿉니다. gcloud run services describe 명령어를 사용하고, 서비스 이름을 지정하여 찾을 수 있습니다. domain으로 시작하는 반환 줄을 찾습니다.
    • PROJECT-ID를 Google Cloud 프로젝트 ID로 바꿉니다.

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

    확인 기한은 최대 600초로 설정합니다.

  3. 구독이 완료되었습니다. 주제에 게시된 메시지가 이제 서비스에 푸시됩니다. 다음 명령어를 사용하여 주제에 테스트 메시지를 푸시할 수 있습니다.

    gcloud pubsub topics publish TOPIC --message "hello"

    TOPIC생성된 주제 이름으로 바꿉니다.

Terraform

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

  1. 프로젝트에서 인증 토큰을 만들도록 Pub/Sub를 허용합니다. .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. 필수 권한으로 만든 서비스 계정으로 Pub/Sub 구독을 만듭니다. .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. 구독이 완료되었습니다. 주제에 게시된 메시지가 이제 서비스에 푸시됩니다. 다음 명령어를 사용하여 주제에 테스트 메시지를 푸시할 수 있습니다.

    gcloud pubsub topics publish TOPIC --message "hello"

    TOPIC생성된 주제 이름으로 바꿉니다.

다음 단계