Cloud Functions (第 2 代) 使用入门

1. 简介

如需开始编写 Cloud Run 函数,您可以使用以下 Codelab:

否则,本 Codelab 将引导您逐步创建 Cloud Functions(第 2 代)函数。

概览

Cloud Functions(第 2 代)是 Google Cloud 函数即服务产品 Google Cloud Functions 的下一代版本。此新版本附带高级功能集,现在由 Cloud RunEventarc 提供支持,让您能够更高级地控制性能和可伸缩性,并更好地控制来自 90 多个事件来源的函数运行时和触发器。

本 Codelab 将引导您创建响应 HTTP 调用并由 Pub/Sub 消息和 Cloud 审核日志触发的 Cloud Functions 函数。

新变化

新版 Cloud Functions 提供由 Cloud Run、Cloud Build、Artifact Registry 和 Eventarc 提供支持的增强型 FaaS 体验。

增强型基础架构

  • 更长的请求处理时间:Cloud Functions 的运行时长可超过默认的 5 分钟,从而更轻松地运行长时间运行的请求工作负载,例如处理来自 Cloud Storage 或 BigQuery 的大量数据。对于 HTTP 函数,此时间最长为 60 分钟。对于事件驱动型函数,此时间目前最长为 10 分钟。
  • 更大的实例:利用 Cloud Functions 上最高 16 GB RAM 和 4 个 vCPU,允许更大的内存中工作负载、计算密集型工作负载和更多并行工作负载。
  • 并发:使用单个函数实例处理多达 1000 个并发请求,从而最大限度地减少冷启动并缩短扩缩时的延迟时间。
  • 实例数下限:提供预热实例,从而减少冷启动,并确保应用的引导时间不会影响应用性能。
  • 流量拆分:支持函数的多个版本,可以在不同的版本之间拆分流量,还可以将函数回滚到先前的版本。

更广泛的事件覆盖范围及 CloudEvents 支持

  • Eventarc 集成:Cloud Functions 现在支持 Eventarc,可使用 Cloud Audit Logs(BigQuery、Cloud SQL、Cloud Storage 等)提供超过 90 个事件来源。当然,Cloud Functions 仍然支持通过直接发布到 Cloud Pub/Sub 来处理来自自定义来源的事件。
  • CloudEvent 格式:所有事件驱动型函数(无论来源如何)均遵循业界标准 CloudEvents ( cloudevents.io),从而确保一致的开发者体验。载荷通过带有 cloudevent.data 载荷的结构化 CloudEvent 发送,并实现 CloudEvent 标准。

学习内容

  • Cloud Functions(第 2 代)概览。
  • 如何编写响应 HTTP 调用的函数。
  • 如何编写响应 Pub/Sub 消息的函数。
  • 如何编写用于响应 Cloud Storage 事件的函数。
  • 如何编写响应 Cloud Audit Logs 的函数。
  • 如何在两个修订版本之间拆分流量。
  • 如何通过实例数下限来消除冷启动。
  • 如何设置并发。

2. 设置和要求

自定进度的环境设置

  1. 登录 Google Cloud 控制台,然后创建一个新项目或重复使用现有项目。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • 项目名称是此项目参与者的显示名称。它是 Google API 尚未使用的字符串。您可以随时更新。
  • 项目 ID 在所有 Google Cloud 项目中必须是唯一的,并且不可变(一经设置便无法更改)。Cloud 控制台会自动生成一个唯一字符串;通常情况下,您无需关注该字符串。在大多数 Codelab 中,您都需要引用项目 ID(通常用 PROJECT_ID 标识)。如果您不喜欢生成的 ID,可以再随机生成一个 ID。或者,您也可以尝试自己的项目 ID,看看是否可用。完成此步骤后便无法更改该 ID,并且此 ID 在项目期间会一直保留。
  • 此外,还有第三个值,即部分 API 使用的项目编号,供您参考。如需详细了解所有这三个值,请参阅文档
  1. 接下来,您需要在 Cloud 控制台中启用结算功能,以便使用 Cloud 资源/API。运行此 Codelab 应该不会产生太多的费用(如果有费用的话)。若要关闭资源以避免产生超出本教程范围的结算费用,您可以删除自己创建的资源或删除整个项目。Google Cloud 的新用户符合参与 $300 USD 免费试用计划的条件。

