使用 Eventarc 触发 Cloud Storage 中的函数

本教程介绍如何在 Cloud Run 中部署事件驱动函数,并使用 Eventarc 通过 Google Cloud CLI 触发该函数以响应 Cloud Storage 事件。

通过为 Eventarc 触发器指定过滤条件,您可以配置事件的路由,包括事件来源和事件目标。对于本教程中的示例,对 Cloud Storage 存储桶的更新会触发事件,系统会以 HTTP 请求的形式向函数发送请求。

设置所需角色

您或您的管理员必须为部署者账号、触发器身份以及可选的 Pub/Sub 服务代理和 Cloud Storage 服务代理授予以下 IAM 角色。

部署者账号所需的角色

  1. 如果您是项目创建者,则会被授予基本 Owner 角色 (roles/owner)。默认情况下,此 Identity and Access Management (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. 默认情况下,只有 Project Owner、Project Editor 以及 Cloud Run Admin 和 Invoker 才能调用 Cloud Run 服务。您可以按服务控制访问权限;但是,出于测试目的,请向 Compute Engine 服务账号授予 Google Cloud 项目的 Cloud Run Invoker 角色 (run.invoker)。此操作会授予项目中所有 Cloud Run 服务和作业的角色。
    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member=serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com \
        --role=roles/run.invoker

    请注意,如果您在未授予 Cloud Run Invoker 角色的情况下为经过身份验证的 Cloud Run 服务创建触发器,则触发器会成功创建且处于活动状态。但是,触发器将无法按预期运行,并且日志中会显示类似于以下内容的消息:

    The request was not authenticated. Either allow unauthenticated invocations or set the proper Authorization header.
  3. 将项目的 Eventarc Event Receiver 角色 (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 的直接事件创建触发器之前,请向 Cloud Storage 服务代理授予 Pub/Sub Publisher 角色 (roles/pubsub.publisher):

    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 推送请求,请向该服务代理授予 Service Account Token Creator 角色 (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

创建 Cloud Storage 存储桶

创建 Cloud Storage 存储桶以用作事件来源:

gcloud storage buckets create -l us-central1 gs://PROJECT_ID-bucket/

编写事件驱动型函数

如需编写事件驱动型函数,请按照以下步骤操作:

Node.js

  1. 创建名为 helloGCS 的新目录,并转到此目录中:

       mkdir helloGCS
       cd helloGCS
    

  2. helloGCS 目录中创建一个 package.json 文件以指定 Node.js 依赖项:

    {
      "name": "nodejs-docs-samples-functions-v2-storage",
      "version": "0.0.1",
      "private": true,
      "license": "Apache-2.0",
      "author": "Google LLC",
      "repository": {
        "type": "git",
        "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
      },
      "engines": {
        "node": ">=16.0.0"
      },
      "scripts": {
        "test": "c8 mocha -p -j 2 test/*.test.js --timeout=60000"
      },
      "dependencies": {
        "@google-cloud/functions-framework": "^3.0.0"
      },
      "devDependencies": {
        "c8": "^10.0.0",
        "mocha": "^10.0.0",
        "sinon": "^18.0.0",
        "supertest": "^7.0.0"
      }
    }
    
  3. 使用以下 Node.js 示例在 helloGCS 目录中创建一个 index.js 文件:

    const functions = require('@google-cloud/functions-framework');
    
    // Register a CloudEvent callback with the Functions Framework that will
    // be triggered by Cloud Storage.
    functions.cloudEvent('helloGCS', cloudEvent => {
      console.log(`Event ID: ${cloudEvent.id}`);
      console.log(`Event Type: ${cloudEvent.type}`);
    
      const file = cloudEvent.data;
      console.log(`Bucket: ${file.bucket}`);
      console.log(`File: ${file.name}`);
      console.log(`Metageneration: ${file.metageneration}`);
      console.log(`Created: ${file.timeCreated}`);
      console.log(`Updated: ${file.updated}`);
    });

Python

  1. 创建名为 helloGCS 的新目录,并转到此目录中:

       mkdir helloGCS
       cd helloGCS
    

  2. helloGCS 目录中创建一个 requirements.txt 文件,以指定 Python 依赖项:

    functions-framework==3.9.2
    cloudevents==1.11.0

    这会添加示例所需的软件包。

  3. 使用以下 Python 示例在 helloGCS 目录中创建一个 main.py 文件:

    from cloudevents.http import CloudEvent
    
    import functions_framework
    
    
    # Triggered by a change in a storage bucket
    @functions_framework.cloud_event
    def hello_gcs(cloud_event: CloudEvent) -> tuple:
        """This function is triggered by a change in a storage bucket.
    
        Args:
            cloud_event: The CloudEvent that triggered this function.
        Returns:
            The event ID, event type, bucket, name, metageneration, and timeCreated.
        """
        data = cloud_event.data
    
        event_id = cloud_event["id"]
        event_type = cloud_event["type"]
    
        bucket = data["bucket"]
        name = data["name"]
        metageneration = data["metageneration"]
        timeCreated = data["timeCreated"]
        updated = data["updated"]
    
        print(f"Event ID: {event_id}")
        print(f"Event type: {event_type}")
        print(f"Bucket: {bucket}")
        print(f"File: {name}")
        print(f"Metageneration: {metageneration}")
        print(f"Created: {timeCreated}")
        print(f"Updated: {updated}")
    
        return event_id, event_type, bucket, name, metageneration, timeCreated, updated
    
    

部署事件驱动的函数

通过在包含示例代码的目录中运行以下命令来部署名为 helloworld-events 的函数:

Node.js

gcloud run deploy helloworld-events \
      --source . \
      --function helloGCS \
      --base-image BASE_IMAGE \
      --region us-central1

BASE_IMAGE 替换为函数所用的基础映像环境,例如 nodejs22。如需详细了解基础映像以及每个映像中包含的软件包,请参阅支持的语言运行时和基础映像

Python

gcloud run deploy helloworld-events \
      --source . \
      --function hello_gcs \
      --base-image BASE_IMAGE \
      --region us-central1

BASE_IMAGE 替换为函数所用的基础映像环境,例如 python313。如需详细了解基础映像以及每个映像中包含的软件包,请参阅支持的语言运行时和基础映像

部署完成后,Google Cloud CLI 会显示运行服务的网址。

创建 Eventarc 触发器

Eventarc 触发器会将 Cloud Storage 存储桶中的事件发送到 helloworld-events Cloud Run 服务。

  1. 创建一个过滤 Cloud Storage 事件的触发器:

    gcloud eventarc triggers create TRIGGER_NAME  \
        --location=${REGION} \
        --destination-run-service=helloworld-events  \
        --destination-run-region=${REGION} \
        --event-filters="type=google.cloud.storage.object.v1.finalized" \
        --event-filters="bucket=PROJECT_ID-bucket" \
        --service-account=PROJECT_NUMBER-compute@developer.gserviceaccount.com

    您需要进行如下替换:

    • TRIGGER_NAME 替换为触发器的名称。
    • PROJECT_ID 替换为您的 Google Cloud 项目 ID。
    • PROJECT_NUMBER 替换为您的 Google Cloud 项目编号。

    请注意,首次在 Google Cloud 项目中创建 Eventarc 触发器时,预配 Eventarc 服务代理可能会有延迟。通常,您可以尝试再次创建触发器,以解决此问题。如需了解详情,请参阅权限遭拒错误

  2. 确认触发器已成功创建。请注意,尽管触发器会立即创建,但它最长可能需要两分钟才能完全正常运行。

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

    输出应类似如下所示:

    NAME: helloworld-events
    TYPE: google.cloud.storage.object.v1.finalized
    DESTINATION: Cloud Run service: helloworld-events
    ACTIVE: Yes
    LOCATION: us-central1
    

生成并查看事件

将文本文件上传到 Cloud Storage 存储桶以生成路由到函数的事件。Cloud Run 函数会在服务日志中记录事件。

  1. 将文本文件上传到 Cloud Storage 以生成事件:

     echo "Hello World" > random.txt
     gcloud storage cp random.txt gs://PROJECT_ID-bucket/random.txt
    

    上传操作会生成事件,而 Cloud Run 函数会记录事件的消息。

  2. 如需查看日志条目,请执行以下操作:

    1. 过滤日志条目并以 JSON 格式返回输出:

      gcloud logging read "resource.labels.service_name=helloworld-events AND textPayload:random.txt" --format=json
      
    2. 查找类似如下的日志条目:

      [
        {
         ....
          "resource": {
            "labels": {
              ....
              "location": "us-central1",
              .....
              "service_name": "helloworld-events"
            },
          },
          "textPayload": "File: random.txt",
           .....
        }
      ]
      

      您可能需要等待一些时间才能看到日志。如果您没有立即看到日志,请稍等片刻,然后再检查一次。

您看到日志条目后,即表示您已成功部署了在文本文件上传到 Cloud Storage 时触发的事件驱动函数。