使用 Eventarc 從 Pub/Sub 觸發函式


本教學課程將示範如何使用 Pub/Sub 觸發條件編寫及觸發以事件為準的 Cloud Run 函式。

您可以為 Eventarc 觸發條件指定篩選器,藉此設定事件路由,包括事件來源和事件目標。在本教學課程的範例中,將訊息發布至 Pub/Sub 主題會觸發事件,並以 HTTP 要求的形式將要求傳送至函式。

如果您是 Pub/Sub 新手,且想進一步瞭解相關資訊,請參閱 Pub/Sub 說明文件,瞭解快速入門課程和重要參考資料。

目標

在這個教學課程中,您將執行下列操作:

  1. 部署事件導向函式
  2. 建立 Eventarc 觸發條件
  3. 將訊息發布至 Pub/Sub 主題,藉此觸發函式

費用

在本文件中,您會使用 Google Cloud的下列計費元件:

您可以使用 Pricing Calculator 根據預測用量產生預估費用。 新 Google Cloud 使用者可能符合申請免費試用的資格。

事前準備

貴機構定義的安全性限制,可能會導致您無法完成下列步驟。如需疑難排解資訊,請參閱「在受限的 Google Cloud 環境中開發應用程式」。

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. Install the Google Cloud CLI.

  3. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  4. To initialize the gcloud CLI, run the following command:

    gcloud init
  5. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  6. Make sure that billing is enabled for your Google Cloud project.

  7. Install the Google Cloud CLI.

  8. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  9. To initialize the gcloud CLI, run the following command:

    gcloud init
  10. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  11. Make sure that billing is enabled for your Google Cloud project.

  12. 如果您未使用 Cloud Shell,請更新 Google Cloud CLI 元件,並使用您的帳戶登入:
    gcloud components update
    gcloud auth login
  13. 啟用 API:
    gcloud services enable artifactregistry.googleapis.com \
        cloudbuild.googleapis.com \
        eventarc.googleapis.com \
        run.googleapis.com \
        logging.googleapis.com \
        pubsub.googleapis.com
  14. 設定本教學課程中使用的設定變數:
    export REGION=us-central1
    gcloud config set run/region ${REGION}
    gcloud config set run/platform managed
    gcloud config set eventarc/location ${REGION}
  15. 建立服務帳戶:
    SERVICE_ACCOUNT=eventarc-trigger-sa
    
    gcloud iam service-accounts create $SERVICE_ACCOUNT
  16. 如果您適用網域限制組織政策,且該政策限制專案的未經驗證叫用,您就必須按照「測試私人服務」一節的說明存取已部署的服務。

必要的角色

您或管理員必須為部署者帳戶、觸發器身分,以及 Pub/Sub 服務代理人和 Cloud Storage 服務代理人授予下列 IAM 角色 (選用)。

部署者帳戶的必要角色

  1. 如果您是專案建立者,系統會授予您基本擁有者角色 (roles/owner)。根據預設,這個身分與存取權管理 (IAM) 角色會包含完整存取大部分 Google Cloud資源所需的權限,因此您可以略過這個步驟。

    如果您不是專案建立者,則必須將必要權限授予專案中的適當實體。舉例來說,主體可以是 Google 帳戶 (適用於使用者),也可以是服務帳戶 (適用於應用程式和運算工作負載)。詳情請參閱活動目的地的角色和權限頁面。

    如要取得完成本教學課程所需的權限,請要求管理員為您授予專案的下列 IAM 角色:

    如要進一步瞭解如何授予角色,請參閱「管理專案、資料夾和機構的存取權」。

    您或許還可透過自訂角色或其他預先定義的角色取得必要權限。

    請注意,根據預設,Cloud Build 權限包含上傳及下載 Artifact Registry 構件的權限。

