GCP-111:Cloud Functions 入門——事件驅動無伺服器函式完全指南
前言
在 GCP 的運算服務裡,Cloud Functions 大概是門檻最低的一個——你只要寫一個函式,丟上去,剩下交給 Google。有事件來就跑、沒事件就縮到零、零成本,連伺服器、容器、程式進入點都不用你操心。
這一課我們把 Cloud Functions 從頭走一遍:它到底是什麼、Gen1 跟 Gen2 差在哪、怎麼接觸發器、定價怎麼算,還有幾個新手最容易踩的坑。ACE 考試的無伺服器選型題也會一併講到。

Gen2 函式把事件來源、觸發路由與 Cloud Run 執行環境接起來,平台負責啟動與擴縮 Instance,程式仍要管理密鑰、逾時與外部副作用。事件來源通常採 at-least-once 交付,重試可能帶來相同事件;用 Event ID、交易或去重紀錄做冪等,才能讓付款、寄信、寫入等動作只生效一次。
什麼是 Cloud Functions?
Cloud Functions 讓你部署單一函式來回應事件:
流程圖暫時無法顯示,請重新整理頁面後再試。
平台負責事件路由、啟動與水平擴縮;程式仍要處理重試、外部副作用與逾時。scale-to-zero 省下閒置成本,但下一個事件可能需要重新冷啟動。
最後那一步有個正式名稱叫 scale-to-zero(縮容至零)——沒有流量時實例會全部關掉,這段時間 Google 一毛錢都不跟你收。代價是下一個請求進來時得重新冷啟動(後面會講),這是無伺服器服務最迷人也最容易卡住新手的特性。
2024 年 Google 將 Cloud Functions Gen2 更名為 Cloud Run functions,因為 Gen2 底層就是跑在 Cloud Run 上。但 gcloud 指令和介面仍然沿用
functions名稱——你後面看到的指令還是gcloud functions deploy,別被名字搞混了。

注意這個畫面的標題是 Cloud Run「建立服務」——這正是上面那段更名的實證:你在 Console 建函式,其實是被帶到 Cloud Run 底下,選最右邊那張「 使用內嵌編輯器建立函式」的卡片。Gen2 = Cloud Run functions,連免費層都跟 Cloud Run 共用同一套。
Gen1 vs Gen2
Gen2 是目前預設且推薦的版本,Gen1 已進入維護模式。
| 特性 | Gen1 | Gen2(推薦) |
|---|---|---|
| 底層平台 | 獨立系統 | Cloud Run |
| 並發處理 | 1 個請求/實例 | 最高 1,000 個請求/實例 |
| HTTP 最大逾時 | 9 分鐘 | 60 分鐘 |
| 事件驅動最大逾時 | 9 分鐘 | 9 分鐘 |
| 最大記憶體 | 8 GiB | 32 GiB(GA) |
| 最大 CPU | 4.8 GHz(約 2 vCPU) | 4 vCPU(GA) |
| 事件來源 | 有限(GCS、Pub/Sub 等) | 90+ 事件來源(Eventarc) |
| VPC 連線 | VPC Connector | Direct VPC Egress |
並發處理——Gen2 的最大優勢
Gen1 每個實例只能處理一個請求。100 個同時請求 = 啟動 100 個實例。
Gen2 支援並發(預設 80),100 個同時請求只需要 2 個實例:
流程圖暫時無法顯示,請重新整理頁面後再試。
Gen2 並發能降低實例數,但同一個 Instance 的記憶體、連線池與暫存空間會被多個請求共用。並發值要用 thread-safety、單請求資源量與下游容量一起決定。
這裡的**冷啟動(cold start)**指的是:實例從零被叫醒、載入你的程式碼、初始化執行環境的那段延遲,通常幾百毫秒到幾秒。實例越少、被重複利用的機會越高,冷啟動就越少——這也是 Gen2 並發省成本之外的另一個好處。
💡 重點提點
Gen2 並發是把雙面刃。同一個實例同時處理多個請求,意思是這些請求共用同一份記憶體——全域變數、資料庫連線、暫存檔,全部是共享的。新手最常踩的坑是:把使用者資料存進全域變數,結果 A 的請求讀到 B 的資料。寫並發函式時,請假設任何兩個請求隨時可能同時跑,狀態要嘛放區域變數、要嘛寫進外部儲存(Firestore、Memorystore),別偷懶塞全域。
觸發器類型
下面會用到幾個 GCP 服務名詞,先一句話交代:GCS(Cloud Storage)是 Google 的物件儲存,放圖片、檔案、備份;Pub/Sub 是訊息佇列,把「發送方」跟「處理方」解耦;Firestore 是 NoSQL 文件資料庫。觸發器的角色就是:當這些服務發生事情(檔案上傳、收到訊息、文件變更),自動叫醒你的函式。

