
這是情境線的第一篇。情境線回答「我要做 X」——不講元件是什麼(那是元件線),不講單一服務的坑(那是各平台的實作篇),講這件事會經過哪些元件、每個節點要決定什麼、三個平台各自怎麼做。
老闆說下個月流量會翻十倍。你的第一反應大概是「那把 task 數乘十」。問題是:翻十倍時第一個斷的,幾乎從來不是運算層。 運算層是最好擴的一層,所以它最不會先斷。先斷的是那些你沒想到會有上限的東西——出口的連線數、健康檢查的門檻、資料庫的連線池、第三方的速率限制。這篇是一條路徑:五個節點,走完你會拿到一張容量表,知道自己離哪面牆多遠。
2026 年 4 月我在自己的壓測平台上跑了一輪 infra 層的比較:Docker 4 CPU / 4GB,一支 Express 只有 ping、data、cpu-heavy 三個 endpoint,VU(virtual user,壓測工具模擬的同時在線使用者)從 10 拉到 1000。我原本以為第一個斷的會是 Node 單程序,症狀應該是 CPU 吃滿、p95(95% 的請求在這個時間內完成)拉長。
結果第一個斷的是 nginx,而且不是在 1000 VU 斷的,是一開就慢:直連 4,855 req/s,前面加一層 nginx 掉到 488,慢了 90%。CPU 沒滿、錯誤率沒跳,就是每個 request 都在等。原因是 nginx 到後端沒開 upstream keepalive,每個 request 重新握一次 TCP。開了之後 4,882,比直連還快 7%。一行設定的差別。
Node 單程序真正斷是在 1000 VU 才出現 9% 錯誤,比 nginx 這面牆晚得多。這就是我說「先斷的不是你以為的那層」的來源:我以為是運算層,結果是我自己加上去的那一層代理。
路徑:五個節點
① 量現在在哪 ──▶ ② 找第一個斷的 ──▶ ③ 查配額 ──▶ ④ 保護外部依賴 ──▶ ⑤ 產容量表
壓測 照斷裂順序找 帳號上限先於架構 那不是你能擴的 離牆多遠順序不能倒。沒有 ①,② 是猜的;沒有 ③,② 測到的可能是帳號配額而不是架構極限;沒有 ⑤,前面全部白做——下次再問「還撐得住嗎」又要重來。
① 量現在在哪:四種壓測各回答一題
「壓測」不是一個動作,是四個:
| 型態 | 怎麼打 | 回答什麼 |
|---|---|---|
| Load | 預期尖峰量,持續 10–30 分鐘 | 尖峰時各項指標正不正常 |
| Stress | 逐步加到壞掉為止 | 哪個元件先斷、斷在多少 |
| Spike | 零 → 尖峰,一秒內 | 擴容來不來得及 |
| Soak | 中等量跑幾小時 | 有沒有慢性洩漏(連線、記憶體、檔案控制代碼) |
「翻十倍」最危險的三個瓶頸——出口連線上限、擴容速度、webhook 雪崩——分別要靠 Soak、Spike、Spike 才測得出來。只做 Load 的結論是「現在沒事」,那不是這個情境要的答案。
決策點:壓哪種?答案取決於你最怕哪個斷——不確定就 Stress,它會告訴你順序。
三個平台:
- 地端 / 自架:k6 + InfluxDB + Grafana,你自己的壓測平台就是這套。壓測機放同一個網段,避免把網路當瓶頸測。
- AWS:同一套 k6,但壓測機放同 VPC 同 AZ 走內部 IP——不然壓測流量本身在燒 NAT 處理費和跨 AZ 費,一場 stress test 可能比一天正常流量還貴。
- Staging 的結果不能直接推到 prod,因為兩邊有四個地方不一樣。一、配額:staging 帳號通常更小(見 ③)。二、資料量:同一條查詢,在小表快,在大表慢。三、NAT 和 ALB 是不同的實體:兩邊平常吃的流量不同,被壓之前的預熱狀態也就不同。四、外部依賴:staging 接的是沙盒,金流沙盒的延遲跟正式環境不同。所以 staging 用來找相對瓶頸,也就是哪個先斷。絕對數字要在 prod 規模的環境測,或在 prod 的離峰時段小心測。
- 自架的壓測平台拿來測 AWS,至少要補三件。一、場景模板要涵蓋四種型態,不要只有 Load。二、同時撈 CloudWatch(AWS 的監控服務):NAT、RDS、ALB 的指標在 AWS 那邊,壓測工具自己看不到。三、加一條「撞到配額」的判斷:錯誤率跳、CPU 卻沒滿,先懷疑配額。
- GCP:(未實作,留空)
我自己那一輪的數字:nginx 不開 keepalive 那組我還是硬跑到 1000 VU,但第一次跑的結果檔裡 10 VU 的 p95 就已經是 49 秒,後面的 VU 級距沒有意義,只是確認它不會自己好。pm2-4(用 pm2 開 4 個 worker 程序)只跑到 100 VU,是因為那一輪只想看 cpu-heavy 的差距:單程序 10 req/s、4 workers 23 req/s,翻倍就夠我下判斷了。完整的 1000 VU 是另一輪垂直擴展測試補的:單程序 4,888、2 workers 4,892(沒差)、4 workers 9,461(+93%,1000 VU 零錯誤)。keepalive 差多少:488 對 4,882,十倍。
② 找第一個斷的:照斷裂順序,不是照直覺
「翻十倍」時各層斷裂的典型順序,跟直覺相反——最好擴的最後斷:
- 出口——所有對外呼叫收斂到一個出口時,連線數有上限。AWS 的 NAT gateway 每個 IP 對每個目的地 55,000 條同時連線(官方數字);自架的 NAT 是 conntrack 表(Linux 核心追蹤每條連線的表)和 ephemeral port(對外連線時系統臨時配給你的來源 port)。撞上限的樣子:錯誤率跳,但運算層 CPU 一點都不高。
- 擴容速度——不是上限,是從決定擴到真的在服務之間的時間。AWS 上 health check 預設連續 5 次、每 30 秒才算健康,新 task 兩分半才收流量;自架 K8s 的 readiness 也有同樣的參數,只是預設值不同。
- 東西向——服務互打如果繞負載平衡器,流量翻十倍時延遲、容量、費用三重放大。
- 資料庫連線——50 個 task × 每個 20 條 = 1,000 條連線,PostgreSQL 直接爆。這條地端和雲端一模一樣。
- webhook 雪崩——第三方回呼成群結隊來,同步處理不完 → 超時 → 對方重送 → 滾雪球。
- 靜態資源——沒有 CDN 的話,圖片和 JS 跟 API 搶同一個入口。
決策點:先修哪個?常見的答案是照投報率,不照斷裂順序——出口的 endpoint、CDN、webhook 非同步這三個改動最小、效果最大,先做;連線池和熔斷第二;東西向脫離負載平衡器最後,那是架構債。我自己的答案在文末,跟這個不一樣。
三個平台:
-
地端:出口是你自己的防火牆或 NAT 主機,上限在 conntrack;擴容速度看 readiness probe 和 image 有沒有預拉;資料庫連線用 PgBouncer。
-
AWS:出口是 NAT gateway(加副 IP 到 8 個是正解,不是多開 NAT);擴容看 target group 的 health check 門檻;資料庫連線用 RDS Proxy——但 PostgreSQL 配 prepared statement 的話 Proxy 幾乎沒效,先量釘住率。細節在 AWS 系列第 2、4、6 篇。
下面照上面六項逐條看,每項分修法、測法、撞到時長什麼樣。
先講清楚:下面的測法與「撞到時」的樣子是照架構推的,還沒在 AWS 上實跑過。
- 出口
- 修法:加副 IP。公有 NAT 預設只能綁 2 個 EIP(Elastic IP,AWS 配給你的固定公開 IP),要加到 8 個得先申請調高配額(官方文件)。
- 修法:呼叫外部 API 改用長連線,也就是 keep-alive(一條 TCP 連線連續送很多個請求)加上連線池複用。每次呼叫都開新連線,是最快撞上限的寫法。
- 測法:用 Soak 持續對同一個外部目的地開連線,看 NAT 的兩個指標。一個是
ErrorPortAllocation:NAT 配不出來源 port 的次數,大於零就是同時開了太多連線(官方指標說明)。另一個是ActiveConnectionCount:目前開著的連線數。 - 撞到時:錯誤率突然跳,task 的 CPU 卻沒滿。
- 擴容速度
- 修法:已知的尖峰用排程擴容事先預熱,也就是尖峰來之前先把 task 開好。
- 修法:image 瘦身,縮短拉取時間。
- 測法:用 Spike 從最小 task 數瞬間打到尖峰,量從「觸發擴容」到「新 task 變成
RUNNING且通過健康檢查」花了幾秒。 - 撞到時:前幾分鐘 5xx,之後恢復。
- 東西向
- 修法:內部呼叫不要繞 ALB。
- 測法:用 Load 對比兩種走法的 p99(99% 的請求在這個時間內完成)。一種是「直連」,服務直接打另一個服務;另一種是「經內部 ALB」。同時看內部 ALB 的
TargetResponseTime(ALB 等後端回應花的時間)和 LCU(ALB 拿來計費的容量單位)。 - 撞到時:p99 比預期多出一段固定延遲。
- 資料庫連線
- 修法:讀多寫少的資料(商品、分類、設定)進快取。沒有快取層的話,每個請求都會穿到資料庫。
- 測法:用 Stress 逐步加 task 數,看 RDS 的
DatabaseConnections(目前的連線數),和 Proxy 的釘住數DatabaseConnectionsCurrentlySessionPinned(被綁死在某一個 client、沒辦法讓別人共用的連線數)。 - 撞到時:連線數一路線性上升,到
max_connections(資料庫允許的連線上限)之後全掛。
- webhook 雪崩
- 修法:改非同步,回呼先進佇列再處理。
- 測法:用 Spike 灌回呼,看佇列深度(排隊等處理的數量)、處理延遲、對方的重送次數。
- 撞到時:佇列深度暴增之後降不下來。
- 靜態資源
- 修法:放 S3 + CloudFront(AWS 的 CDN)。順便讓 TLS 在邊緣終止,也就是 HTTPS 的加解密在 CloudFront 那端做完,ALB 壓力少一大截。
- 測法:用 Load 對比有沒有 CloudFront,看源站(CDN 背後真正出內容的地方)的請求數和傳出流量。
- 撞到時:源站請求數沒降,就是 CDN 沒接好。
- 出口
-
GCP:(未實作,留空)
跟 K8s 經驗的關係:上面六項裡,哪幾項的直覺可以直接帶過來?
資料庫連線(連線池與快取)跟 K8s 完全一樣,④ 節的逾時與熔斷也一樣。webhook 改非同步同樣能直接帶過來,它是架構問題,跟平台無關。
帶不過來的有兩種。出口(VPC Endpoint)、靜態資源(CloudFront)、擴容速度(排程預熱)這三項的做法是 AWS 特有的。東西向(內部呼叫脫離 ALB)則是 ECS 才有的問題,K8s 天生沒這毛病。
③ 查配額:帳號上限先於架構極限
這一步地端沒有,雲端才有,而且最容易讓 ② 的結論是錯的:你以為測到架構的極限,其實是撞到帳號配額。
AWS 的例子:新帳號 Fargate 預設只有 6 個 vCPU(會依用量自動放寬,但不會剛好在你需要的那一刻);一個 ALB 最多 100 個 target group 而且不可調;Lambda 預設並行 1,000 但前面的 API Gateway 預設放 10,000 rps 進來。這些數字全在官方配額頁,壓測前先查。
決策點:要不要提前申請?有已知尖峰(促銷、開賣)就申請,不等自動放寬。
三個平台:
- 地端:沒有配額,只有硬體——但有另一種「配額」:機器買了要等、機房 uplink 頻寬是固定的。
- AWS:Service Quotas 主控台,或
describe-account-limits。可調的申請,不可調的(🔒)是真天花板,架構要繞。壓測前確認 vCPU、task 啟動速率、ALB target 數都夠;staging 帳號的配額通常比 prod 小,在 staging 撞到的牆可能只是配額。 - GCP:(未實作,留空)
④ 保護外部依賴:那不是你能擴的東西
你的服務可以擴到一百個實例,金流閘道還是原來那個速度。這裡要的不是擴容,是不要讓對方的慢變成你的全掛:
- 逾時——沒設就是無限等
- 熔斷——連續失敗就先別打,別把執行緒全卡在那
- 每個依賴各自一組連線池——別讓金流變慢把會員登入一起拖死
- 能非同步的進佇列
決策點:熔斷門檻設多少?看對方的 SLA,不是看你的。
這一節三個平台的做法幾乎一樣,因為它是應用層的事,不是基礎設施的事。差別只在佇列用什麼:地端 RabbitMQ 或 Redis Streams,AWS 用 SQS 配死信佇列。
測法也一樣:用 mock 模擬對方變慢,看自己的 p99 和執行緒池占用。撞到的樣子:對方一慢,自己全部跟著變慢——那就是沒有隔離。
⑤ 產容量表:不留下這張,前面白做
壓完要留下這張表,每一列一個元件。表頭的 Headroom 是「餘裕」:離天花板還剩多少空間,算法在表下面。
| 元件 | 天花板(來源) | 目前尖峰 | Headroom | 預估撞牆時間 | 動作 |
|---|---|---|---|---|---|
| (出口連線) | AWS:NAT 每個 IP 對同一目的地 55,000 條(官方) | 加副 IP / keep-alive | |||
| (運算配額) | AWS:Fargate vCPU 帳號配額(查) | 提前申請 | |||
| (資料庫連線) | AWS:RDS max_connections(查) | RDS Proxy | |||
| (負載平衡器規則 / 後端組數) | AWS:每個 ALB 100 個 target group(官方,🔒 不可調) | 拆 ALB | |||
| (佇列吞吐) | AWS:SQS FIFO 一般模式每種動作 300 TPS(官方) | 開高吞吐模式+分散 message group |
「天花板」欄要寫來源——官方配額頁、或你自己壓出來的數字。「預估撞牆時間」= 依業務成長率推算,這才是容量規劃,前面都只是量測。
這張表回答容量規劃的三個問題:
- 現在用了多少?看「目前尖峰」。
- 尖峰會到多少?看業務預估,例如促銷、開賣、月底結帳。
- 離天花板多遠?看 Headroom,Headroom = 天花板 − 尖峰預估。
天花板可以查文件或壓出來。前兩題沒有壓測,就是猜的。
決策點:表上哪幾列要進告警?Headroom 最小的前三個。
我的答案是逐項修:照斷裂順序一個一個來,不挑投報率高的先做。這跟上面「照投報率」的常見建議相反,我知道。
理由是我自己的水平擴展數據:1 台 6,309 req/s、1000 VU 有 10% 錯誤;加到 2 台 9,685、錯誤消失;加到 4 台 9,498,沒有更好,因為牆已經換成 DB 連線池了。每修掉一面牆,下一面牆就換位置,事先算好的投報率在修完第一個之後就不準了。所以我寧願每次只修當下撞到的那一面,修完重量一次,再決定下一個。你可以反駁我:這樣慢。對,但我還沒看過哪次「先修投報率最高的」在第二輪不用重算。
怎麼證明做到了
不是「有壓測報告」就算,是這四件:
- 容量表每一列都有數字,而且最近一季量過
- Spike test 實測「觸發擴容到真的在服務」的秒數,小於業務能容忍的時間
- 壓測前查過配額、外部依賴有 mock、結果同時有壓測工具和平台指標兩邊的數據
- 故意讓一個外部依賴變慢,確認只有它的呼叫變慢,其他功能沒跟著死
這條路徑連到哪
- 元件線:出口、負載平衡器、連線池、佇列這些元件是什麼、綁在哪——AWS 元件地圖
- 坑:ALB 的五個預設值(health check 150 秒、target group 上限)、 ElastiCache(RDS Proxy pinning)、成本管理(壓測流量的帳單)
- 地端的完整數據:跨層容量規劃、最佳化路線圖,本文開場那幾個數字都出自這一輪壓測
- 回 Infra 總覽 的三入口,或 策展
流量翻十倍不是把數字乘十。是找出第一面會撞的牆,然後知道自己離它多遠。