觸發條件身分的必要角色

  1. 請記下 Compute Engine 預設服務帳戶,因為您會將其附加至 Eventarc 觸發事件,以便代表觸發事件的身分,用於測試用途。啟用或使用使用 Compute Engine 的 Google Cloud 服務後,系統會自動建立這個服務帳戶,且電子郵件格式如下:

    PROJECT_NUMBER-compute@developer.gserviceaccount.com

    PROJECT_NUMBER 替換為您的 Google Cloud專案編號。您可以在 Google Cloud 控制台的「歡迎」頁面上找到專案編號,也可以執行下列指令:

    gcloud projects describe PROJECT_ID --format='value(projectNumber)'

    對於實際環境,我們強烈建議建立新的服務帳戶,並授予一或多個包含必要最低權限的 IAM 角色,並遵循最低權限原則。

  2. 根據預設,只有專案擁有者、專案編輯者,以及 Cloud Run 管理員和叫用者可以叫用 Cloud Run 服務。您可以依服務控制存取權,但為了測試,請將 Google Cloud 專案的 Cloud Run 叫用者角色 (run.invoker) 授予 Compute Engine 服務帳戶。這樣一來,系統就會為專案中的所有 Cloud Run 服務和工作授予這個角色。
    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member=serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com \
        --role=roles/run.invoker

    請注意,如果您為經過驗證的 Cloud Run 服務建立觸發條件,但未授予 Cloud Run 叫用者角色,系統會成功建立觸發條件並啟用。不過,觸發條件無法正常運作,而且記錄中會顯示類似以下的訊息:

    The request was not authenticated. Either allow unauthenticated invocations or set the proper Authorization header.
  3. 將專案的 Eventarc 事件接收者角色 (roles/eventarc.eventReceiver) 授予 Compute Engine 預設服務帳戶,以便 Eventarc 觸發條件接收事件供應者的事件。
    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member=serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com \
        --role=roles/eventarc.eventReceiver

Cloud Storage 服務代理的選用角色

  • 為 Cloud Storage 的直接事件建立觸發條件之前,請先將 Pub/Sub 發布者角色 (roles/pubsub.publisher) 授予 Cloud Storage 服務代理:

    SERVICE_ACCOUNT="$(gcloud storage service-agent --project=PROJECT_ID)"
    
    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member="serviceAccount:${SERVICE_ACCOUNT}" \
        --role='roles/pubsub.publisher'

Pub/Sub 服務代理人的選用角色

  • 如果您是在 2021 年 4 月 8 日當天或之前啟用 Cloud Pub/Sub 服務代理,以便支援經過驗證的 Pub/Sub 推送要求,請將 服務帳戶權杖建立者角色 (roles/iam.serviceAccountTokenCreator) 授予服務代理。否則,系統會預設授予這個角色:
    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member=serviceAccount:service-PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com \
        --role=roles/iam.serviceAccountTokenCreator

建立 Pub/Sub 主題

在 Cloud Run 中,部署函式時不會自動建立 Pub/Sub 主題。在部署函式之前,請將訊息發布至這個 Pub/Sub 主題,藉此觸發函式:

gcloud pubsub topics create YOUR_TOPIC_NAME

