1. 簡介
如要開始編寫 Cloud Run 函式,您可以使用下列程式碼研究室:
否則,本程式碼研究室會逐步引導您建立 Cloud Functions (第 2 代)。
總覽
Cloud Functions (第 2 代) 是 Google Cloud Functions 的下一個版本,也是 Google Cloud 的函式即服務產品。這個新版本提供先進的功能集,並採用 Cloud Run 和 Eventarc 技術,讓您進一步控管效能和擴充性,以及 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. 設定和需求
自助式環境設定
- 登入 Google Cloud 控制台,然後建立新專案或重複使用現有專案。如果您還沒有 Gmail 或 Google Workspace 帳戶,請務必建立帳戶。
- 「Project name」是這個專案參與者的顯示名稱。這是 Google API 不會使用的字元字串。您隨時可以更新。
- 專案 ID 在所有 Google Cloud 專案中不得重複,且無法變更 (設定後即無法變更)。Cloud 控制台會自動產生唯一字串,您通常不需要特別留意。在大多數程式碼研究室中,您需要參照專案 ID (通常會標示為
PROJECT_ID
)。如果您不喜歡產生的 ID,可以隨機產生另一組 ID。或者,您也可以自行嘗試,看看是否可用。這項設定在這個步驟後即無法變更,並會在專案期間維持不變。 - 提醒您,有些 API 會使用第三個值,也就是「專案編號」。如要進一步瞭解這三個值,請參閱說明文件。
- 接下來,您需要在 Cloud 控制台中啟用帳單功能,才能使用 Cloud 資源/API。執行本程式碼研究室時,費用應該不會太高,如要關閉資源,避免產生教學課程以外的費用,您可以刪除建立的資源,或刪除整個專案。Google Cloud 新使用者可享有 $300 美元的免費試用期。
啟動 Cloud Shell
雖然 Google Cloud 可透過筆記型電腦遠端操作,但在本程式碼研究室中,您將使用 Google Cloud Shell,這是在雲端運作的指令列環境。
在 Google Cloud 控制台中,按一下右上方工具列的 Cloud Shell 圖示:
佈建並連線至環境的作業需要一些時間才能完成。完成後,畫面應如下所示:
這個虛擬機器會載入您需要的所有開發工具。提供永久的 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」部分看到該函式:
測試
使用下列指令測試函式:
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」部分看到該函式:
測試
將訊息傳送至主題,藉此測試函式:
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 時觸發,如下所示:
第二個事件會在 VM 建立後觸發,如下所示:
請注意含有 first: true
和 last: true
值的運算欄位。第二個稽核記錄包含標示執行個體所需的所有資訊,因此我們會使用 last: true
標記在 Cloud Functions 中偵測這項資訊。
設定
如要使用 Cloud 稽核記錄函式,您必須為 Eventarc 啟用稽核記錄。您也必須使用具有 eventarc.eventReceiver
角色的服務帳戶。
- 啟用 Cloud 稽核記錄: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
取得程式碼
複製包含應用程式的存放區:
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 代環境的函式:
按一下 Add Eventarc Trigger
按鈕:
系統會在右側開啟側邊面板,您可以為 Eventarc 觸發條件選擇不同的事件供應器和事件。
選擇正確的事件供應者和事件,然後按一下 Save Trigger
:
最後,您可以在下一頁中使用 GitHub 上的 index.js
和 package.json
檔案更新 index.js
和 package.json
檔案,然後按一下 Deploy
按鈕:
測試
如要測試稽核記錄函式,您必須在 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
:
部署背景為黃色的第二個修訂版本:
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 秒,模擬長時間初始化。它也包含 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] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
這是因為系統會建立更多函式執行個體來處理要求。如果您檢查函式的活動執行個體數量,應該會發現某個時間點建立了多個執行個體,而這些執行個體導致了冷啟動問題:
部署
部署與先前函式相同的新函式。部署完成後,您可以增加並行作業數:
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 稽核記錄的函式。
- 如何在兩個修訂版本之間拆分流量。
- 如何透過最少的執行個體消除冷啟動。
- 如何設定並行。