启动 Cloud Shell

虽然可以通过笔记本电脑对 Google Cloud 进行远程操作,但在此 Codelab 中,您将使用 Google Cloud Shell,这是一个在云端运行的命令行环境。

Google Cloud 控制台 中,点击右上角工具栏中的 Cloud Shell 图标:

55efc1aaa7a4d3ad.png

预配和连接到环境应该只需要片刻时间。完成后,您应该会看到如下内容:

7ffe5cbb04455448.png

这个虚拟机已加载了您需要的所有开发工具。它提供了一个持久的 5 GB 主目录,并且在 Google Cloud 中运行,大大增强了网络性能和身份验证功能。您在此 Codelab 中的所有工作都可以在浏览器中完成。您无需安装任何程序。

设置 gcloud

在 Cloud Shell 中,确保您的项目 ID 已设置并保存到 PROJECT_ID 变量,并且 REGION 已设置为 us-west1

gcloud config set project [YOUR-PROJECT-ID]
PROJECT_ID=$(gcloud config get-value project)
REGION=us-west1

启用 API

启用所有必要的服务:

gcloud services enable \
  artifactregistry.googleapis.com \
  cloudfunctions.googleapis.com \
  cloudbuild.googleapis.com \
  eventarc.googleapis.com \
  run.googleapis.com \
  logging.googleapis.com \
  pubsub.googleapis.com

3. HTTP 函数

对于第一个函数,我们将创建一个用于响应 HTTP 请求的经过身份验证的 Node.js 函数。我们还将使用 10 分钟的超时时间来展示函数如何有更多时间响应 HTTP 请求。

创建

为应用创建一个文件夹并导航到该文件夹:

mkdir ~/hello-http && cd $_

创建一个仅响应 HTTP 请求的 index.js 文件:

const functions = require('@google-cloud/functions-framework');

functions.http('helloWorld', (req, res) => {
  res.status(200).send('HTTP with Node.js in GCF 2nd gen!');
});

创建一个 package.json 文件来指定依赖项:

{
  "name": "nodejs-functions-gen2-codelab",
  "version": "0.0.1",
  "main": "index.js",
  "dependencies": {
    "@google-cloud/functions-framework": "^2.0.0"
  }
}

部署

部署函数的方法如下:

gcloud functions deploy nodejs-http-function \
  --gen2 \
  --runtime nodejs16 \
  --entry-point helloWorld \
  --source . \
  --region $REGION \
  --trigger-http \
  --timeout 600s

虽然此步骤不是绝对必要的,但请注意超时为 600 秒。这样,函数就有更长的超时时间来响应 HTTP 请求。

部署函数后,您可以在 Cloud 控制台的“Cloud Functions”部分下看到它:

7541800e1e3f299f.png

测试

使用以下命令测试函数:

gcloud functions call nodejs-http-function \
  --gen2 --region $REGION

您应该会看到 HTTP with Node.js in GCF 2nd gen! 作为响应的消息。

4. Pub/Sub 函数

对于第二个函数,我们将创建一个由发布到特定主题的 Pub/Sub 消息触发的 Python 函数。

设置 Pub/Sub 身份验证令牌

如果您在 2021 年 4 月 8 日当天或之前启用了 Pub/Sub 服务账号,请将 iam.serviceAccountTokenCreator 角色授予 Pub/Sub 服务账号:

PROJECT_NUMBER=$(gcloud projects list --filter="project_id:$PROJECT_ID" --format='value(project_number)')

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 主题:

TOPIC=cloud-functions-gen2-topic
gcloud pubsub topics create $TOPIC