準備應用程式

  1. 將應用程式存放區範例複製到本機電腦中:

    Node.js

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

    Python

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

    Go

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

    Java

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

    .NET

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

    Ruby

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

    PHP

    git clone https://github.com/GoogleCloudPlatform/php-docs-samples.git
    
  2. 變更為包含 Pub/Sub 存取範例程式碼的目錄:

    Node.js

    cd nodejs-docs-samples/functions/v2/helloPubSub/
    

    Python

    cd python-docs-samples/functions/v2/pubsub/
    

    Go

    cd golang-samples/functions/functionsv2/hellopubsub/
    

    Java

    cd java-docs-samples/functions/v2/pubsub/
    

    .NET

    cd dotnet-docs-samples/functions/helloworld/HelloPubSub/
    

    Ruby

    cd ruby-docs-samples/functions/helloworld/pubsub/
    

    PHP

    cd php-docs-samples/functions/helloworld_pubsub/
    
  3. 查看程式碼範例:

    Node.js

    const functions = require('@google-cloud/functions-framework');
    
    // Register a CloudEvent callback with the Functions Framework that will
    // be executed when the Pub/Sub trigger topic receives a message.
    functions.cloudEvent('helloPubSub', cloudEvent => {
      // The Pub/Sub message is passed as the CloudEvent's data payload.
      const base64name = cloudEvent.data.message.data;
    
      const name = base64name
        ? Buffer.from(base64name, 'base64').toString()
        : 'World';
    
      console.log(`Hello, ${name}!`);
    });

    Python

    import base64
    
    from cloudevents.http import CloudEvent
    import functions_framework
    
    
    # Triggered from a message on a Cloud Pub/Sub topic.
    @functions_framework.cloud_event
    def subscribe(cloud_event: CloudEvent) -> None:
        # Print out the data from Pub/Sub, to prove that it worked
        print(
            "Hello, " + base64.b64decode(cloud_event.data["message"]["data"]).decode() + "!"
        )
    
    

    Go

    
    // Package helloworld provides a set of Cloud Functions samples.
    package helloworld
    
    import (
    	"context"
    	"fmt"
    	"log"
    
    	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
    	"github.com/cloudevents/sdk-go/v2/event"
    )
    
    func init() {
    	functions.CloudEvent("HelloPubSub", helloPubSub)
    }
    
    // MessagePublishedData contains the full Pub/Sub message
    // See the documentation for more details:
    // https://cloud.google.com/eventarc/docs/cloudevents#pubsub
    type MessagePublishedData struct {
    	Message PubSubMessage
    }
    
    // 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 {
    	Data []byte `json:"data"`
    }
    
    // helloPubSub consumes a CloudEvent message and extracts the Pub/Sub message.
    func helloPubSub(ctx context.Context, e event.Event) error {
    	var msg MessagePublishedData
    	if err := e.DataAs(&msg); err != nil {
    		return fmt.Errorf("event.DataAs: %w", err)
    	}
    
    	name := string(msg.Message.Data) // Automatically decoded from base64.
    	if name == "" {
    		name = "World"
    	}
    	log.Printf("Hello, %s!", name)
    	return nil
    }
    

    Java

    import com.google.cloud.functions.CloudEventsFunction;
    import com.google.gson.Gson;
    import functions.eventpojos.PubSubBody;
    import io.cloudevents.CloudEvent;
    import java.nio.charset.StandardCharsets;
    import java.util.Base64;
    import java.util.logging.Logger;
    
    public class SubscribeToTopic implements CloudEventsFunction {
      private static final Logger logger = Logger.getLogger(SubscribeToTopic.class.getName());
    
      @Override
      public void accept(CloudEvent event) {
        // The Pub/Sub message is passed as the CloudEvent's data payload.
        if (event.getData() != null) {
          // Extract Cloud Event data and convert to PubSubBody
          String cloudEventData = new String(event.getData().toBytes(), StandardCharsets.UTF_8);
          Gson gson = new Gson();
          PubSubBody body = gson.fromJson(cloudEventData, PubSubBody.class);
          // Retrieve and decode PubSub message data
          String encodedData = body.getMessage().getData();
          String decodedData =
              new String(Base64.getDecoder().decode(encodedData), StandardCharsets.UTF_8);
          logger.info("Hello, " + decodedData + "!");
        }
      }
    }

    .NET

    using CloudNative.CloudEvents;
    using Google.Cloud.Functions.Framework;
    using Google.Events.Protobuf.Cloud.PubSub.V1;
    using Microsoft.Extensions.Logging;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace HelloPubSub;
    
    public class Function : ICloudEventFunction<MessagePublishedData>
    {
        private readonly ILogger _logger;
    
        public Function(ILogger<Function> logger) =>
            _logger = logger;
    
        public Task HandleAsync(CloudEvent cloudEvent, MessagePublishedData data, CancellationToken cancellationToken)
        {
            string nameFromMessage = data.Message?.TextData;
            string name = string.IsNullOrEmpty(nameFromMessage) ? "world" : nameFromMessage;
            _logger.LogInformation("Hello {name}", name);
            return Task.CompletedTask;
        }
    }

    Ruby

    require "functions_framework"
    require "base64"
    
    FunctionsFramework.cloud_event "hello_pubsub" do |event|
      # The event parameter is a CloudEvents::Event::V1 object.
      # See https://cloudevents.github.io/sdk-ruby/latest/CloudEvents/Event/V1.html
      name = Base64.decode64 event.data["message"]["data"] rescue "World"
    
      # A cloud_event function does not return a response, but you can log messages
      # or cause side effects such as sending additional events.
      logger.info "Hello, #{name}!"
    end

    PHP

    
    use CloudEvents\V1\CloudEventInterface;
    use Google\CloudFunctions\FunctionsFramework;
    
    // Register the function with Functions Framework.
    // This enables omitting the `FUNCTIONS_SIGNATURE_TYPE=cloudevent` environment
    // variable when deploying. The `FUNCTION_TARGET` environment variable should
    // match the first parameter.
    FunctionsFramework::cloudEvent('helloworldPubsub', 'helloworldPubsub');
    
    function helloworldPubsub(CloudEventInterface $event): void
    {
        $log = fopen(getenv('LOGGER_OUTPUT') ?: 'php://stderr', 'wb');
    
        $cloudEventData = $event->getData();
        $pubSubData = base64_decode($cloudEventData['message']['data']);
    
        $name = $pubSubData ? htmlspecialchars($pubSubData) : 'World';
        fwrite($log, "Hello, $name!" . PHP_EOL);
    }