函式要「被什麼叫醒」就在這裡設。HTTP 觸發是預設(函式本身就是一個網址,不必特別在這加);事件驅動則從這個選單挑: Pub/Sub、Cloud Storage、Firestore 各有現成入口,其餘上百種來源全走最下面那條「 其他 Eventarc 觸發條件」——這就是 Gen2 靠 Eventarc 撐起 90+ 事件來源的地方。
HTTP 觸發器
最直接的一種,函式本身就是一個 HTTPS Endpoint(端點,也就是一個可以被呼叫的網址):
# Python 範例
import functions_framework
@functions_framework.http
def hello(request):
name = request.args.get("name", "World")
return f"Hello, {name}!"
# 部署
gcloud functions deploy hello \
--gen2 \
--runtime=python312 \
--trigger-http \
--allow-unauthenticated \
--region=asia-east1 \
--entry-point=hello
# 取得 URL
gcloud functions describe hello \
--region=asia-east1 \
--format="value(serviceConfig.uri)"
Cloud Storage 觸發器
GCS 有檔案變動時自動執行:
import functions_framework
from cloudevents.http import CloudEvent
@functions_framework.cloud_event
def on_file_upload(cloud_event: CloudEvent):
data = cloud_event.data
bucket = data["bucket"]
name = data["name"]
print(f"New file: gs://{bucket}/{name}")
gcloud functions deploy on-file-upload \
--gen2 \
--runtime=python312 \
--trigger-event-filters="type=google.cloud.storage.object.v1.finalized" \
--trigger-event-filters="bucket=my-upload-bucket" \
--region=asia-east1 \
--entry-point=on_file_upload
Pub/Sub 觸發器
收到 Pub/Sub 訊息時自動執行:
import base64
import functions_framework
from cloudevents.http import CloudEvent
@functions_framework.cloud_event
def on_message(cloud_event: CloudEvent):
data = base64.b64decode(cloud_event.data["message"]["data"]).decode()
print(f"Received message: {data}")
gcloud functions deploy on-message \
--gen2 \
--runtime=python312 \
--trigger-topic=my-topic \
--region=asia-east1 \
--entry-point=on_message
Eventarc 觸發器(Gen2 獨有)
Eventarc 是 GCP 的統一事件路由服務——它把各種 Google Cloud 服務(甚至第三方)發生的事件,標準化後轉送給你的函式。有了它,Gen2 不用為每種來源各寫一套整合,支援 90+ 種事件來源,包括:
- Cloud SQL 資料庫變更
- BigQuery 作業完成
- Cloud Build 建置狀態變更
- Firestore 文件變更
- 第三方服務(GitHub、Stripe 等)
流程圖暫時無法顯示,請重新整理頁面後再試。
HTTP 適合同步請求;Pub/Sub 適合主動解耦;GCS 與 Eventarc 適合由雲端資源變更驅動。無論來源為何,都要明確定義重試、事件格式與失敗處理,而不是只把 Trigger 接上就算完成。
支援的語言
| 語言 | 版本(2026) |
|---|---|
| Node.js | 24 |
| Python | 3.12、3.13、3.14 |
| Go | 1.26 |
| Java | 21、25 |
| .NET | 最新穩定版 |
| Ruby | 最新穩定版 |
| PHP | 最新穩定版 |
各語言的支援版本會持續往前推進(新版本上架、舊版本退役都很頻繁),實際部署前以官方的 runtime support 頁面為準。考試不會考你某個 runtime 的精確小版本,記得「Cloud Functions 支援主流語言、版本跟著官方滾動」這個概念就夠了。

