examlab .net 用最有效率的方法,考取最有價值的證照
本篇導覽 約 20 分鐘

App Engine

3,820 字 · 約 20 分鐘閱讀 ·

PCD 學習筆記:Google App Engine Standard 與 Flexible 環境、F1/F2/F4/B1/B2/B4/B8 instance class、自動/基本/手動三種 scaling、流量分割、自訂網域與 managed SSL、dispatch.yaml、cron.yaml、queue.yaml,以及與 Cloud Run 的取捨。

立即做 20 題練習 → 免費 · 不用註冊 · PCD

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.yamlcron.yamlqueue.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: python311runtime: java17,Google 就給你一個帶該直譯器、加上你 requirements.txt 或 Maven/Gradle 相依套件的代管 base image。Standard runtime 由 Google 持續更新;要鎖定 minor 版本就重新部署。

Flexible 的代管與自訂 runtime

Flexible runtime 對 legacy 應用較友善。你可以選代管 runtime(runtime: nodejsruntime: python)讓 App Engine 自動產生 Dockerfile,也可以寫 runtime: custom 自帶 Dockerfilecustom 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_instancesmax_instances:autoscaler 的上下限。min_instances: 0 在 Standard 上啟用 scale to zero。
  • min_idle_instancesmax_idle_instances:預熱的閒置容量,用來吸收尖峰。

Basic scaling

Basic scaling 用於批次性或低流量工作。請求進來時 App Engine 才啟動 instance,閒置 idle_timeout 分鐘後關閉。你設 max_instancesidle_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/usagecpu/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 設一個對應版本的 GOOGAPPUID cookie。同一個瀏覽器永遠打到同一版本,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-QueueNameX-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.yamldispatch.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_countresponse_latenciesinstance_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.yamlinbound_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 分割(--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 為單位。

相關章節

延伸閱讀

官方資料來源

更多 PCD 主題