部署事件導向函式

如要部署函式,請在包含範例程式碼的目錄中執行下列指令:

Node.js

  gcloud run deploy FUNCTION \
        --source . \
        --function helloPubSub \
        --base-image BASE_IMAGE \

取代:

  • FUNCTION 改為您要部署的函式名稱。如果省略這個參數,系統會在您執行指令時提示您輸入名稱。
  • BASE_IMAGE 搭配函式的基礎映像檔環境,例如 nodejs22。如要進一步瞭解基礎映像檔和各個映像檔所包含的套件,請參閱「支援的語言執行階段和基礎映像檔」。

Python

  gcloud run deploy FUNCTION \
        --source . \
        --function subscribe \
        --base-image BASE_IMAGE \

取代:

  • FUNCTION 改為您要部署的函式名稱。如果省略這個參數,系統會在您執行指令時提示您輸入名稱。
  • BASE_IMAGE 搭配函式的基礎映像檔環境,例如 python313。如要進一步瞭解基礎映像檔和各個映像檔所包含的套件,請參閱「支援的語言執行階段和基礎映像檔」。

Go

  gcloud run deploy FUNCTION \
        --source . \
        --function HelloPubSub \
        --base-image BASE_IMAGE \

取代:

  • FUNCTION 替換為您要部署的函式名稱。如果省略這個參數,系統會在您執行指令時提示您輸入名稱。
  • BASE_IMAGE 搭配函式的基礎映像檔環境,例如 go123。如要進一步瞭解基礎映像檔和各個映像檔所包含的套件,請參閱「支援的語言執行階段和基礎映像檔」。

Java

  gcloud run deploy FUNCTION \
        --source . \
        --function functions.SubscribeToTopic \
        --base-image BASE_IMAGE \

取代:

  • FUNCTION 改為您要部署的函式名稱。如果省略這個參數,系統會在您執行指令時提示您輸入名稱。
  • BASE_IMAGE 搭配函式的基礎映像檔環境,例如 java21。如要進一步瞭解基礎映像檔和各個映像檔所包含的套件,請參閱「支援的語言執行階段和基礎映像檔」。

.NET

  gcloud run deploy FUNCTION \
        --source . \
        --function HelloPubSub.Function \
        --base-image BASE_IMAGE \

取代:

  • FUNCTION 替換為您要部署的函式名稱。如果省略這個參數,系統會在您執行指令時提示您輸入名稱。
  • BASE_IMAGE 搭配函式的基礎映像檔環境,例如 dotnet8。如要進一步瞭解基礎映像檔和各個映像檔所包含的套件,請參閱「支援的語言執行階段和基礎映像檔」。

Ruby

  gcloud run deploy FUNCTION \
        --source . \
        --function hello_pubsub \
        --base-image BASE_IMAGE \

取代:

  • FUNCTION 替換為您要部署的函式名稱。如果省略這個參數,系統會在您執行指令時提示您輸入名稱。
  • BASE_IMAGE 搭配函式的基礎映像檔環境,例如 ruby34。如要進一步瞭解基礎映像檔和各個映像檔所包含的套件,請參閱「支援的語言執行階段和基礎映像檔」。

