開始使用 Cloud Functions (第 2 代)

1. 簡介

如要開始編寫 Cloud Run 函式,您可以使用下列程式碼研究室:

否則,本程式碼研究室會逐步引導您建立 Cloud Functions (第 2 代)。

總覽

Cloud Functions (第 2 代) 是 Google Cloud Functions 的下一個版本,也是 Google Cloud 的函式即服務產品。這個新版本提供先進的功能集,並採用 Cloud RunEventarc 技術,讓您進一步控管效能和擴充性,以及 90 多個事件來源的函式執行階段和觸發條件。

本程式碼研究室將逐步說明如何建立 Cloud 函式,以回應 HTTP 呼叫,並由 Pub/Sub 訊息和 Cloud Audit Logs 觸發。

新功能

這個新版 Cloud Functions 提供更優質的函式即服務 (FaaS) 體驗,並由 Cloud Run、Cloud Build、Artifact Registry 和 Eventarc 提供支援。

強化基礎架構

  • 更長的請求處理時間:讓 Cloud Functions 的執行時間超過預設的 5 分鐘,以便執行更長的請求工作負載,例如處理來自 Cloud Storage 或 BigQuery 的大量資料串流。對於 HTTP 函式,則為最多 60 分鐘。事件驅動函式目前的逾時時間上限為 10 分鐘。
  • 較大的執行個體:在 Cloud Functions 上使用最多 16 GB 的 RAM 和 4 個 vCPU,以便處理較大的記憶體內工作負載、耗用大量運算資源的工作負載,以及更多並行工作負載。
  • 並行處理:使用單一函式執行個體處理最多 1, 000 個並行要求,盡可能減少冷啟動並縮短縮放時的延遲時間。
  • 最少執行個體:提供預先暖機的執行個體,以減少冷啟動情況,並確保應用程式的引導時間不會影響應用程式效能。
  • 流量拆分:支援函式多個版本、在不同版本之間拆分流量,以及將函式回溯至先前版本。

更廣泛的事件涵蓋範圍和 CloudEvents 支援功能

  • Eventarc 整合:Cloud Functions 現已原生支援 Eventarc,可透過 Cloud Audit 記錄 (BigQuery、Cloud SQL、Cloud Storage 等) 提供超過 90 個以上事件來源。當然,Cloud Functions 仍支援直接發布至 Cloud Pub/Sub 的自訂來源事件。
  • CloudEvent 格式:所有事件驅動函式都會遵循業界標準的 CloudEvents ( cloudevents.io),無論來源為何,都能確保開發人員體驗一致。酬載會透過結構化 CloudEvent 傳送,並附帶 cloudevent.data 酬載,並實作 CloudEvent 標準。

課程內容

  • Cloud Functions (第 2 代) 總覽。
  • 如何編寫可回應 HTTP 呼叫的函式。
  • 如何編寫回應 Pub/Sub 訊息的函式。
  • 如何編寫可回應 Cloud Storage 事件的函式。
  • 如何編寫可回應 Cloud 稽核記錄的函式。
  • 如何在兩個修訂版本之間拆分流量。
  • 如何透過最少的執行個體消除冷啟動。
  • 如何設定並行。

2. 設定和需求

自助式環境設定

  1. 登入 Google Cloud 控制台,然後建立新專案或重複使用現有專案。如果您還沒有 Gmail 或 Google Workspace 帳戶,請務必建立帳戶

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • 「Project name」是這個專案參與者的顯示名稱。這是 Google API 不會使用的字元字串。您隨時可以更新。
  • 專案 ID 在所有 Google Cloud 專案中不得重複,且無法變更 (設定後即無法變更)。Cloud 控制台會自動產生唯一字串,您通常不需要特別留意。在大多數程式碼研究室中,您需要參照專案 ID (通常會標示為 PROJECT_ID)。如果您不喜歡產生的 ID,可以隨機產生另一組 ID。或者,您也可以自行嘗試,看看是否可用。這項設定在這個步驟後即無法變更,並會在專案期間維持不變。
  • 提醒您,有些 API 會使用第三個值,也就是「專案編號」。如要進一步瞭解這三個值,請參閱說明文件
  1. 接下來,您需要在 Cloud 控制台中啟用帳單功能,才能使用 Cloud 資源/API。執行本程式碼研究室時,費用應該不會太高,如要關閉資源,避免產生教學課程以外的費用,您可以刪除建立的資源,或刪除整個專案。Google Cloud 新使用者可享有 $300 美元的免費試用期

啟動 Cloud Shell

雖然 Google Cloud 可透過筆記型電腦遠端操作,但在本程式碼研究室中,您將使用 Google Cloud Shell,這是在雲端運作的指令列環境。