为应用创建一个文件夹并导航到该文件夹:

mkdir ~/hello-pubsub && cd $_

创建一个仅记录包含 CloudEvent ID 的消息的 main.py 文件:

import functions_framework

@functions_framework.cloud_event
def hello_pubsub(cloud_event):
   print('Pub/Sub with Python in GCF 2nd gen! Id: ' + cloud_event['id'])

创建一个包含以下内容的 requirements.txt 文件,以指定依赖项:

functions-framework==3.*

部署

部署函数的方法如下:

gcloud functions deploy python-pubsub-function \
  --gen2 \
  --runtime python39 \
  --entry-point hello_pubsub \
  --source . \
  --region $REGION \
  --trigger-topic $TOPIC

函数部署后,您可以在 Cloud 控制台中的“Cloud Functions 函数”部分下看到该函数:

107029714c32baff.png

测试

向该主题发送消息以测试函数:

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

您应该会在日志中看到已接收到的 CloudEvent:

gcloud functions logs read python-pubsub-function \
  --region $REGION --gen2 --format "value(log)"

5. Cloud Storage 函数

对于下一个函数,我们将创建一个 Node.js 函数来响应来自 Cloud Storage 存储分区的事件。

设置

如需使用 Cloud Storage 函数,请将 pubsub.publisher IAM 角色授予 Cloud Storage 服务账号:

SERVICE_ACCOUNT=$(gsutil kms serviceaccount -p $PROJECT_NUMBER)

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member serviceAccount:$SERVICE_ACCOUNT \
  --role roles/pubsub.publisher

创建

为应用创建一个文件夹并导航到该文件夹:

mkdir ~/hello-storage && cd $_

创建一个仅响应 Cloud Storage 事件的 index.js 文件:

const functions = require('@google-cloud/functions-framework');

functions.cloudEvent('helloStorage', (cloudevent) => {
  console.log('Cloud Storage event with Node.js in GCF 2nd gen!');
  console.log(cloudevent);
});

创建一个 package.json 文件来指定依赖项:

{
  "name": "nodejs-functions-gen2-codelab",
  "version": "0.0.1",
  "main": "index.js",
  "dependencies": {
    "@google-cloud/functions-framework": "^2.0.0"
  }
}

部署

首先,创建一个 Cloud Storage 存储分区(或使用您现有的存储分区):

​​export BUCKET="gs://gcf-gen2-storage-$PROJECT_ID"
gsutil mb -l $REGION $BUCKET

部署函数的方法如下:

gcloud functions deploy nodejs-storage-function \
  --gen2 \
  --runtime nodejs16 \
  --entry-point helloStorage \
  --source . \
  --region $REGION \
  --trigger-bucket $BUCKET \
  --trigger-location $REGION

函数部署完毕后,您可以在 Cloud 控制台中的“Cloud Functions 函数”部分下看到该函数。

测试

通过将文件上传到存储分区来测试函数:

echo "Hello World" > random.txt
gsutil cp random.txt $BUCKET/random.txt

您应该会在日志中看到已接收到的 CloudEvent:

gcloud functions logs read nodejs-storage-function \
  --region $REGION --gen2 --limit=100 --format "value(log)"

6. Cloud Audit Logs 函数

对于下一个函数,我们将创建一个 Node.js 函数,以便在创建 Compute Engine 虚拟机实例时接收 Cloud Audit Log 事件。作为回应,它会向新创建的虚拟机添加标签,指定虚拟机的创建者。

确定新创建的 Compute Engine 虚拟机

创建虚拟机时,Compute Engine 会发出 2 个审核日志。

第一个代码会在虚拟机创建开始时发出,如下所示:

8d394a481644c4b6.png

第二个事件会在创建虚拟机后发出,如下所示:

ee0e221d82887cd1.png

请注意包含 first: truelast: true 值的 operation 字段。第二个审核日志包含为实例添加标签所需的所有信息,因此我们将使用 last: true 标志在 Cloud Functions 中检测该日志。