PHP

  gcloud run deploy FUNCTION \
        --source . \
        --function helloworldPubsub \
        --base-image BASE_IMAGE \

取代:

  • FUNCTION 改為您要部署的函式名稱。如果省略這個參數,系統會在您執行指令時提示您輸入名稱。
  • BASE_IMAGE 搭配函式的基礎映像檔環境,例如 php84。如要進一步瞭解基礎映像檔和各個映像檔所包含的套件,請參閱「支援的語言執行階段和基礎映像檔」。

如果系統提示您在指定區域建立存放區,請按下 y 回應。部署完成後,Google Cloud CLI 會顯示服務執行的網址。

建立 Eventarc 觸發條件

如要使用 Pub/Sub 觸發條件部署函式,請在包含程式碼範例的目錄中執行下列指令:

  1. 建立 Eventarc Pub/Sub 觸發條件:

    gcloud eventarc triggers create TRIGGER_NAME  \
        --location=${REGION} \
        --destination-run-service=FUNCTION \
        --destination-run-region=${REGION} \
        --event-filters="type=google.cloud.pubsub.topic.v1.messagePublished" \
        --service-account=PROJECT_NUMBER-compute@developer.gserviceaccount.com

    取代:

    • TRIGGER_NAME 替換為觸發條件的名稱。
    • FUNCTION 替換為函式名稱。
    • PROJECT_NUMBER 換成您的 Google Cloud 專案編號。

    請注意,在 Google Cloud 專案中首次建立 Eventarc 觸發條件時,Eventarc 服務代理程式可能會延遲佈建。您通常可以嘗試重新建立觸發條件來解決這個問題。詳情請參閱權限遭拒錯誤

  2. 確認觸發條件是否已成功建立。請注意,雖然觸發條件會立即建立,但觸發條件最多可能需要兩分鐘才能完全運作。

    gcloud eventarc triggers list --location=${REGION}

    畫面會顯示如下的輸出內容:

    NAME: helloworld-events
    TYPE: google.cloud.pubsub.topic.v1.messagePublished
    DESTINATION: Cloud Run service: helloworld-events
    ACTIVE: Yes
    LOCATION: us-central1
    

觸發函式

如要測試 Pub/Sub 函式,請按照下列步驟操作:

  1. 將主題指派給變數:

    TOPIC_ID=$(gcloud eventarc triggers describe TRIGGER_NAME --location $REGION --format='value(transport.pubsub.topic)')
    
  2. 將訊息發布到主題:

    gcloud pubsub topics publish $TOPIC_ID --message="Hello World"
    

Cloud Run 服務會記錄傳入訊息的內容。您可以在 Cloud Run 執行個體的「Logs」(記錄) 部分查看這項資訊:

  1. 前往 Google Cloud 控制台
  2. 按一下函式。
  3. 選取 [Logs] (記錄) 分頁標籤。

    記錄需要一些時間才會出現。如果您沒有立即看到記錄,請稍候片刻再查看一次。

  4. 尋找「Hello World!」訊息。

清除所用資源

如果您是為了這個教學課程建立新專案,請刪除專案。如果您使用現有的專案,且想保留該專案而不採用本教學課程中的變更,請刪除為教學課程建立的資源

刪除專案

如要避免付費,最簡單的方法就是刪除您為了本教學課程所建立的專案。

如要刪除專案:

  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.

刪除教學課程資源

  1. 刪除您在本教學課程中部署的 Cloud Run 服務:

    gcloud run services delete SERVICE_NAME

    其中 SERVICE_NAME 是您選擇的服務名稱。

    您也可以從 Google Cloud 控制台刪除 Cloud Run 服務。

  2. 移除您在教學課程設定期間新增的所有 gcloud CLI 預設設定。

    例如:

    gcloud config unset run/region

    gcloud config unset project

  3. 刪除本教學課程中建立的其他 Google Cloud 資源:

    • 刪除 Eventarc 觸發條件:
      gcloud eventarc triggers delete TRIGGER_NAME
      
      請將 TRIGGER_NAME 替換為觸發條件名稱。

後續步驟