cover

「用 Lambda 就不用管 server 了!」——這句話只對了一半。你確實不用管 server,但你要管的東西一點也沒少,只是換了一批。

結論:另一種形狀,先講怎麼分工

我的經驗法則:同步且短暫的用 Lambda,非同步或長時間的用 ECS,排程的用 EventBridge + Lambda。純 Serverless 很少見,生產環境多數是混的。

然後是四個 Serverless 特有的坑,每一個在 ECS 那個世界不存在:

  1. 一次呼叫最多 15 分鐘、同步回應最多 6 MB
  2. 並行預設 1,000,但前面的 API Gateway 預設放 10,000 rps 進來——官方自己寫這兩個對不上
  3. 接了 VPC 就沒有網路,要 NAT 或 endpoint;閒置 14 天後下一次呼叫會失敗
  4. 每個並行環境自己開資料庫連線——1,000 並行就是 1,000 條,這正是 RDS Proxy 被做出來的原因

一、什麼場景適合

適合不適合
短 request 的 API handlerWebSocket 之外的長連線、常駐 worker
S3 上傳後處理、佇列消費超過 15 分鐘的處理(影片轉檔)
排程任務需要 GPU、高 CPU 的計算
流量極不穩定、大多時候是零的東西流量穩定且高——ECS 塞滿反而便宜

最後一列是判斷的核心:Serverless 的價值是「不用就不付」,流量一直都在的服務用它不划算。

二、四個硬限制,全在官方配額頁

Lambda 配額:

1. 時間與大小:一次呼叫最長 900 秒(15 分鐘);記憶體 128 MB 到 10,240 MB,1,769 MB 等於一個 vCPU(CPU 跟著記憶體給,要更多 CPU 就加記憶體);同步呼叫的請求和回應各 6 MB,非同步 1 MB。超過 15 分鐘的工作不是 Lambda 的事,是 ECS RunTask 或 Step Functions 的事。

2. 並行:預設 1,000 個並行執行環境(新帳號更低,依用量自動放寬);每個 function 每 10 秒最多再長 1,000 個。官方在配額頁末尾自己寫了這段:「API Gateway 預設節流是每秒 10,000 個請求,而 Lambda 預設並行是 1,000。因為這個不對稱,可能有比 Lambda 能處理的更多請求從 API Gateway 進來。」——前門開得比後門大。上線前先申請並行配額,或在 API Gateway 那層把節流設到 Lambda 吃得下的量。

3. 接 VPC 的代價:官方——Lambda 預設有公網;一旦接到你的 VPC(為了連 RDS、ElastiCache),它就只能連 VPC 內的東西,要上網或叫其他 AWS 服務得走 NAT 或 endpoint,跟 第 2 篇的 private subnet 一樣。而且 Lambda 要為你建一張 Hyperplane ENI,建的時候 function 是 Pending 狀態,可能要幾分鐘;閒置 14 天 Lambda 會回收 ENI、function 變 Inactive,下一次呼叫會失敗,要再等 ENI 建好。低頻的 VPC Lambda(每月跑一次的報表)會踩這個。

4. 資料庫連線:每個並行環境是獨立的程序,各自開自己的連線。1,000 並行打 PostgreSQL 就是 1,000 條連線——比 ECS 擴容那個問題(第 6 篇)更兇,因為 Lambda 擴得更快、更碎。RDS Proxy 一開始就是為這個場景設計的;但 第 6 篇講的 pinning 問題在這裡一樣存在,PostgreSQL + prepared statement 要先量。

三、Cold start:存在,但先看你的 SLA

Function 一段時間沒被呼叫會被回收,下次呼叫要重新啟動——這就是 cold start。網路上那些「各語言 cold start 幾毫秒」的表不要信:沒來源,而且跟 image 大小、初始化程式碼、記憶體設定都有關,你自己量才算數。

我的判斷是:大部分場景不需要 Provisioned Concurrency。cold start 只在第一次和擴容時發生,之後的 warm invocation 是毫秒級。API 對延遲極度敏感才花這個錢:

aws lambda put-provisioned-concurrency-config --function-name hello-api \
  --qualifier prod --provisioned-concurrent-executions 5

四、EventBridge:事件匯流排與排程

EventBridge 是事件路由器:你的服務發一個事件,它依規則送到對的目標。跨服務的「領域事件」放這裡——訂單成立要同時通知點數、會員、推播,發一個事件、各自訂閱,比同步一個一個呼叫乾淨。

aws events put-rule --name order-created --event-bus-name my-app-events \
  --event-pattern '{"source":["my-app.orders"],"detail-type":["OrderCreated"]}'
aws events put-targets --rule order-created --event-bus-name my-app-events \
  --targets '[{"Id":"process-order","Arn":"arn:aws:lambda:ap-northeast-1:123456789012:function:process-order"}]'

排程用 EventBridge Scheduler,有時區參數——不用再寫 UTC 自己換算,直接寫 Asia/Taipei:

aws scheduler create-schedule --name daily-report \
  --schedule-expression "cron(0 9 * * ? *)" --schedule-expression-timezone "Asia/Taipei" \
  --target '{"Arn":"arn:aws:lambda:ap-northeast-1:123456789012:function:daily-report","RoleArn":"arn:aws:iam::123456789012:role/scheduler-role"}' \
  --flexible-time-window '{"Mode":"OFF"}'

非同步呼叫的失敗要有地方去:EventBridge 或 SQS 觸發 Lambda,處理失敗會重試,重試完還是失敗的訊息要接死信佇列或 destination——不接就是無聲消失。這條跟 ECS 那邊的佇列一樣,第 7 篇的「死信佇列非空就告警」在這裡同樣適用。

API Gateway 選 HTTP API 還是 REST API 一句話:要 WAF、usage plan、快取才選 REST,其他用 HTTP API。價格差看定價頁,這篇不寫百分比——那會變。

這篇的驗證標準

主張❌ 不算✅ 算
並行夠「應該夠」壓測到預期尖峰,看 Throttles 指標為零;API Gateway 節流設了
VPC Lambda 不會卡能連到 RDS閒置 14 天後呼叫一次,確認第一次失敗被重試接住
連線數不爆用了 RDS ProxyProxy 的釘住指標接近零;資料庫連線數 << 並行數
失敗不會消失有重試故意丟一則會失敗的事件,確認出現在死信佇列

實戰 Checklist

  • 超過 15 分鐘、超過 6 MB 的不用 Lambda
  • 並行配額申請到預期尖峰;API Gateway 節流對齊 Lambda 並行
  • VPC Lambda 有 NAT 或 endpoint;低頻的接一層重試
  • 連 RDS 走 RDS Proxy,且量過釘住率
  • 非同步觸發都接死信佇列;死信非空就告警

從 K8s 過來的話

Lambda 對 Knative Serving 或 OpenFaaS,SQS + Lambda 對 KEDA 的事件驅動擴縮,EventBridge Scheduler 對 CronJob,Step Functions 對 Argo Workflows。但心智差最多的是「沒有常駐程序」——K8s 上你習慣的連線池、記憶體快取、背景執行緒,在 Lambda 都不保證活過這次呼叫。這篇的四個坑有三個是從這裡來的。

相關文章


Serverless 最大的價值不是「不用管 server」,是「讓你只付你真正用到的錢」——前提是你的流量真的有時候是零。