App Engine 簡介
App Engine 是 Google Cloud 最早的 Platform-as-a-Service (PaaS),於 2008 年推出,至今仍是執行 HTTP 應用程式而不必管理伺服器、作業系統或負載平衡器最簡單的方式之一。你交給 App Engine 的是原始碼(Standard)或 container image(Flexible),它會處理路由、擴充、健康檢查、TLS 終結、記錄與當機復原。對 PCD 考生來說,App Engine 會出現在請求驅動的 Web 應用、行動後端、用流量分割做 A/B 測試、排程工作、任務佇列,以及「該選 App Engine 還是 Cloud Run」的取捨題上。
本章節涵蓋兩種環境(Standard 與 Flexible)、支援的 runtime、三種 scaling 模式(automatic、basic、manual)、F1 到 B8 的 instance class、流量分割策略、版本遷移、自訂網域與 managed SSL、三個搭配的設定檔(dispatch.yaml、cron.yaml、queue.yaml),以及 App Engine 與 Cloud Run 之間的決策樹。
白話文解釋(Plain English Explanation)
類比 1:服務式公寓 vs 自有產權公寓
App Engine Standard 像是全套服務的飯店式公寓。房間附家具、清潔人員每天打掃,沒人住的時候大樓還會把燈關掉(scale to zero)。你不能打牆、不能改水管,只能用他們提供的廚房設備(支援的語言 runtime)。App Engine Flexible 則像是自有產權的公寓:你可以重新裝潢廚房、自帶烤箱(自訂 Docker image)、想放什麼食材都可以(任何 OS 套件或函式庫)。代價是這間公寓永遠至少有一個人住——你無法 scale to zero,整夜門房(底層的 Compute Engine VM)也要算錢。
類比 2:自排 vs 定速 vs 手排
三種 scaling 模式就像三種駕駛風格。Automatic scaling 是自排:踩油門(送進流量)車子自己決定何時升檔(加 instance)何時降檔(移除 instance)。Basic scaling 是會自動熄火的定速:一段時間沒收到請求車子會自己停在路邊,下一個請求進來才發動引擎。Manual scaling 是卡在三檔的手排:你決定有幾個 instance、它們永遠在跑——適合背景 worker 或不想被踢出的記憶體快取。
類比 3:流量分割就像收音機的調諧旋鈕
App Engine 的流量分割像一個同時可以播多個電台的收音機調諧旋鈕。版本 v1 佔頻譜的 90%,v2 佔 10%,聽眾轉到哪個位置就聽到哪個電台。你可以一個百分點一個百分點地調(--splits v1=0.9,v2=0.1),也可以叫收音機用 cookie 記住每位聽眾的電台(--split-by=cookie),讓同一位使用者每次都聽到同樣的版本。確認 v2 沒問題後把旋鈕完全轉過去,v1 還會在背景靜默播放,直到你把它關掉。
App Engine Standard 與 Flexible 的差異
兩種環境不可互換;選錯是經典考題陷阱。
Standard 環境
App Engine Standard 在 Google 管理的 sandbox 上以語言專屬 runtime 執行你的程式。第二代支援的 runtime 有 Python 3.7+、Java 11/17/21、Node.js 10+、PHP 7.2+、Ruby 2.5+、Go 1.11+。第一代的 Python 2.7 與 Java 8 已棄用。Standard 的 cold start 在秒級以下,可以縮減到零個 instance,並以細粒度的 instance-hour 計費。代價是:沒有 shell access、無法安裝 runtime 沒附帶的系統函式庫、除了部分 Java runtime 外不能跑長時間背景執行緒,對外網路存取要透過 sockets API(歷史上有配額)。
Flexible 環境
App Engine Flexible 在 Google 替你代管的 Compute Engine VM 上以 Docker container 跑你的程式。你選擇 runtime(runtime: python、或自帶 Dockerfile 的 runtime: custom)。Flexible 支援任何語言、自訂 OS 套件、可 SSH 進去除錯、可以跑背景程序,但最少要 1 個 instance(不能 scale to zero),啟動時間是分鐘級(要開完整 VM),而且只要 VM 存在就持續收 vCPU、記憶體與 persistent disk 的錢。
最關鍵、考試最愛考的差異:只有 App Engine Standard 能 scale to zero。題目若提到「沒有流量時要把成本壓到最低」或「從閒置瞬間爆衝到數千 req/s」,答案是 Standard 不是 Flexible。Flexible 最少 1 台 VM 表示閒置 24 小時也要付錢。
支援的 Runtime 與自訂 Runtime
Standard 的代管 runtime
App Engine Standard 的 runtime 是版本化、有強烈意見的設計。你在 app.yaml 宣告 runtime: python311 或 runtime: java17,Google 就給你一個帶該直譯器、加上你 requirements.txt 或 Maven/Gradle 相依套件的代管 base image。Standard runtime 由 Google 持續更新;要鎖定 minor 版本就重新部署。
Flexible 的代管與自訂 runtime
Flexible runtime 對 legacy 應用較友善。你可以選代管 runtime(runtime: nodejs、runtime: python)讓 App Engine 自動產生 Dockerfile,也可以寫 runtime: custom 自帶 Dockerfile。custom runtime 就是團隊用來跑 Rust、.NET Framework、COBOL,或任何其他技術棧的方法——container 只要監聽 PORT 環境變數指定的埠就行。
PCD 場景如果提到「我們要一個 Python 3.9 web app,需要 C++ extension,還要一個只在 Debian 才有的系統套件」,答案就是 Flexible 自訂 runtime。Standard sandbox 禁止任意 native 函式庫;Flexible 讓你在 Dockerfile 裡 apt-get install 任何東西。
Scaling 模式:Automatic、Basic、Manual
每個 App Engine service 版本在 app.yaml 中只能宣告一個 scaling 區塊。三種模式不可互換,更換 scaling 區塊必須以新版本重新部署。
Automatic scaling
Automatic scaling 鎖定請求驅動型應用。你設定門檻,App Engine 自動增減 instance 來達成目標。主要參數:
target_cpu_utilization:0.5 到 0.95,預設 0.6。scheduler 把平均 CPU 維持在這個水準。target_throughput_utilization:0.5 到 0.95,預設 0.6。比對 instance 的並行請求容量。max_concurrent_requests:單一 instance 同時處理多少請求(Standard 預設 10,第二代 runtime 最多 1000)。min_instances與max_instances:autoscaler 的上下限。min_instances: 0在 Standard 上啟用 scale to zero。min_idle_instances與max_idle_instances:預熱的閒置容量,用來吸收尖峰。
Basic scaling
Basic scaling 用於批次性或低流量工作。請求進來時 App Engine 才啟動 instance,閒置 idle_timeout 分鐘後關閉。你設 max_instances 與 idle_timeout(預設 5 分鐘)。新 instance 啟動時請求要等候,所以對延遲敏感的應用不要用 Basic。
Manual scaling
Manual scaling 固定 instance 數量。宣告 instances: N,App Engine 就永遠維持 N 個 instance(直到下次重新部署)。Manual 是 Standard 上唯一允許 24 小時長請求的模式,也是唯一保證長時間 in-memory 狀態的模式。適合 WebSocket bridge、排程的長時間 worker、不想被 autoscaler 踢掉的記憶體快取。
常見考題誘餌:「我們要一份跨請求都存活的記憶體快取。」Standard 的 automatic scaling 閒置一陣子就會殺掉 instance,整份快取直接消失。解法不是 Manual scaling 把 instance 釘住,而是把狀態外部化到 Memorystore for Redis。把快取建在本機 instance 記憶體即使 Manual scaling 技術上可行也是反模式。
Instance Class:F1、F2、F4、F4_1G 與 B1、B2、B4、B8
Instance class 決定每個 Standard instance 的 vCPU 與記憶體配額。F 系列搭配 automatic(front-end)scaling,B 系列搭配 basic 與 manual(back-end)scaling。在 app.yaml 宣告 instance_class: F2。
| Class | 記憶體 | CPU 上限 | Scaling | 典型用途 |
|---|---|---|---|---|
| F1 | 256 MB | 600 MHz | Automatic | 預設;輕量 Web app |
| F2 | 512 MB | 1.2 GHz | Automatic | 中型 API |
| F4 | 1024 MB | 2.4 GHz | Automatic | 重型請求處理 |
| F4_1G | 2048 MB | 2.4 GHz | Automatic | 吃記憶體的 handler |
| B1 | 256 MB | 600 MHz | Basic/Manual | 輕量背景工作 |
| B2 | 512 MB | 1.2 GHz | Basic/Manual | B 系列預設 |
| B4 | 1024 MB | 2.4 GHz | Basic/Manual | 較重的批次作業 |
| B4_1G | 2048 MB | 2.4 GHz | Basic/Manual | 帶狀態的大型批次 |
| B8 | 2048 MB | 4.8 GHz | Basic/Manual | 最大後端等級 |
Standard 以 class 對應費率按 instance-hour 計費;F1 最便宜、B8 最貴。class 選太小會 OOM 與 500;選太大就花錢買閒置餘裕。看 Cloud Monitoring 的 appengine.googleapis.com/system/memory/usage 與 cpu/usage 來合理選型。
F = Front-end = Automatic scaling。B = Back-end = Basic 或 Manual scaling。 App Engine 強制這個配對。要對 automatic_scaling: 宣告 instance_class: B4 會在部署階段就被擋下。口訣「F 跑前線快、B 跑後台批」很好記。
流量分割與版本遷移
版本是不可變的
每次 gcloud app deploy 都會建立一個不可變的新 版本。版本同時存在;只有路由決定哪個版本接到使用者流量。兩個相關操作控制路由:
流量分割
gcloud app services set-traffic SERVICE --splits=v1=0.9,v2=0.1 把 90% 請求送到 v1、10% 送到 v2。App Engine 透過 --split-by 支援三種分割演算法:
- IP(
--split-by=ip):對 client IP 做雜湊。同一個使用者通常落在同一個版本,但 NAT 後的使用者會被混在一起。 - Cookie(
--split-by=cookie):App Engine 設一個對應版本的GOOGAPPUIDcookie。同一個瀏覽器永遠打到同一版本,A/B 測試需要維持 session 一致時用這個。 - Random(
--split-by=random):每個請求都重抽。適合 stateless 的負載測試。
Cookie/IP 分割接受小數兩位(例如 0.05),random 接受一位。
遷移 vs 分割
CLI 另外支援 漸進式流量遷移:gcloud app services set-traffic SERVICE --splits=v2=1 --migrate。App Engine 在數分鐘內把流量從現行版本逐步移到 v2,邊看健康狀況邊推進,錯誤竄升時自動回退。遷移只在 Standard 自動擴充並設定 warm-up 請求的 service 上有效。
gcloud app deploy 的 --no-promote 旗標是考試重點。預設情況下,gcloud app deploy 會讓新版本立即收 100% 流量。加上 --no-promote 就只部署、不切流量——你可以先用 https://VERSION-dot-SERVICE-dot-PROJECT.appspot.com 這個版本專屬 URL 跑煙霧測試,再決定切換。搭配 --no-stop-previous-version 確保隨時可瞬間回退。
自訂網域與 Managed SSL
用 gcloud 對應網域
預設情況下 App Engine 應用以 https://PROJECT.appspot.com 提供服務。Production 部署透過 gcloud app domain-mappings create example.com 對應自訂網域。App Engine 會回傳一組 DNS 記錄(A、AAAA、CNAME)讓你到註冊商加上去。
Managed SSL 與萬用字元限制
App Engine 透過 Let's Encrypt 自動為已驗證的自訂網域簽發並更新 managed SSL 憑證。萬用字元網域(*.example.com)需要自行上傳憑證,因為 Let's Encrypt 的萬用字元簽發要 DNS-01 驗證,App Engine 目前沒有自動化這部分。SSL 在 DNS 解析後幾分鐘到 24 小時內完成,憑證在到期前約 30 天自動更新。
在 app.yaml 強制 HTTPS
可以在 app.yaml 的 handler 層級強制 HTTPS:
handlers:
- url: /.*
script: auto
secure: always
secure: always 會在你的程式碼跑之前就在 load balancer 上把 HTTP 重新導向 HTTPS。
若要做跨區域韌性,或讓單一網域擋在多個 service 前面,把自訂網域指向 Global External HTTPS Load Balancer 加 Serverless NEG 而不是用 App Engine 原生 domain mapping。Load Balancer 帶來 Cloud CDN、Cloud Armor (WAF) 與 IAP,這些都不是 App Engine domain mapping 原生整合的功能。
Service 與 dispatch.yaml
一個 App Engine 應用 由一個以上的 service(舊名 module)組成。每個 service 是一個獨立的微服務,可以有自己的程式碼、runtime、scaling、instance class。第一個部署的 service 一定叫 default。Service 在專案層級共用配額、datastore、Cloud Tasks 佇列。
dispatch.yaml 是應用層級的路由表,覆寫預設 URL 模式(https://SERVICE-dot-PROJECT.appspot.com),把 URL pattern 對應到 service 上,讓單一 hostname 散到多個 service:
dispatch:
- url: "*/api/*"
service: api-service
- url: "admin.example.com/*"
service: admin-console
- url: "*/*"
service: default
以 gcloud app deploy dispatch.yaml 部署。第一個符合的規則優先,所以要從最具體排到最通用。dispatch.yaml 是應用層級不是 service 層級,部署一次就被所有 service 共用。
cron.yaml 排程工作
cron.yaml 註冊由 App Engine cron 服務排程觸發的 HTTP cron job:
cron:
- description: "夜間清理"
url: /tasks/cleanup
schedule: every 24 hours
target: worker-service
timezone: Asia/Taipei
- description: "每天 9 點報表"
url: /tasks/daily-report
schedule: every day 09:00
以 gcloud app deploy cron.yaml 部署。Cron 服務對指定 target service(沒指定就是 default)發出 HTTP GET。安全性靠檢查 X-Appengine-Cron: true header——這個 header 只有 App Engine cron 服務能設,外部呼叫者無法偽造。考試常考這種以 header 做認證的模式。
背後其實 App Engine Cron 就是 Cloud Scheduler 的同一套機制。Google 現在建議新的排程直接走 Cloud Scheduler,因為它可以排程 Cloud Run、Cloud Functions、Pub/Sub 與地端 HTTP endpoint。cron.yaml 仍可用,但問題若問「全新的多服務架構應該用什麼排程器」,正解是 Cloud Scheduler。
queue.yaml 與 Cloud Tasks
queue.yaml 設定的是 App Engine 經典的 Task Queue:HTTP 扇出的 push queue 與 worker 拉取的 pull queue。新的帳號改用 Cloud Tasks,那是改名後支援多 runtime 的後繼者。你仍可定義 queue 設定(速率、retry 參數、target service),但改以 gcloud tasks queues create 或 Cloud Tasks API:
queue:
- name: email-queue
rate: 10/s
bucket_size: 100
max_concurrent_requests: 20
retry_parameters:
task_retry_limit: 10
task_age_limit: 2d
min_backoff_seconds: 5
max_backoff_seconds: 300
max_doublings: 16
Cloud Tasks 佇列可以鎖定 App Engine HTTP handler 或任意 HTTPS endpoint(Cloud Run、Cloud Functions、外部服務)。對 App Engine 目標,Cloud Tasks 會附 X-AppEngine-QueueName 與 X-AppEngine-TaskName header;對非 AE 目標則附 OIDC token。
App Engine vs Cloud Run 取捨
兩者都是 serverless HTTP runtime,這是最常見的混淆。用以下決策表:
選 App Engine Standard:
- 應用符合官方支援的 runtime(Python、Java、Node、Go、PHP、Ruby),不需要自訂 Dockerfile。
- 需要內建功能像是
cron.yaml、dispatch.yaml、免費額度的每日 instance-hours、Memcache API(legacy)。 - 想用單一產品處理路由、service、排程、佇列,不想接一堆元件。
選 Cloud Run:
- 想部署任意 container image(任何語言、任何 base OS)。
- 需要完整控制 instance 並行(1 到 1000),同樣以秒計費。
- 需要請求 timeout 到 60 分鐘(App Engine Standard HTTP 上限 10 分鐘;Flexible 60 分鐘)。
- 需要 Direct VPC Egress(Cloud Run 原生支援;App Engine 需要 Serverless VPC Connector)。
- 需要 gRPC、HTTP/2 串流、WebSocket(Cloud Run 都支援;App Engine Standard 不支援 gRPC,HTTP/2 也有限制)。
選 App Engine Flexible:
- 既要自訂 Docker image 又要 App Engine 的 service/dispatch 模型。
- 團隊大量投資在
app.yaml慣例上,懶得遷移。 - 對大多數綠地 container workload,Cloud Run 已經是預設;Flexible 主要是過渡用的橋。
PCD 題目常以 App Engine 的詞彙包裝 Cloud Run 場景,看你是否抓到線索。看到「任何 container image」、「gRPC 串流」、「Direct VPC Egress」、「請求 timeout 到 60 分鐘」這類字眼就指向 Cloud Run,即便題目描述「Web app 加流量分割」(這是兩者都有的功能)。讀條件,不要被 buzzword 帶風向。
設定檔:app.yaml
app.yaml 是每個 service 的核心。一個典型的 Python 3.11 Standard 自動擴充設定:
runtime: python311
service: api
instance_class: F2
automatic_scaling:
target_cpu_utilization: 0.65
min_instances: 1
max_instances: 40
max_concurrent_requests: 80
env_variables:
DB_HOST: "10.0.0.5"
LOG_LEVEL: "INFO"
handlers:
- url: /static
static_dir: static
secure: always
- url: /.*
script: auto
secure: always
inbound_services:
- warmup
vpc_access_connector:
name: projects/PROJECT/locations/REGION/connectors/CONN_NAME
幾個值得留意的旋鈕:inbound_services: [warmup] 透過註冊 /_ah/warmup 請求在第一個真實請求之前預載程式碼。vpc_access_connector 讓 service 可以連到 VPC 的私有 IP,例如 Cloud SQL 的 private instance 或 Memorystore Redis instance。
記錄、監控與健康檢查
每個 App Engine 請求都會在 Cloud Logging 的 appengine.googleapis.com/request_log 留下結構化記錄。應用的 stdout/stderr 以 INFO 到 ERROR 等級被擷取。Logs Viewer 透過共用 trace ID 把請求記錄和應用記錄串在一起。
Cloud Monitoring 會自動建立 appengine.googleapis.com/http/server/response_count、response_latencies、instance_count,以及每個 instance 的記憶體與 CPU 用量儀表板。健康檢查分成 liveness_check(instance 還在嗎?)與 readiness_check(instance 準備好接流量了嗎?)。在 Flexible 上預設 endpoint 是 /liveness_check 與 /readiness_check,可在 app.yaml 調整。Standard 由平台自行處理健康檢查,你只看到結果。
App Engine 在新 instance 加入 load balancer 池前先發出的 GET /_ah/warmup 請求。在這個 URL 上實作 handler 來預載快取、預編譯樣板、開啟資料庫連線,可降低下一個真實請求的延遲。在 app.yaml 設 inbound_services: [warmup] 即可啟用。文件:https://cloud.google.com/appengine/docs/standard/configuring-warmup-requests
常見陷阱
- 上班時間 deploy 沒加
--no-promote,結果壞掉的新版本立刻接 100% 流量。永遠先煙霧測試。 - 選 Flexible 想「閒置省錢」——Flexible 不能 scale to zero。閒置便宜的答案是 Standard 或 Cloud Run。
- 把狀態存在 instance 檔案系統——instance 是短暫的。改用 Cloud Storage、Memorystore 或 Firestore。
- 忘了 App Engine 是綁區域的——在
gcloud app create --region=asia-east1時就要仔細選好。事後不能換區域,只能換 project。 - 對短請求濫用 manual scaling——manual instance 持續收費,且崩潰自動回復的行為跟 automatic scaling 不同。
考試重點
- 熟悉 三種 scaling 模式 各自的適用場景。
- 背熟 F-class / B-class 配對:F 對 automatic、B 對 basic 與 manual。
- 記住 Standard 可 scale to zero、Flexible 不行。
- 知道
--no-promote與流量分割旗標(--splits、--split-by、--migrate)。 - 記住三個搭配設定檔:dispatch.yaml(路由)、cron.yaml(排程)、queue.yaml(任務佇列,現在用 Cloud Tasks)。
- 知道 App Engine 應用以 project 為單位綁定單一區域,事後不能改。
- 知道什麼時候改建議 Cloud Run:任何 container、gRPC、60 分鐘 timeout、Direct VPC Egress。
- 知道
secure: always強制 HTTPS、X-Appengine-Cron: true保護 cron URL。
常見問題 (FAQ)
怎麼回退 App Engine 部署?
把流量切回上一個版本:gcloud app services set-traffic SERVICE --splits=PREVIOUS_VERSION=1。因為每次部署都產生一個不可變版本並留在 project(每個 service 上限 210 個版本),回退就只是切路由。沒有所謂的 rollback 指令——把流量 100% 切回前一版就是 rollback。
App Engine Standard 可以連到私有 Cloud SQL 嗎?
可以,透過 Serverless VPC Access connector。在 app.yaml 宣告 vpc_access_connector: name: projects/.../connectors/...。Standard service 的對外呼叫就會走 connector,連到 Cloud SQL、Memorystore,或 VPC 內任何資源的私有 IP。
App Engine Standard 的請求 timeout 上限是多少?
對 HTTP 請求,自動擴充 service 上限是 10 分鐘。Basic 與 Manual scaling 是 24 小時,但請求必須由單一 instance 處理。如果需要 serverless 模型下的 60 分鐘 timeout,Cloud Run 比較適合;若是長時間非同步工作,丟給 Cloud Tasks。
用 cookie 做流量分割跟 random 差在哪?
Cookie 分割(--split-by=cookie)會在回應上設一個 GOOGAPPUID cookie。同一個 cookie 的後續請求永遠路由到同一個版本,給使用者一致的體驗——A/B 測試要比較整個 session 的指標時必要。Random 分割每個請求重抽,適合負載測試或 version 之間在使用者面前完全等價的場景。
為什麼 App Engine 區域不能改?
App Engine 在第一次 gcloud app create --region=REGION 時建立區域專屬基礎設施(datastore namespace、區域 load balancer)。要遷到別的區域得開新 project、重新部署、改 DNS。這個鎖定是刻意設計的,為了讓延遲與資料駐留地可預測。
什麼時候 Cloud Run Jobs 比 App Engine 更適合做批次?
Cloud Run Jobs 適合一次性或排程的非 HTTP 批次任務:ETL 跑、影像處理、夜間報表產生。App Engine 是請求驅動的;連 cron job 都是發 HTTP 給某個 handler,handler 必須在請求 timeout 內結束。Cloud Run Jobs 自己有 task 模型,單次最久 24 小時,還能跨 task 並行。
App Engine 對 Google API 的對外流量會收錢嗎?
從 App Engine 對同區域內 Google Cloud 服務(Cloud Storage、BigQuery、Pub/Sub)的對外流量是免費的。跨區或到網際網路的 egress 以標準網路費率計費。Standard 每日免費 instance-hour 配額(F1 class 每日 28 instance-hours)以 project 為單位。
相關章節
延伸閱讀
- App Engine 官方文件:https://cloud.google.com/appengine/docs
- Standard 環境總覽:https://cloud.google.com/appengine/docs/standard
- Flexible 環境總覽:https://cloud.google.com/appengine/docs/flexible
- 版本之間的流量分割:https://cloud.google.com/appengine/docs/standard/splitting-traffic
app.yaml參考:https://cloud.google.com/appengine/docs/standard/reference/app-yaml- App Engine 與 Cloud Run 取捨:https://cloud.google.com/blog/topics/developers-practitioners/where-should-i-run-my-stuff-choosing-google-cloud-compute-option