Google Cloud 控制台中,按一下右上方工具列的 Cloud Shell 圖示:

55efc1aaa7a4d3ad.png

佈建並連線至環境的作業需要一些時間才能完成。完成後,畫面應如下所示:

7ffe5cbb04455448.png

這個虛擬機器會載入您需要的所有開發工具。提供永久的 5 GB 主目錄,而且在 Google Cloud 中運作,可大幅提升網路效能和驗證功能。您可以在瀏覽器中完成本程式碼研究室的所有工作。您不需要安裝任何東西。

設定 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 函式

針對第一個函式,我們將建立一個已驗證的 Node.js 函式,用於回應 HTTP 要求。我們也將使用 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 函式

針對第二個函式,我們將建立 Python 函式,由發布至特定主題的 Pub/Sub 訊息觸發。

設定 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 $_

建立 main.py 檔案,只記錄含有 CloudEvent ID 的訊息:

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 稽核記錄函式

接下來,我們將建立 Node.js 函式,在建立 Compute Engine VM 執行個體時接收 Cloud 稽核記錄事件。系統會在回應中,為新建的 VM 新增標籤,指定 VM 建立者。

判斷新建立的 Compute Engine VM

建立 VM 時,Compute Engine 會傳送 2 個稽核記錄。

第一個事件會在建立 VM 時觸發,如下所示:

8d394a481644c4b6.png

第二個事件會在 VM 建立後觸發,如下所示:

ee0e221d82887cd1.png

請注意含有 first: truelast: true 值的運算欄位。第二個稽核記錄包含標示執行個體所需的所有資訊,因此我們會使用 last: true 標記在 Cloud Functions 中偵測這項資訊。

設定

如要使用 Cloud 稽核記錄函式,您必須為 Eventarc 啟用稽核記錄。您也必須使用具有 eventarc.eventReceiver 角色的服務帳戶。

  1. 啟用 Cloud 稽核記錄:Compute Engine API 的管理員讀取資料讀取資料寫入記錄類型:

76b7417ea4071241.png

  1. 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

取得程式碼

複製包含應用程式的存放區:

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

前往應用程式目錄:

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

index.js 檔案包含應用程式程式碼,可接收包裝在 CloudEvent 中的稽核記錄。接著,它會擷取 Compute Engine VM 執行個體詳細資料,並在 VM 執行個體上設定標籤。歡迎自行深入研究 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 Console 部署函式,並新增 Eventarc 觸發條件。

首先,前往「Cloud Functions」專區,並建立使用第 2 代環境的函式:

8ba79a12ee152d8.png

按一下 Add Eventarc Trigger 按鈕:

655346320a5e3631.png

系統會在右側開啟側邊面板,您可以為 Eventarc 觸發條件選擇不同的事件供應器和事件。

選擇正確的事件供應者和事件,然後按一下 Save Trigger

7d24325ff06c9b05.png

最後,您可以在下一頁中使用 GitHub 上的 index.jspackage.json 檔案更新 index.jspackage.json 檔案,然後按一下 Deploy 按鈕:

f2e338eed2ccf5a2.png

測試

如要測試稽核記錄函式,您必須在 Cloud 控制台中建立 Compute Engine VM (您也可以使用 gcloud 建立 VM,但該方法似乎不會產生稽核記錄)。

前往 Cloud 控制台的「Compute Engine」>「VM 執行個體」部分,建立新的 VM。建立 VM 後,您應該會在 Cloud Console 的「Basic information」(基本資訊) 部分,或透過下列指令,在 VM 上看到已新增的 creator 標籤:

gcloud compute instances describe YOUR_VM_NAME

輸出內容應會顯示標籤,如下所示:

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

7. 流量拆分

Cloud Functions (第 2 代) 支援多個函式修訂版本,可在不同修訂版本之間拆分流量,並將函式回溯至先前版本。這是因為第 2 代函式實際上是 Cloud Run 服務。

在這個步驟中,您將部署 2 個函式修訂版本,然後將流量以 50/50 的比例拆分給這 2 個版本。

建立

建立應用程式資料夾並前往該資料夾:

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 秒,模擬長時間初始化。它也包含 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 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. 恭喜!

恭喜您完成程式碼研究室!

涵蓋內容

  • Cloud Functions (第 2 代) 總覽。
  • 如何編寫可回應 HTTP 呼叫的函式。
  • 如何編寫回應 Pub/Sub 訊息的函式。
  • 如何編寫可回應 Cloud Storage 事件的函式。
  • 如何編寫可回應 Cloud 稽核記錄的函式。
  • 如何在兩個修訂版本之間拆分流量。
  • 如何透過最少的執行個體消除冷啟動。
  • 如何設定並行。