设置

如需使用 Cloud Audit Log 函数,您必须为 Eventarc 启用审核日志。您还需要使用具有 eventarc.eventReceiver 角色的服务账号。

  1. 为 Compute Engine API 启用 Cloud Audit Logs 管理员读取数据读取数据写入日志类型:

76b7417ea4071241.png

  1. 向默认 Compute Engine 服务账号授予 eventarc.eventReceiver IAM 角色:
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \
  --role roles/eventarc.eventReceiver

获取代码

克隆包含应用的代码库:

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

前往应用目录:

cd eventarc-samples/gce-vm-labeler/gcf/nodejs

index.js 文件包含用于接收封装在 CloudEvent 中的审核日志的应用代码。然后,该脚本会提取 Compute Engine 虚拟机实例详细信息,并在虚拟机实例上设置标签。您可以自行详细研究 index.js

部署

您可以像以前一样使用 gcloud 部署函数。请注意该函数如何过滤包含 --trigger-event-filters 标志的 Compute Engine 插入操作的审核日志:

gcloud functions deploy gce-vm-labeler \
  --gen2 \
  --runtime nodejs16 \
  --entry-point labelVmCreation \
  --source . \
  --region $REGION \
  --trigger-event-filters="type=google.cloud.audit.log.v1.written,serviceName=compute.googleapis.com,methodName=beta.compute.instances.insert" \
  --trigger-location us-central1

您还可以通过 Google Cloud 控制台部署该函数并添加 Eventarc 触发器。

首先,前往 Cloud Functions 部分,然后在第 2 代环境中创建一个函数:

8ba79a12ee152d8

点击 Add Eventarc Trigger 按钮:

655346320a5e3631.png

这会在右侧打开一个边栏,您可以在其中为 Eventarc 触发器选择不同的事件提供程序和事件。

选择正确的事件提供方和事件,然后点击 Save Trigger

7d24325ff06c9b05

最后,在下一页上,您可以使用 GitHub 上的 index.jspackage.json 文件更新 index.jspackage.json 文件,然后点击 Deploy 按钮:

f2e338eed2ccf5a2.png

测试

如需测试审核日志函数,您需要在 Cloud 控制台中创建一个 Compute Engine 虚拟机(您也可以使用 gcloud 创建虚拟机,但它似乎不会生成审核日志)。

转到 Cloud 控制台的 Compute Engine > 虚拟机实例部分,然后创建一个新的虚拟机。虚拟机创建完成后,您应该会在 Cloud 控制台中的基本信息部分或使用以下命令看到虚拟机上添加的 creator 标签:

gcloud compute instances describe YOUR_VM_NAME

您应该会在输出中看到标签,如以下示例所示:

...
labelFingerprint: ULU6pAy2C7s=
labels:
  creator: atameldev
...

7. 流量拆分

Cloud Functions(第 2 代)支持函数的多个修订版本,可在不同的修订版本之间分配流量,还可将函数回滚到先前版本。之所以能够这样,是因为第 2 代函数在底层是 Cloud Run 服务。

在此步骤中,您将部署一个函数的 2 个修订版本,然后在它们之间按 50-50 的比例拆分流量。

创建

为应用创建一个文件夹,然后打开该文件夹:

mkdir ~/traffic-splitting && cd $_

创建一个 main.py 文件,并在其中添加一个 Python 函数,用于读取颜色环境变量,并以相应背景颜色的 Hello World 进行响应:

import os

color = os.environ.get('COLOR')

def hello_world(request):
    return f'<body style="background-color:{color}"><h1>Hello World!</h1></body>'

部署

部署背景为橙色的函数的第一个修订版本:

COLOR=orange
gcloud functions deploy hello-world-colored \
  --gen2 \
  --runtime python39 \
  --entry-point hello_world \
  --source . \
  --region $REGION \
  --trigger-http \
  --allow-unauthenticated \
  --update-env-vars COLOR=$COLOR