「執行階段」就是挑程式語言的地方。可以看到同一個語言會有多個版本並列,還會標「 預先發布版」或「已不適用」——正好對上這一節說的:Cloud Functions 支援主流語言、版本跟著官方一直滾動。這也是為什麼別去背精確小版本,挑當下的穩定版就對了。
資源配置與冷啟動
資源配置
gcloud functions deploy my-func \
--gen2 \
--runtime=nodejs24 \
--trigger-http \
--memory=512Mi \ # 記憶體(128Mi ~ 32Gi)
--cpu=1 \ # vCPU(0.083 ~ 4)
--min-instances=1 \ # 最小實例(消除冷啟動)
--max-instances=20 \ # 最大實例(控制成本)
--concurrency=80 \ # 每實例並發數(Gen2)
--timeout=60 \ # 逾時(秒)
--region=asia-east1
消除冷啟動
# 保留暖機實例
gcloud functions deploy my-func \
--min-instances=1
# 設定 0 就是允許縮容至零(預設)
gcloud functions deploy my-func \
--min-instances=0
流程圖暫時無法顯示,請重新整理頁面後再試。
min-instances 解決冷啟動,concurrency 決定每個 Instance 同時處理多少請求,max-instances
則保護成本與下游服務;三者應根據壓測一起設定。
事件交付語義
At-Least-Once(至少一次)
Cloud Functions 的事件觸發是 at-least-once(至少一次) 語義,意思是同一個事件可能觸發你的函式好幾次——Google 保證「至少送到一次」,但不保證「剛好一次」。
💡 重點提點
「事件可能重複觸發」既是 ACE 高頻考點,也是實務最痛的地雷。解法只有一個:把函式寫成冪等(idempotent)——同一個事件跑一次、跑十次,結果都一樣。最常見的做法是用事件的唯一 ID 當作「處理過沒」的鎖(下面的範例就是這招)。如果你的函式會扣款、發信、寫入訂單,沒做冪等就上線,遲早會出現重複扣款、重複寄信的災難。
解法:設計冪等函式
所謂冪等(idempotent),白話講就是「同一個操作做幾次,效果都跟做一次一樣」。下面用事件的唯一 ID 先查一次「處理過了沒」,處理過就直接跳出:
from google.cloud import firestore
db = firestore.Client()
@functions_framework.cloud_event
def process_order(cloud_event):
event_id = cloud_event["id"] # 每個事件有唯一 ID
# 檢查是否已經處理過
doc = db.collection("processed_events").document(event_id).get()
if doc.exists:
print(f"Already processed: {event_id}")
return
# 執行業務邏輯
do_business_logic(cloud_event.data)
# 記錄已處理
db.collection("processed_events").document(event_id).set({
"processed_at": firestore.SERVER_TIMESTAMP
})
上面的「先查、執行、再寫」適合解釋概念,但正式環境不能把三步當成天然原子操作;兩個重複事件同時通過查詢時,仍可能一起執行副作用。內部資料更新要用交易或唯一鍵保護,外部 API 則要傳遞相同的 idempotency key,或用 Outbox/工作佇列把狀態與副作用拆開。
流程圖暫時無法顯示,請重新整理頁面後再試。
At-least-once 不代表副作用一定重複;關鍵是用 Event ID 取得唯一處理權,並讓每種副作用具備可重試的原子邊界。單純在記憶體判斷,或把「查詢後寫入」分成兩個未受保護步驟,都擋不住競態。
Secret Manager 整合
方式一:部署時引用(推薦)
# 掛載為環境變數
gcloud functions deploy my-func \
--set-secrets=DB_PASSWORD=db-password:latest
# 掛載為檔案
gcloud functions deploy my-func \
--set-secrets=/secrets/api-key=api-key:latest
方式二:程式碼中呼叫
from google.cloud import secretmanager
def get_secret(secret_id):
client = secretmanager.SecretManagerServiceClient()
name = f"projects/my-project/secrets/{secret_id}/versions/latest"
response = client.access_secret_version(name=name)
return response.payload.data.decode("utf-8")
IAM 需求:函式的 **Service Account(服務帳號,函式執行時代表的身分)**需要 roles/secretmanager.secretAccessor 這個 IAM 角色,否則讀 Secret 會被拒。權限不足是新手部署完第一次踩的雷,看到 PermissionDenied 先回來檢查這一條。
本地開發與測試
Cloud Functions 提供 Functions Framework,可在本地模擬雲端執行環境:
安裝與啟動
# Python
pip install functions-framework
functions-framework --target=hello --debug --port=8080
# Node.js
npm install @google-cloud/functions-framework
npx functions-framework --target=hello --port=8080
測試 HTTP 函式
curl http://localhost:8080?name=Bobo
測試事件驅動函式(CloudEvents)
curl -X POST http://localhost:8080 \
-H "ce-specversion: 1.0" \
-H "ce-type: google.cloud.storage.object.v1.finalized" \
-H "ce-source: //storage.googleapis.com/projects/my-project" \
-H "ce-id: test-001" \
-H "Content-Type: application/json" \
-d '{"bucket": "my-bucket", "name": "test.txt"}'
定價與免費層
⚠️ 官方定價頁已把 Gen2(Cloud Run functions)併入 Cloud Run 計費,下面是 Gen2 的額度。Gen1 用的是不同單位、不同數字:200 萬次呼叫、400,000 GB-秒、200,000 GHz-秒、5 GB 對外流量——兩代別搞混。
免費層(每月,Gen2)
| 項目 | 免費額度 |
|---|---|
| 呼叫次數 | 200 萬次 |
| 運算(CPU) | 180,000 vCPU-秒 |
| 運算(記憶體) | 360,000 GiB-秒 |
| 對外流量 | 北美區域出站 1 GB |
超過免費層
| 項目 | 費用 |
|---|---|
| 呼叫 | $0.40 / 100 萬次 |
| CPU | $0.000024 / vCPU-秒 |
| 記憶體 | $0.0000025 / GiB-秒 |
定價就是 Cloud Run 定價,因為 Gen2 底層直接跑在 Cloud Run 上。
Cloud Functions vs Cloud Run vs App Engine
這是 ACE 考試最高頻的選型題:
| 特性 | Cloud Functions | Cloud Run | App Engine |
|---|---|---|---|
| 部署單位 | 單一函式 | 容器 | 應用程式 |
| 語言 | 7 種 runtime | 任何(Docker) | 指定 runtime |
| 觸發方式 | 事件 / HTTP | HTTP | HTTP |
| 最大逾時 | 60 分鐘(HTTP Gen2) | 60 分鐘 | 24 小時 |
| 並發 | 1000/實例(Gen2) | 1000/實例 | 依 instance class |
| 最適合 | 事件驅動函式 | 容器化微服務 | 傳統 Web 應用 |
| 免費層 | 200 萬次/月 | 200 萬次/月 | 28 F-hours/天 |
選型公式
流程圖暫時無法顯示,請重新整理頁面後再試。
先看部署單位:聚焦事件處理選函式、完整容器服務選 Cloud Run、傳統平台式 Web App 選 App Engine;有明確開始與完成、可能超過事件逾時的批次工作則應轉向 Cloud Run Jobs。
ACE 考試重點整理
必背知識點
- Gen2 是預設,底層是 Cloud Run,Gen1 進入維護模式
- Gen2 支援並發(最高 1000/實例),Gen1 只能 1 個請求/實例
- HTTP 觸發最長 60 分鐘(Gen2),事件觸發最長 9 分鐘
- 事件交付是 at-least-once,需要冪等設計
- Eventarc 讓 Gen2 支援 90+ 種事件來源
- 免費層:每月 200 萬次呼叫
- Functions Framework 可本地測試
常見陷阱題
Q:Cloud Functions 能處理長時間運行的 ETL 任務嗎? A:HTTP 觸發最長 60 分鐘,事件觸發最長 9 分鐘。超過這個時間就該改用 Cloud Run Jobs(最長 7 天)。
Q:100 個用戶同時呼叫 Gen1 函式,需要幾個實例? A:100 個。Gen1 每實例只能處理 1 個請求。Gen2 只需要 2 個實例(concurrency=80)。
Q:Cloud Functions 保證事件只處理一次嗎? A:不保證。Cloud Functions 是 at-least-once,需要自己設計冪等邏輯。
Q:GCS 上傳自動觸發處理,用什麼服務? A:Cloud Functions(GCS 觸發器最自然的搭配)。
實戰範例:圖片上傳自動生成縮圖
# main.py
import functions_framework
from cloudevents.http import CloudEvent
from google.cloud import storage
from PIL import Image
import io
storage_client = storage.Client()
@functions_framework.cloud_event
def generate_thumbnail(cloud_event: CloudEvent):
data = cloud_event.data
bucket_name = data["bucket"]
file_name = data["name"]
# 跳過非圖片和已是縮圖的檔案
if not file_name.lower().endswith((".jpg", ".jpeg", ".png")):
return
if file_name.startswith("thumbnails/"):
return
# 下載原圖
bucket = storage_client.bucket(bucket_name)
blob = bucket.blob(file_name)
image_data = blob.download_as_bytes()
# 生成縮圖
image = Image.open(io.BytesIO(image_data))
image.thumbnail((200, 200))
# 上傳縮圖
output = io.BytesIO()
image.save(output, format="JPEG", quality=85)
output.seek(0)
thumb_blob = bucket.blob(f"thumbnails/{file_name}")
thumb_blob.upload_from_file(output, content_type="image/jpeg")
print(f"Thumbnail created: thumbnails/{file_name}")
# 部署
gcloud functions deploy generate-thumbnail \
--gen2 \
--runtime=python312 \
--trigger-event-filters="type=google.cloud.storage.object.v1.finalized" \
--trigger-event-filters="bucket=my-image-bucket" \
--region=asia-east1 \
--memory=512Mi \
--entry-point=generate_thumbnail
流程圖暫時無法顯示,請重新整理頁面後再試。
任何「寫回同一事件來源」的函式都要先設計迴圈終止條件。這個範例用輸出 prefix 與檔案類型過濾;正式環境還可加入 metadata、generation 或 Event ID 去重,讓重試也保持冪等。
總結
Cloud Functions 是 GCP 最輕量的上手方式:寫一個函式、接上觸發器、剩下交給平台。真正會在工作和考場上反覆出現的,其實就是這幾個觀念——Gen2 是預設、底層是 Cloud Run;並發是把雙面刃,狀態別亂塞全域;事件是 at-least-once,函式一定要冪等。前面兩個 💡 重點提點講的就是這兩個最容易翻車的地方,記熟它們,比背一堆定價數字實用得多。
選型上記住一句話就好:收到事件要跑一段邏輯,先想 Cloud Functions;要部署容器化服務,去 Cloud Run;要架傳統 Web App,找 App Engine。
下一課 ACE-212:App Engine 應用開發與部署,我們來看 GCP 這個歷史最悠久的 PaaS 平台,今天還適合用在哪些場景。
運算服務選型系列
| 課程 | 服務 | 適合場景 |
|---|---|---|
| GCP-109 | Cloud Run | 容器化 API/微服務,scale-to-zero |
| 本課 GCP-111 | Cloud Functions | 事件驅動單一函式(GCS/Pub/Sub 觸發) |
| ACE-212 | App Engine | 快速部署 Web App、免費層最多 |
| ACE-206 | GKE | 完整 K8s 控制、大規模微服務 |
| ACE-203 | 綜合比較 | 全部 Serverless 選型決策樹 |