1. 簡介
總覽
Cloud Run 函式是 Google Cloud 的函式即服務產品,採用 Cloud Run 和 Eventarc 技術,可讓您進一步掌控效能和擴充性,以及加強控管函式執行階段,並透過 90 個以上的事件來源觸發作業。
本程式碼研究室將逐步說明如何建立 Cloud Run 函式,以回應 HTTP 呼叫,並透過 Pub/Sub 訊息和 Cloud Audit Logs 觸發。
本程式碼研究室也會使用 --base-image
旗標指定基本映像檔,在部署函式時自動更新基本映像檔。啟用 Cloud Run 的基本映像檔自動更新功能後,Google 就能自動修補基本映像檔的作業系統和語言執行階段元件,您不必重建或重新部署服務,即可更新基礎映像檔。詳情請參閱「自動更新基本映像檔」
如果您不想使用自動更新基礎映像檔,可以從本程式碼研究室的範例中移除 --base-image
旗標。
課程內容
- Cloud Run 函式簡介,以及如何使用自動更新基礎映像檔。
- 如何編寫可回應 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 函式
首先,請建立 authenticated 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 Console 的 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 會發出 2 則稽核記錄。
第一個事件會在 VM 建立作業開始時發出,第二個事件會在 VM 建立完成後發出。
稽核記錄中的作業欄位不同,包含 first: true
和 last: true
值。第二個稽核記錄包含標記執行個體所需的所有資訊,因此我們將使用 last: true
標記在 Cloud Run 函式中偵測到該記錄。
設定
如要使用 Cloud 稽核記錄功能,您必須為 Eventarc 啟用稽核記錄。您也必須使用具備 eventarc.eventReceiver
角色的服務帳戶。
- 啟用 Cloud 稽核記錄的 Admin Read、Data Read 和 Data Write 記錄類型,適用於 Compute Engine API。
- 將
eventarc.eventReceiver
IAM 角色授予預設的 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 Console 的「Basic information」(基本資訊) 區段中,或使用下列指令,看到 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!") }
部署
部署函式的第一個修訂版本,預設的最小執行個體值為零:
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 函式簡介,以及如何使用自動更新基礎映像檔。
- 如何編寫可回應 HTTP 呼叫的函式。
- 如何編寫函式來回應 Pub/Sub 訊息。
- 如何編寫可回應 Cloud Storage 事件的函式。
- 如何將流量拆分給兩個修訂版本。
- 如何透過執行個體數量下限,避免冷啟動。