此时,如果您通过在浏览器中查看 HTTP 触发器(上述部署命令的 URI 输出)来测试函数,则应该会看到背景为橙色的 Hello World

36ca0c5f39cc89cf.png

部署背景为黄色的第二个修订版本:

COLOR=yellow
gcloud functions deploy hello-world-colored \
  --gen2 \
  --runtime python39 \
  --entry-point hello_world \
  --source . \
  --region $REGION \
  --trigger-http \
  --allow-unauthenticated \
  --update-env-vars COLOR=$COLOR

由于这是最新修订版,因此如果您测试该函数,应该会看到背景为黄色的 Hello World

391286a08ad3cdde.png

按 50-50 的比例分配流量

如需在橙色和黄色修订版本之间拆分流量,您需要查找底层 Cloud Run 服务的修订版本 ID。以下是用于查看修订 ID 的命令:

gcloud run revisions list --service hello-world-colored \
  --region $REGION --format 'value(REVISION)'

输出应类似如下所示:

hello-world-colored-00001-man
hello-world-colored-00002-wok

现在,按如下所示在两个修订版本之间拆分流量(请根据您的修订版本名称更新 X-XXX):

gcloud run services update-traffic hello-world-colored \
  --region $REGION \
  --to-revisions hello-world-colored-0000X-XXX=50,hello-world-colored-0000X-XXX=50

测试

通过访问函数的公开网址对其进行测试。您应该会有一半的时间看到橙色修订版本,另一半时间看到黄色修订版本:

36ca0c5f39cc89cf.png 391286a08ad3cdde.png

如需了解详情,请参阅回滚、逐步发布和流量迁移

8. 实例数下限

在 Cloud Functions(第 2 代)中,您可以指定保持备用状态并准备好处理请求的函数实例数下限。这对于限制冷启动的次数非常有用。

在此步骤中,您将部署一个初始化缓慢的函数。您会观察到冷启动问题。然后,您将最小实例值设置为 1 来部署该函数,以消除冷启动。

创建

为应用创建一个文件夹,并打开该文件夹:

mkdir ~/min-instances && cd $_

创建 main.go 文件,此 Go 服务有一个 init 函数,该函数会休眠 10 秒以模拟较长的初始化。它还包含一个用于响应 HTTP 调用的 HelloWorld 函数:

package p

import (
        "fmt"
        "net/http"
        "time"
)

func init() {
        time.Sleep(10 * time.Second)
}

func HelloWorld(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, "Slow HTTP Go in GCF 2nd gen!")
}

部署

部署函数的第一个修订版本,并将默认最小实例值为零:

gcloud functions deploy slow-function \
  --gen2 \
  --runtime go116 \
  --entry-point HelloWorld \
  --source . \
  --region $REGION \
  --trigger-http \
  --allow-unauthenticated

使用以下命令测试函数:

gcloud functions call slow-function \
  --gen2 --region $REGION

您会发现在第一次调用时会延迟 10 秒(冷启动),然后会看到消息。后续调用应立即返回。

设置实例数下限

如需消除首次请求时的冷启动,请将 --min-instances 标志设置为 1,然后重新部署函数,如下所示:

gcloud functions deploy slow-function \
  --gen2 \
  --runtime go116 \
  --entry-point HelloWorld \
  --source . \
  --region $REGION \
  --trigger-http \
  --allow-unauthenticated \
  --min-instances 1

测试

再次测试函数:

gcloud functions call slow-function \
  --gen2 --region $REGION

您应该不会再在第一个请求中看到 10 秒的延迟。得益于实例数下限,首次调用(经过很长时间没有调用)的冷启动问题已不存在!

如需了解详情,请参阅使用实例数下限

9. 并发

在 Cloud Functions(第 2 代)中,函数实例默认处理 1 个并发请求,但您可以指定实例可以同时处理的并发请求数量。这对于防止冷启动也很有用,因为无需为每个并行请求创建新的函数实例。

