1. 简介
概览
Cloud Run functions 是 Google Cloud 的函数即服务产品,由 Cloud Run 和 Eventarc 提供支持,可让您对性能和可伸缩性进行更高级的控制,并更好地控制函数运行时和来自 90 多个事件来源的触发器。
此 Codelab 将引导您创建可响应 HTTP 调用并由 Pub/Sub 消息和 Cloud 审核日志触发的 Cloud Run 函数。
此 Codelab 还通过使用 --base-image 标志指定基础映像,为函数部署使用自动基础映像更新。通过为 Cloud Run 配置自动基础映像更新,使 Google 能够自动为基础映像的操作系统和语言运行时组件进行安全补丁。您无需重新构建或重新部署服务即可更新基础映像。如需了解详情,请参阅自动基础映像更新
如果您不想使用自动基础映像更新,可以从本 Codelab 中显示的示例中移除 --base-image 标志。
学习内容
- Cloud Run functions 概览以及如何使用自动基础映像更新。
- 如何编写响应 HTTP 调用请求的函数。
- 如何编写响应 Pub/Sub 消息的函数。
- 如何编写响应 Cloud Storage 事件的函数。
- 如何在两个修订版本之间拆分流量。
- 如何通过设置实例数下限来避免冷启动。
2. 设置和要求
创建根文件夹
为所有示例创建一个根文件夹。
mkdir crf-codelab cd crf-codelab
设置环境变量
设置将在整个 Codelab 中使用的环境变量
gcloud config set project <YOUR-PROJECT-ID> REGION=<YOUR_REGION> PROJECT_ID=$(gcloud config get-value project)
启用 API
启用所有必要的服务:
gcloud services enable \ artifactregistry.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 hello-http
创建可响应 HTTP 请求的 index.js 文件:
const functions = require('@google-cloud/functions-framework');
functions.http('helloWorld', (req, res) => {
res.status(200).send('HTTP with Node.js in Cloud Run functions!');
});
创建一个 package.json 文件以指定依赖项:
{
"name": "nodejs-run-functions-codelab",
"version": "0.0.1",
"main": "index.js",
"dependencies": {
"@google-cloud/functions-framework": "^2.0.0"
}
}
部署
部署函数的方法如下:
gcloud run deploy nodejs-run-function \
--source . \
--function helloWorld \
--base-image nodejs22 \
--region $REGION \
--timeout 600 \
--no-allow-unauthenticated
此命令使用 buildpack 将函数源代码转换为可用于生产用途的容器映像。
请注意以下事项:
--source标志用于告知 Cloud Run 将函数构建为可运行的基于容器的服务--function标志(新)用于将新服务的入口点设置为您希望调用的函数签名--base-image标志(新增)用于指定函数的基础映像环境,例如nodejs22、python312、go123、java21、dotnet8、ruby33或php83。如需详细了解基础映像以及每个映像中包含的软件包,请参阅运行时基础映像。- (可选)
--timeout标志允许函数具有更长的超时时间来响应 HTTP 请求。在此示例中,我们使用 600 秒来演示 10 分钟的响应时间。 - (可选)
--no-allow-unauthenticated,以防止您的函数被公开调用
测试
使用以下命令测试函数:
# get the Service URL SERVICE_URL="$(gcloud run services describe nodejs-run-function --region $REGION --format 'value(status.url)')" # invoke the service curl -H "Authorization: bearer $(gcloud auth print-identity-token)" -X GET $SERVICE_URL
您应该会看到 HTTP with Node.js in Cloud Run functions! 消息作为响应。
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-run-functions-pubsub-topic gcloud pubsub topics create $TOPIC
为应用创建一个文件夹,然后前往该文件夹:
mkdir ../hello-pubsub cd ../hello-pubsub
创建一个 main.py 文件,用于记录包含 CloudEvent ID 的消息:
import functions_framework
@functions_framework.cloud_event
def hello_pubsub(cloud_event):
print('Pub/Sub with Python in Cloud Run functions! Id: ' + cloud_event['id'])
创建一个包含以下内容的 requirements.txt 文件,以指定依赖项:
functions-framework==3.*
部署
部署函数的方法如下:
gcloud run deploy python-pubsub-function \
--source . \
--function hello_pubsub \
--base-image python313 \
--region $REGION \
--no-allow-unauthenticated
检索将用于服务账号身份的项目编号。
PROJECT_NUMBER=$(gcloud projects list --filter="project_id:$PROJECT_ID" --format='value(project_number)')
创建触发器
gcloud eventarc triggers create python-pubsub-function-trigger \
--location=$REGION \
--destination-run-service=python-pubsub-function \
--destination-run-region=$REGION \
--event-filters="type=google.cloud.pubsub.topic.v1.messagePublished" \
--transport-topic=projects/$PROJECT_ID/topics/$TOPIC \
--service-account=$PROJECT_NUMBER-compute@developer.gserviceaccount.com
测试
通过向主题发送消息来测试函数:
gcloud pubsub topics publish $TOPIC --message="Hello World"
您应该会在日志中看到已接收到的 CloudEvent:
gcloud run services logs read python-pubsub-function --region $REGION --limit=10
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 ../hello-storage
创建一个仅响应 Cloud Storage 事件的 index.js 文件:
const functions = require('@google-cloud/functions-framework');
functions.cloudEvent('helloStorage', (cloudevent) => {
console.log('Cloud Storage event with Node.js in Cloud Run functions!');
console.log(cloudevent);
});
创建一个 package.json 文件以指定依赖项:
{
"name": "nodejs-crf-cloud-storage",
"version": "0.0.1",
"main": "index.js",
"dependencies": {
"@google-cloud/functions-framework": "^2.0.0"
}
}
部署
首先,创建 Cloud Storage 存储分区(或使用您已有的现有存储分区):
export BUCKET_NAME="gcf-storage-$PROJECT_ID" export BUCKET="gs://gcf-storage-$PROJECT_ID" gsutil mb -l $REGION $BUCKET
部署函数的方法如下:
gcloud run deploy nodejs-crf-cloud-storage \ --source . \ --base-image nodejs22 \ --function helloStorage \ --region $REGION \ --no-allow-unauthenticated
部署函数后,您可以在 Cloud 控制台的 Cloud Run 部分下看到该函数。
现在,创建 Eventarc 触发器。
BUCKET_REGION=$REGION gcloud eventarc triggers create nodejs-crf-cloud-storage-trigger \ --location=$BUCKET_REGION \ --destination-run-service=nodejs-crf-cloud-storage \ --destination-run-region=$REGION \ --event-filters="type=google.cloud.storage.object.v1.finalized" \ --event-filters="bucket=$BUCKET_NAME" \ --service-account=$PROJECT_NUMBER-compute@developer.gserviceaccount.com
测试
通过将文件上传到存储分区来测试函数:
echo "Hello World" > random.txt gsutil cp random.txt $BUCKET/random.txt
您应该会在日志中看到已接收到的 CloudEvent:
gcloud run services logs read nodejs-crf-cloud-storage --region $REGION --limit=10
6. Cloud Audit Logs
对于下一个函数,我们来创建一个 Node.js 函数,该函数会在创建 Compute Engine 虚拟机实例时接收 Cloud Audit Log 事件。作为响应,它会向新创建的虚拟机添加一个标签,用于指定虚拟机的创建者。
确定新创建的 Compute Engine 虚拟机
创建虚拟机时,Compute Engine 会发出 2 个审核日志。
第一个是在虚拟机创建开始时发出的。第二个是在创建虚拟机后发出的。
在审核日志中,操作字段不同,包含 first: true 和 last: true 值。第二个审核日志包含标记实例所需的所有信息,因此我们将使用 last: true 标志在 Cloud Run 函数中检测到它。
设置
如需使用 Cloud Audit Log 功能,您必须为 Eventarc 启用审核日志。您还需要使用具有 eventarc.eventReceiver 角色的服务账号。
- 为 Compute Engine API 启用 Cloud Audit Logs 管理员读取、数据读取和数据写入日志类型。
- 向默认 Compute Engine 服务账号授予
eventarc.eventReceiverIAM 角色:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \ --role roles/eventarc.eventReceiver
创建函数
此 Codelab 使用 Node.js,但您可以在 https://github.com/GoogleCloudPlatform/eventarc-samples 找到其他示例
创建 package.json 文件
{
"dependencies": {
"googleapis": "^84.0.0"
}
}
创建 node.js 文件
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
const { google } = require("googleapis");
var compute = google.compute("v1");
exports.labelVmCreation = async (cloudevent) => {
const data = cloudevent.body;
// in case an event has >1 audit log
// make sure we respond to the last event
if (!data.operation || !data.operation.last) {
console.log("Operation is not last, skipping event");
return;
}
// projects/dogfood-gcf-saraford/zones/us-central1-a/instances/instance-1
var resourceName = data.protoPayload.resourceName;
var resourceParts = resourceName.split("/");
var project = resourceParts[1];
var zone = resourceParts[3];
var instanceName = resourceParts[5];
var username = data.protoPayload.authenticationInfo.principalEmail.split("@")[0];
console.log(`Setting label username: ${username} to instance ${instanceName} for zone ${zone}`);
var authClient = await google.auth.getClient({
scopes: ["https://www.googleapis.com/auth/cloud-platform"]
});
// per docs: When updating or adding labels in the API,
// you need to provide the latest labels fingerprint with your request,
// to prevent any conflicts with other requests.
var labelFingerprint = await getInstanceLabelFingerprint(authClient, project, zone, instanceName);
var responseStatus = await setVmLabel(
authClient,
labelFingerprint,
username,
project,
zone,
instanceName
);
// log results of setting VM label
console.log(JSON.stringify(responseStatus, null, 2));
};
async function getInstanceLabelFingerprint(authClient, project, zone, instanceName) {
var request = {
project: project,
zone: zone,
instance: instanceName,
auth: authClient
};
var response = await compute.instances.get(request);
var labelFingerprint = response.data.labelFingerprint;
return labelFingerprint;
}
async function setVmLabel(authClient, labelFingerprint, username, project, zone, instanceName) {
var request = {
project: project,
zone: zone,
instance: instanceName,
resource: {
labels: { "creator": username },
labelFingerprint: labelFingerprint
},
auth: authClient
};
var response = await compute.instances.setLabels(request);
return response.statusText;
}
部署
部署函数的方法如下:
gcloud run deploy gce-vm-labeler \ --source . \ --function labelVmCreation \ --region $REGION \ --no-allow-unauthenticated
现在,创建触发器。请注意,该函数如何使用 --trigger-event-filters 标志过滤 Compute Engine 插入的审核日志。
gcloud eventarc triggers create gce-vm-labeler-trigger \ --location=$REGION \ --destination-run-service=gce-vm-labeler \ --destination-run-region=$REGION \ --event-filters="type=google.cloud.audit.log.v1.written,serviceName=compute.googleapis.com,methodName=v1.compute.instances.insert" \ --service-account=$ROJECT_NUMBER-compute@developer.gserviceaccount.com
测试
设置环境变量:
# if you're using europe-west1 as your region
ZONE=europe-west1-d
VM_NAME=codelab-crf-auditlog
运行以下命令可创建虚拟机:
gcloud compute instances create $VM_NAME --zone=$ZONE --machine-type=e2-medium --image-family=debian-11 --image-project=debian-cloud
虚拟机创建完成后,您应该会在 Cloud 控制台的基本信息部分或使用以下命令看到添加的 creator 标签:
gcloud compute instances describe $VM_NAME --zone=$ZONE
您应该会在输出中看到标签,如以下示例所示:
... labelFingerprint: ULU6pAy2C7s= labels: creator: atameldev ...
清理
请务必删除虚拟机实例。在本实验中,您将不会再使用该变量。
gcloud compute instances delete $VM_NAME --zone=$ZONE
7. 流量分配
Cloud Run functions 支持函数的多个修订版本,让您可以在不同的修订版本之间分配流量,还可以将函数回滚到先前版本。
在此步骤中,您将部署函数的 2 个修订版本,然后在它们之间按 50-50 的比例拆分流量。
创建
为应用创建一个文件夹,然后前往该文件夹:
mkdir ../traffic-splitting cd ../traffic-splitting
创建一个 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>'
创建一个包含以下内容的 requirements.txt 文件,以指定依赖项:
functions-framework==3.*
部署
部署第一个修订版本的函数(采用橙色背景):
COLOR=orange gcloud run deploy hello-world-colors \ --source . \ --base-image python313 \ --function hello_world \ --region $REGION \ --allow-unauthenticated \ --update-env-vars COLOR=$COLOR
此时,如果您在浏览器中查看 HTTP 触发器(上述部署命令的 URI 输出),测试该函数,则应会看到带有橙色背景的 Hello World:

