1. 简介
如需开始编写 Cloud Run 函数,您可以使用以下 Codelab:
否则,本 Codelab 将引导您逐步创建 Cloud Functions(第 2 代)函数。
概览
Cloud Functions(第 2 代)是 Google Cloud 函数即服务产品 Google Cloud Functions 的下一代版本。此新版本附带高级功能集,现在由 Cloud Run 和 Eventarc 提供支持,让您能够更高级地控制性能和可伸缩性,并更好地控制来自 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. 设置和要求
自定进度的环境设置
- 登录 Google Cloud 控制台,然后创建一个新项目或重复使用现有项目。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个。
- 项目名称是此项目参与者的显示名称。它是 Google API 尚未使用的字符串。您可以随时更新。
- 项目 ID 在所有 Google Cloud 项目中必须是唯一的,并且不可变(一经设置便无法更改)。Cloud 控制台会自动生成一个唯一字符串;通常情况下,您无需关注该字符串。在大多数 Codelab 中,您都需要引用项目 ID(通常用
PROJECT_ID
标识)。如果您不喜欢生成的 ID,可以再随机生成一个 ID。或者,您也可以尝试自己的项目 ID,看看是否可用。完成此步骤后便无法更改该 ID,并且此 ID 在项目期间会一直保留。 - 此外,还有第三个值,即部分 API 使用的项目编号,供您参考。如需详细了解所有这三个值,请参阅文档。
- 接下来,您需要在 Cloud 控制台中启用结算功能,以便使用 Cloud 资源/API。运行此 Codelab 应该不会产生太多的费用(如果有费用的话)。若要关闭资源以避免产生超出本教程范围的结算费用,您可以删除自己创建的资源或删除整个项目。Google Cloud 的新用户符合参与 $300 USD 免费试用计划的条件。
启动 Cloud Shell
虽然可以通过笔记本电脑对 Google Cloud 进行远程操作,但在此 Codelab 中,您将使用 Google Cloud Shell,这是一个在云端运行的命令行环境。
在 Google Cloud 控制台 中,点击右上角工具栏中的 Cloud Shell 图标:
预配和连接到环境应该只需要片刻时间。完成后,您应该会看到如下内容:
这个虚拟机已加载了您需要的所有开发工具。它提供了一个持久的 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”部分下看到它:
测试
使用以下命令测试函数:
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 函数”部分下看到该函数:
测试
向该主题发送消息以测试函数:
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 个审核日志。
第一个代码会在虚拟机创建开始时发出,如下所示:
第二个事件会在创建虚拟机后发出,如下所示:
请注意包含 first: true
和 last: true
值的 operation 字段。第二个审核日志包含为实例添加标签所需的所有信息,因此我们将使用 last: true
标志在 Cloud Functions 中检测该日志。
设置
如需使用 Cloud Audit Log 函数,您必须为 Eventarc 启用审核日志。您还需要使用具有 eventarc.eventReceiver
角色的服务账号。
- 为 Compute Engine API 启用 Cloud Audit Logs 管理员读取、数据读取和数据写入日志类型:
- 向默认 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 代环境中创建一个函数:
点击 Add Eventarc Trigger
按钮:
这会在右侧打开一个边栏,您可以在其中为 Eventarc 触发器选择不同的事件提供程序和事件。
选择正确的事件提供方和事件,然后点击 Save Trigger
:
最后,在下一页上,您可以使用 GitHub 上的 index.js
和 package.json
文件更新 index.js
和 package.json
文件,然后点击 Deploy
按钮:
测试
如需测试审核日志函数,您需要在 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
:
部署背景为黄色的第二个修订版本:
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
:
按 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
测试
通过访问函数的公开网址对其进行测试。您应该会有一半的时间看到橙色修订版本,另一半时间看到黄色修订版本:
如需了解详情,请参阅回滚、逐步发布和流量迁移。
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] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
这是因为正在创建更多函数实例来处理请求。如果您检查了该函数的活跃实例数,应该还会发现在某个时间点创建了多个实例,这些实例导致了冷启动问题:
部署
部署与上一个函数完全相同的新函数。部署完成后,您需要提高其并发性:
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 的函数。
- 如何在两个修订版本之间拆分流量。
- 如何通过实例数下限来消除冷启动。
- 如何设置并发。