在此步骤中,您将使用上一步中初始化缓慢的函数。您将向其发送 10 个请求,并再次观察冷启动问题,因为需要创建新的函数实例来处理这些请求。

为了解决冷启动问题,您将部署另一个并发值为 100 的函数。您会发现,这 10 个请求现在不会导致冷启动问题,并且单个函数实例可以处理所有请求。

不使用并发进行测试

获取函数的网址:

SLOW_URL=$(gcloud functions describe slow-function --region $REGION --gen2 --format="value(serviceConfig.uri)")

使用名为 hey 的开源基准测试工具向运行缓慢的函数发送 10 个并发请求。hey 已安装到 Cloud Shell 中:

hey -n 10 -c 10 $SLOW_URL

您应该会在 hey 的输出中看到某些请求花费的时间较长:

Summary:
  Total:        10.9053 secs
  Slowest:      10.9048 secs
  Fastest:      0.4439 secs
  Average:      9.7930 secs
  Requests/sec: 0.9170

  Total data:   310 bytes
  Size/request: 31 bytes

Response time histogram:
  0.444 [1]     |■■■■
  1.490 [0]     |
  2.536 [0]     |
  3.582 [0]     |
  4.628 [0]     |
  5.674 [0]     |
  6.720 [0]     |
  7.767 [0]     |
  8.813 [0]     |
  9.859 [0]     |
  10.905 [9]    |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

这是因为正在创建更多函数实例来处理请求。如果您检查了该函数的活跃实例数,应该还会发现在某个时间点创建了多个实例,这些实例导致了冷启动问题:

9f5c6877836d62fb.png

部署

部署与上一个函数完全相同的新函数。部署完成后,您需要提高其并发性:

gcloud functions deploy slow-concurrent-function \
  --gen2 \
  --runtime go116 \
  --entry-point HelloWorld \
  --source . \
  --region $REGION \
  --trigger-http \
  --allow-unauthenticated \
  --min-instances 1

设置并发

将函数底层 Cloud Run 服务的并发请求数设置为 100(最多可设置为 1000)。这可确保单个函数实例可以处理至少 100 个请求:

gcloud run services update slow-concurrent-function \
  --concurrency 100 \
  --cpu 1 \
  --region $REGION 

使用并发进行测试

获取函数的网址:

SLOW_CONCURRENT_URL=$(gcloud functions describe slow-concurrent-function --region $REGION --gen2 --format="value(serviceConfig.uri)")

然后,使用 hey 发送 10 个并发请求:

hey -n 10 -c 10 $SLOW_CONCURRENT_URL

您应该会在 hey 的输出中看到所有请求都已快速处理:

Summary:
  Total:        0.2164 secs
  Slowest:      0.2163 secs
  Fastest:      0.0921 secs
  Average:      0.2033 secs
  Requests/sec: 46.2028

  Total data:   310 bytes
  Size/request: 31 bytes

Response time histogram:
  0.092 [1]     |■■■■
  0.105 [0]     |
  0.117 [0]     |
  0.129 [0]     |
  0.142 [0]     |
  0.154 [0]     |
  0.167 [0]     |
  0.179 [0]     |
  0.191 [0]     |
  0.204 [0]     |
  0.216 [9]     |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

由于并发性提高,单个函数实例就能够处理所有请求,冷启动问题也随之消失!

如需了解详情,请参阅并发

10. 恭喜!

恭喜您完成此 Codelab!

所学内容

  • Cloud Functions(第 2 代)概览。
  • 如何编写响应 HTTP 调用的函数。
  • 如何编写响应 Pub/Sub 消息的函数。
  • 如何编写用于响应 Cloud Storage 事件的函数。
  • 如何编写响应 Cloud Audit Logs 的函数。
  • 如何在两个修订版本之间拆分流量。
  • 如何通过实例数下限来消除冷启动。
  • 如何设置并发。