部署具有黄色背景的第二个修订版本:
COLOR=yellow gcloud run deploy hello-world-colors \ --source . \ --base-image python313 \ --function hello_world \ --region $REGION \ --allow-unauthenticated \ --update-env-vars COLOR=$COLOR
由于这是最新修订版本,因此如果您测试该函数,应该会看到带有黄色背景的 Hello World:

将流量平均分配
如需在橙色修订版本和黄色修订版本之间拆分流量,您需要找到 Cloud Run 服务的修订版本 ID。以下是查看修订版本 ID 的命令:
gcloud run revisions list --service hello-world-colors \ --region $REGION --format 'value(REVISION)'
输出应类似如下所示:
hello-world-colors-00001-man hello-world-colors-00002-wok
现在,按如下方式在这两个修订版本之间拆分流量(请根据您的修订版本名称更新 X-XXX):
gcloud run services update-traffic hello-world-colors \ --region $REGION \ --to-revisions hello-world-colors-0000X-XXX=50,hello-world-colors-0000X-XXX=50
测试
访问函数的公共网址,以测试该函数。一半的时间,您应该会看到橙色修订版本;另一半时间,您应该会看到黄色修订版本:

如需了解详情,请参阅回滚、逐步发布和流量迁移。
8. 实例数下限
在 Cloud Run functions 中,您可以指定要保持备用状态并且准备处理请求的函数实例数下限。这对于限制冷启动的次数非常有用。
在此步骤中,您将部署一个初始化速度较慢的函数。您会发现冷启动问题。然后,您将部署该函数,并将最小实例值设置为 1,以消除冷启动。
创建
为应用创建一个文件夹,然后导航到该文件夹:
mkdir ../min-instances cd ../min-instances
创建 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 Cloud Run functions!")
}
部署
使用默认的最小实例值 0 部署函数的第一个修订版本:
gcloud run deploy go-slow-function \ --source . \ --base-image go123 \ --function HelloWorld \ --region $REGION \ --no-allow-unauthenticated
使用以下命令测试函数:
# get the Service URL SERVICE_URL="$(gcloud run services describe go-slow-function --region $REGION --format 'value(status.url)')" # invoke the service curl -H "Authorization: bearer $(gcloud auth print-identity-token)" -X GET $SERVICE_URL
您会发现,第一次调用时有 10 秒的延迟(冷启动),然后才会看到消息。后续调用应立即返回。
设置实例数下限
如需避免在首次请求时出现冷启动,请重新部署函数,并将 --min-instances 标志设置为 1,如下所示:
gcloud run deploy go-slow-function \ --source . \ --base-image go123 \ --function HelloWorld \ --region $REGION \ --no-allow-unauthenticated \ --min-instances 1
测试
再次测试该函数:
curl -H "Authorization: bearer $(gcloud auth print-identity-token)" -X GET $SERVICE_URL
您应该不会再看到第一个请求出现 10 秒的延迟。由于设置了实例数下限,首次调用(在长时间未调用后)的冷启动问题已得到解决!
如需了解详情,请参阅使用最少的实例。
9. 恭喜!
恭喜您完成此 Codelab!
所学内容
- Cloud Run functions 概览以及如何使用自动基础映像更新。
- 如何编写响应 HTTP 调用请求的函数。
- 如何编写响应 Pub/Sub 消息的函数。
- 如何编写响应 Cloud Storage 事件的函数。
- 如何在两个修订版本之间拆分流量。
- 如何通过设置实例数下限来避免冷启动。