cover

上一篇幫你選好架構了,這篇聊 Serverless 的「但是」——Cold Start 有多痛、隱藏成本在哪、還有實務上最常見的混合部署怎麼搞。

先講結論

  • Cold Start 的痛不在「有」,在於「不可預測」。Go/Rust < 100ms 幾乎無感,Java 可以飆到 5 秒
  • Lambda 最貴的不是 Lambda 本身,是 API Gateway 和 NAT Gateway
  • 生產環境不要全 Serverless 或全 Server——混合部署才是正解

Cold Start:到底有多慢?

Cold Start 發生在 function 閒置一段時間後,平台回收了執行環境,下一個請求進來要重新配置。延遲來自四個部分:

  1. 容器配置:50-200ms
  2. Runtime 初始化:10-100ms
  3. 程式碼 + 相依套件載入:50ms 到好幾秒
  4. VPC 網路介面配置(如果有開 VPC):額外 1-10 秒

不同語言的差距很大:

RuntimeCold Start 延遲
Go / Rust< 100ms(幾乎無感)
Python / Node.js100-500ms
Java / .NET500ms - 5s(JVM 啟動 + class loading)

你有沒有遇過 API 偶爾回應超慢,其他時候又很快?八成就是 Cold Start。用戶不會管你的技術架構,他們只知道「這網站有時候很慢」。

怎麼解?

Provisioned Concurrency(Lambda) — 預先保持 warm 實例,完全消除 Cold Start。但你要為預留的實例持續付費,等於把「按次計費」的優勢打了折扣。

Keep-warm 定時觸發 — 每幾分鐘用 CloudWatch Event 發一個空請求。便宜但不能保證流量暴增時所有實例都是 warm 的。

Cloud Run min-instances — 設 min-instances: 1,確保始終有 warm container。成本介於 Provisioned Concurrency 和完全冷啟動之間。

選輕量 runtime — 如果 Cold Start 真的是痛點,考慮從 Java 換到 Go。或者接受命運。


Lambda 的隱藏成本:比 Lambda 本身還貴的東西

我第一次看到 Lambda 帳單的時候很開心——35。NAT Gateway——12。

隱藏成本項目費用備註
API Gateway$3.50/百萬次往往比 Lambda 本身貴
NAT Gateway0.045/GBLambda 在 VPC 裡存取外部服務就要付
CloudWatch Logs按儲存量function 一多,log 量暴增

Lambda 本身按次計費很便宜,但周邊服務的費用可能把你的成本優勢完全吃掉。在決定用 Serverless 之前,把整個架構的成本都算進去,不要只看 Lambda pricing page 就覺得「太便宜了」。


混合部署:生產環境的正確答案

很少有團隊全部用一種架構。最常見的做法:核心 API 跑在 Server/CaaS,背景任務用 Serverless。

使用者 → ALB → Core API (ECS Fargate)
                ↓ publish event
              SQS → Background Worker (Lambda) → 圖片縮放、寄 email
              S3  → File Processor (Lambda) → 影片轉檔、CSV 匯入
         CloudWatch → Scheduled Tasks (Lambda) → 報表、清理、同步

原則很簡單:

  • 使用者直接互動的 API → Server/CaaS(低延遲、穩定)
  • 非同步、可容忍延遲的任務 → Serverless(省錢)
  • Cron job → Serverless(不需要 24 小時跑一台機器只為了每天執行 5 分鐘)
  • 用 Message Queue(SQS/Pub/Sub)解耦核心 API 和背景處理

實戰範例:Lambda + SAM Template

# template.yaml — SAM 部署範本
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
 
Globals:
  Function:
    Runtime: nodejs20.x
    MemorySize: 256
    Timeout: 10
    Environment:
      Variables:
        USERS_TABLE: !Ref UsersTable
 
Resources:
  GetUserFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: handler.handler
      CodeUri: ./src
      Events:
        GetUser:
          Type: Api
          Properties:
            Path: /users/{id}
            Method: GET
      ProvisionedConcurrencyConfig:
        ProvisionedConcurrentExecutions: 2
      Policies:
        - DynamoDBReadPolicy:
            TableName: !Ref UsersTable
 
  UsersTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: users
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: userId
          AttributeType: S
      KeySchema:
        - AttributeName: userId
          KeyType: HASH

注意 ProvisionedConcurrentExecutions: 2——這代表始終有 2 個 warm 實例等著,Cold Start 問題直接消失,但帳單會多一筆。

實戰範例:Cloud Run 部署

#!/bin/bash
PROJECT_ID="my-project"
SERVICE_NAME="user-api"
REGION="asia-east1"
IMAGE="gcr.io/${PROJECT_ID}/${SERVICE_NAME}"
 
docker build -t "${IMAGE}:latest" .
docker push "${IMAGE}:latest"
 
gcloud run deploy "${SERVICE_NAME}" \
  --image "${IMAGE}:latest" \
  --region "${REGION}" \
  --platform managed \
  --allow-unauthenticated \
  --memory 512Mi \
  --cpu 1 \
  --min-instances 1 \
  --max-instances 10 \
  --concurrency 80 \
  --timeout 300
 
SERVICE_URL=$(gcloud run services describe "${SERVICE_NAME}" \
  --region "${REGION}" --format "value(status.url)")
echo "Deployed: ${SERVICE_URL}"

--min-instances 1 確保至少一個 container 常駐,避免 Cold Start。--concurrency 80 表示每個 container 同時處理 80 個請求——Cloud Run 的 concurrency 模型和 Lambda(一個 instance 一個請求)不同,這點要注意。


容易忽略的坑

Lambda 記憶體和 CPU 是綁定的。 128MB 只給很少的 CPU,1,769MB 才等於一個 vCPU。如果你的 function 是 CPU 密集型(加解密、壓縮),增加記憶體反而可能降低總成本,因為執行時間大幅縮短。

Timeout + 重試 = 風暴。 Lambda 非同步呼叫預設重試 2 次。如果下游服務超時導致失敗,重試會讓下游壓力更大。設好 timeout + Dead Letter Queue,別讓重試變成 DDoS。

並發限制。 Lambda 每個 Region 預設 1,000 並發。一個吃流量的 function 可能擠掉同帳號其他 function 的額度。用 Reserved Concurrency 保護關鍵 function。

本地開發體驗。 SAM Local、Serverless Offline 都沒辦法完全模擬雲端環境(IAM、Event Source Mapping)。別太相信本地測試的結果,搭配一個真實的測試環境驗證。


延伸閱讀


Serverless 最大的謊言不是「不需要伺服器」,而是「不需要理解伺服器」。