1. 簡介
總覽
Cloud Run functions 是 Google Cloud 的函式即服務 (FaaS) 產品,採用 Cloud Run 和 Eventarc,可讓您進一步掌控效能和擴充性,並加強控管函式執行階段和 90 多個事件來源的觸發條件。
本程式碼研究室將逐步說明如何建立 Cloud Run 函式,這些函式可回應 HTTP 呼叫,並由 Pub/Sub 訊息和 Cloud 稽核記錄觸發。
本程式碼研究室也會使用 --base-image 旗標指定基本映像檔,為函式部署作業啟用自動基本映像檔更新。啟用 Cloud Run 的基礎映像檔自動更新功能後,Google 就能自動修補基礎映像檔的作業系統和語言執行階段元件,您不必重建或重新部署服務,即可更新基礎映像檔。詳情請參閱基底映像檔自動更新
如果您不想使用自動更新基礎映像檔功能,可以從本程式碼研究室範例中移除 --base-image 旗標。
課程內容
- Cloud Run functions 簡介,以及如何使用基礎映像檔自動更新。
- 如何編寫會回應 HTTP 呼叫的函式。
- 如何編寫會回應 Pub/Sub 訊息的函式。
- 如何編寫會回應 Cloud Storage 事件的函式。
- 如何在兩個修訂版本之間拆分流量。
- 如何設定執行個體數量下限,避免冷啟動發生。
2. 設定和需求
建立根資料夾
為所有範例建立根資料夾。
mkdir crf-codelab cd crf-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 函式
首先,請建立經過驗證的 Node.js 函式來回應 HTTP 要求。我們也將逾時設為 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
這項指令會使用 buildpacks,將函式原始碼轉換為可立即用於實際工作環境的容器映像檔。
請注意下列事項:
--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 bucket 的事件。
設定
如要使用 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
建立 index.js 檔案,回應 Cloud Storage 事件:
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 bucket (或使用現有 bucket):
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
測試
將檔案上傳至 bucket 來測試函式:
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 稽核記錄
接下來,我們將建立 Node.js 函式,在 Compute Engine VM 執行個體建立時接收 Cloud 稽核記錄事件。該函式會回應事件,為新建立的 VM 加上標籤,註明 VM 的建立者。
判斷新建立的 Compute Engine VM
每當 VM 建立時,Compute Engine 會產生兩個稽核記錄。
第一個是在 VM 開始建立時產生,第二個是在 VM 建立後產生。
在稽核記錄中,作業欄位不同,包含 first: true 和 last: true 值。第二個稽核記錄包含為執行個體加上標籤所需的全部資訊,因此我們將使用 last: true 旗標,在 Cloud Run functions 偵測該記錄。
設定
您必須為 Eventarc 啟用稽核記錄,並使用具備 eventarc.eventReceiver 角色的服務帳戶,才能使用 Cloud 稽核記錄函式。
- 啟用 Cloud 稽核記錄的 Admin Read、Data Read 和 Data Write 記錄類型,適用於 Compute Engine API。
- 將
eventarc.eventReceiverIAM 角色授予預設的 Compute Engine 服務帳戶:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \ --role roles/eventarc.eventReceiver
建立函式
本程式碼研究室使用 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
執行下列指令來建立 VM:
gcloud compute instances create $VM_NAME --zone=$ZONE --machine-type=e2-medium --image-family=debian-11 --image-project=debian-cloud
VM 建立完畢後,Cloud 控制台的「基本資訊」專區應該會顯示函式為 VM 加上的 creator 標籤,您也可以使用下列指令查看:
gcloud compute instances describe $VM_NAME --zone=$ZONE
輸出內容應會顯示標籤,如下例所示:
... labelFingerprint: ULU6pAy2C7s= labels: creator: atameldev ...
清除所用資源
請務必刪除 VM 執行個體。本實驗室不會再使用這個檔案。
gcloud compute instances delete $VM_NAME --zone=$ZONE
7. 流量拆分
Cloud Run functions 支援多個函式修訂版本,可將流量分配給不同修訂版本,以及將函式復原至先前的版本。
在這個步驟中,您將部署函式的 2 個修訂版本,然後將流量平均分配給這兩個版本。
建立
為應用程式建立資料夾,並前往該資料夾:
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 秒,模擬初始化時間較長的情境。另外還有 HelloWorld 函式,會回應 HTTP 呼叫:
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. 恭喜!
恭喜您完成本程式碼研究室!
涵蓋內容
- Cloud Run functions 簡介,以及如何使用基礎映像檔自動更新。
- 如何編寫會回應 HTTP 呼叫的函式。
- 如何編寫會回應 Pub/Sub 訊息的函式。
- 如何編寫會回應 Cloud Storage 事件的函式。
- 如何在兩個修訂版本之間拆分流量。
- 如何設定執行個體數量下限,避免冷啟動發生。