01

擴縮是慢半拍的回授

desired 算得出來,容量卻要等下一期取樣與暖機才接得住這一波。

自動擴縮不是一顆「把機器變大」的按鈕。它是一條控制迴路:取樣、跟目標比、增減實例、等新實例真的能接流量、再取下一次樣。面試要講的不是演算法名稱,而是這條迴路比流量慢在哪裡,慢的那段由誰吸收。

目標追蹤(target tracking)把「要幾台」收成一個除法。訊號是這一期看得到的負載,單台容量是它在目標利用率下願意承擔的量,不夠就向上取整:

desired = ceil( signal / (unitCapacity × targetUtilization) )

單台每分鐘能做 200 筆請求、目標利用率 60% 時,每台只計畫承擔 120 筆。訊號是 400,desired = ceil(400 / 120) = 4。訊號衝到 1600,desired = ceil(1600 / 120) = 14。公式本身沒有時間;時間在「訊號是上一期的」以及「新的那 10 台還在暖機」。

flowchart LR
    Load[負載] --> Metric[指標]
    Metric --> Policy[政策]
    Policy --> Boot[啟動]
    Boot --> Cap[容量]
    Cap --> Metric
  

容量指回的是指標,不是負載。多出來的實例要等下一期取樣才會改變決策,所以這一期的尖峰用不到這一期才啟動的容量。

關鍵擴出去成功,只代表 desired 改了

ASG 或 ECS service 的 desired count 上升,不表示這一分鐘的請求有地方去。RUNNING 還沒跟上、或新任務還在拉映像檔時,溢出已經記在延遲、佇列長度或拒絕數上。

一句話答法自動擴縮是有延遲的回授:台數由訊號除以單台目標容量再向上取整,但新容量要等取樣週期與暖機,接不住決策當下的那一波。
02

指標會遲到,也會被截斷

CPU 到頂之後不再告訴你超載多少;佇列在機器還是零台時就存在。

政策只看得到你餵它的訊號。訊號選錯,公式再漂亮也是在追一個謊。實務上三條常用訊號差在「會不會飽和」以及「沒有實例時還看不看得到」。

訊號 看得到的量 飽和之後 零實例時
CPU / 記憶體 已在跑的實例有多忙 停在 100%,超載幅度消失 沒有樣本,政策不會醒
每目標請求數 ALB 實際打進來的請求 不截斷,尖峰高度還在 沒有目標就沒有「每目標」;群組至少留 1,或改看負載平衡器總請求
佇列堆積 可見訊息、或每任務堆積 堆積會繼續長,幅度還在 佇列還在,可以從 0 叫醒

CPU 是落後且會截斷的忙碌度;請求率看得到高度;佇列比 CPU 更早,因為工作在處理器忙起來之前就已經堆在外面。

截斷會把一次補齊變成連乘。沿用第 1 節的單台 200、目標 60%:真實需求停在 1600,未截斷的 desired 是 14。若訊號是 CPU,觀測值不會高過「目前就緒台數 × 200」。從 4 台就緒開始,每一輪都假設上一輪加進去的機器已經能服務:

輪次 期初就緒 CPU 觀測 desired
0 4 min(1600, 800) = 800 7
1 7 min(1600, 1400) = 1400 12
2 12 1600(容量已超過需求) 14

同樣 1600 筆,請求率指標第一輪就會把 desired 訂在 14。CPU 要連走三輪,每輪之間還隔著一個取樣週期加暖機。

請求率也有自己的謊:它假設每筆請求一樣貴。健康檢查、快取命中與一隻會打跨服務的重端點混在同一個 ALBRequestCountPerTarget 裡,目標追蹤會對平均成本擴,對那隻重端點仍然不夠。佇列指標的經典寫法是可見訊息數除以執行中任務數,目標是「每任務可接受的堆積」。執行中任務為 0 時這個除法沒有定義,必須另給一個醒法(直接用可見訊息數,或在除數為 0 時填一個很大的堆積),否則群組會停在 min = 0。

陷阱不要用延遲或錯誤率當擴縮指標

p99 與 5xx 是結果,不是負載。下游壞了也會讓它們變差,政策會在錯誤的方向加機器。擴縮對不齊的那一段,用佇列吸收或用熔斷、艙壁隔離與降級拒絕;那不是把 desired 再乘二能補上的。

一句話答法CPU 到 100% 就不再說超載多少,只會以大約 100/目標 的倍率逐輪追;請求率看得到高度,佇列還能在零實例時把群組叫醒。
03

目標追蹤、冷卻與震盪

冷卻是故意少做一次決策;它同時擋住擴與縮。

EC2 Auto Scaling Group 上常見三種動態政策。Simple 過了門檻就做一次固定增減,然後進入 cooldown。Step 依超出的幅度分級(例如 CPU 高一點加 1 台、高很多加 3 台),同樣受 cooldown 或暖機約束。Target tracking 只收一個目標值,由服務去算 desired;ASG 這條路不使用群組預設的 cooldown(預設 300 秒那條是給 simple 用的),改看 EstimatedInstanceWarmup。暖機中的實例不計入 CloudWatch 平均,避免新機器的低使用率把平均值拉下來、立刻觸發縮容;但計算「還要加幾台」時把它們算進已啟動的數量,避免同一波缺口連開兩輪。

ECS Service Auto Scaling 走的是 Application Auto Scaling,不是 ASG 那組 API。目標追蹤可以分別設擴出冷卻與縮入冷卻,預設指標有 ECSServiceAverageCPUUtilization、記憶體,以及 ALBRequestCountPerTarget。名字像,時間常數不要混:ASG 用暖機穩定,ECS 仍讓你對擴與縮各設一段冷卻。

Kubernetes 的對應物是 HPA 的 behavior。縮容穩定視窗預設 300 秒:這段時間內看到的建議值取最保守的那個,避免一個尖峰過去就立刻拆掉 Pod。物件本身(指標來源、行為欄位)由容器編排負責;這裡只借它的時間常數,說明「穩定視窗」與 cooldown 是同一種阻尼。

sequenceDiagram
    participant L as 負載
    participant M as 指標
    participant P as 政策
    participant N as 新實例
    L->>M: 尖峰到達
    M->>P: 上期超標
    P->>N: 啟動
    Note over N: 暖機,尚不能接
    N-->>P: 納入已啟動數
    P->>M: 下期才重看
  

決策用的是上一期指標。新實例在暖機結束前既接不到流量,也不該拿來稀釋平均值;下一次能不能再動,還要看冷卻過了沒有。

阻尼的代價要用兩次尖峰才看得清楚。中間的山谷若允許縮容,第二個峰會從很小的台數重新暖機,方向反轉變多、溢出變多。若冷卻把山谷蓋住,第一個峰加上去的機器還在,第二個峰接得住,反轉消失,空轉上升。第 4 節把雙尖峰的冷卻從 0 調到 5 時,反應式的方向反轉從 3 次掉到 0,溢出從 2400 降到 1200,空轉從 6000 升到 12800。冷卻不是「讓你錯過第二個峰」的開關,它是把兩次峰黏成一次容量、用錢換少震一次。

陷阱冷卻也會把擴容切成好幾刀

它不只阻止縮。上一刀擴完之後,下一刀要等冷卻結束;等得到的時候負載可能已經從尖峰掉下來,最終台數會小於尖峰當下的 desired,溢出卻已經記完。預設那組單尖峰裡,未截斷的尖峰 desired 是 14,反應式實際最高只到 10,就是第二刀被冷卻推到需求已回落之後。

一句話答法冷卻和穩定視窗是故意少做一次決策,用來換掉擴了又縮;代價是下一次反向的流量得自己扛過整段冷卻,下一次同向的擴容也可能被切晚。
04

同一條尖峰,兩種政策

反應式用上一分鐘的誤差去追;預測用提前量去買尖峰前的空轉。

下面這組並排把第 1 到第 3 節的延遲放進同一條 16 分鐘的曲線。左欄是反應式目標追蹤,右欄是帶提前量的預測。兩邊共用流量、目標利用率、冷卻與暖機;提前量只餵右欄。縱軸共用,柱高可以直接比。

規則就這些,沒有另一套隱藏劇本。每格 1 分鐘,單台就緒後每分鐘 200 筆。開場台數用基準負載套第 1 節的公式,所以預設從 4 台就緒開始。每一格先決策、再接這一格的流量。反應式的訊號是上一格的需求(第 0 格用基準負載);預測的訊號是從這一格起、往後「提前量」格以內(含這一格)的最大需求。暖機 W 表示這一格啟動的實例從第 t+W 格開始可服務,W = 0 就是當格計入。冷卻 C 表示一次增減之後,要再等超過 C 格才能下一次;C = 0 時下一格就能再動。CPU 截斷把訊號壓到「決策前已就緒的台數 × 200」。縮容先取消還沒就緒的,再減正在服務的,最少留 1 台。已啟動總數(含暖機)拿來和 desired 比,所以同一波不會連開兩輪;訊號用的是外部需求,不會被新實例的低使用率稀釋。

互動 · 反應式對預測 改參數,兩邊同時重算

綠柱是這一分鐘真正能接的容量,黃柱是已啟動但仍在暖機的部分,藍線是需求。藍線上的紅點表示需求高過綠柱。提前量只作用右欄。

反應式 只看上一分鐘
反應式容量與需求
溢出請求0
空轉(請求空位)0
實例分鐘0
方向反轉0
預測 看未來提前量內的最大需求
預測容量與需求
溢出請求0
空轉(請求空位)0
實例分鐘0
方向反轉0
需求 可服務 暖機中 溢出
調整參數後,這裡比較兩邊的溢出與空轉。

橫軸是第 0 到第 15 分鐘,兩邊共用縱軸。預設讀數:反應式溢出 1200、空轉 7000、實例分鐘 91;預測溢出 0、空轉 10200、實例分鐘 121。右欄用尖峰前的空轉換掉溢出。

三組對照都從重置後的預設出發,只改講到的旋鈕。

提前量與暖機。預設提前量 3、暖機 2,右欄溢出是 0。暖機改成 4、提前量維持 3,右欄溢出變成 200,左欄從 1200 升到 1600。提前量改成 0、暖機回到 2,右欄溢出是 600,左欄仍是 1200:提前量 0 仍看得到這一分鐘,反應式只看上一分鐘,兩欄不會重合。提前量要蓋住的是「決策到可服務」的整段,不是跟暖機數字比大小就結束;中間若被冷卻擋下一刀,蓋住的時間還要再長。

雙尖峰與冷卻。流量改成雙尖峰(兩個中心相隔 5 分鐘)、冷卻改成 0:左欄方向反轉 3 次,溢出 2400,空轉 6000,兩個峰都從低檔重新暖機。冷卻改成 5:左欄反轉 0 次,溢出降到 1200,空轉升到 12800。右欄在提前量 3 時兩種冷卻的溢出都是 0,差別在反轉與空轉,不在有沒有接住。

CPU 截斷。流量改成階梯維持,冷卻與暖機都歸零,尖峰位置維持 6。請求率下,左欄在階梯出現的下一分鐘從 4 直接到 14,溢出 800;右欄因提前量 3,在階梯開始前就到 14,溢出 0。改成 CPU 截斷後,左欄變成 7、12、14,溢出 1000;右欄也是 7、12、14,只是發生在階梯之前,溢出仍是 0。截斷限的是看得到的高度,提前量限的是什麼時候開始看。這組 4、7、12、14 就是第 2 節那張表。

一句話答法反應式必漏掉至少一個取樣延遲;預測是用提前量把容量買在尖峰之前。提前量短於暖機加冷卻擋下的那一刀,右欄一樣溢出,只是比左欄少。
05

排程、預測、縮到零與垂直擴縮

看得到的日週期用時鐘;重複出現的形狀用預測;單行程尺寸不是平行度。

第 4 節的右欄是把預測收成「提前看 N 分鐘的最大值」。產品裡的預測比這個粗,也比這個依賴歷史。AWS Predictive Scaling 用最多約兩週的負載去推未來約 48 小時,排程緩衝(scheduling buffer)決定提早多久把容量準備好,這段緩衝要不短於暖機。Forecast only 只給你看曲線;forecast and scale 會真的改 desired。它通常與一條目標追蹤並存,追蹤負責預測沒吃掉的殘差。並存時把追蹤的縮容關掉(DisableScaleIn),否則追蹤會把預測剛墊上去的緩衝縮掉。

政策 何時動作 提前量 主要失敗
Target tracking 指標偏離目標 沒有,暖機後才補上 尖峰比迴路短就溢出;CPU 會截斷
Step / Simple 告警門檻 沒有,冷卻期間拒絕下一次 門檻太密就震;太疏就補太慢
Scheduled 時鐘 你自己把 min 提前到暖機之前 日曆外的尖峰完全沒反應
Predictive 歷史負載的預測 緩衝應蓋過暖機 沒見過的活動日會低估;和追蹤同時縮會拆掉緩衝
佇列 backlog 每任務堆積 訊息在 CPU 忙之前就看得到 執行中任務為 0 時除法沒定義
垂直(改規格 / VPA) 單行程長期不夠 通常要替換實例或重啟 不增加平行度,接不住 QPS 尖峰

排程與預測買的是提前量;目標追蹤買的是殘差。垂直擴縮改的是單台尺寸,不在這條台數公式裡。

縮到零是目標追蹤的死角,不是一種更積極的目標。CPU 或記憶體在一台實例都沒有時沒有樣本,政策永遠看不到該擴。排程可以在營業開始前把 min 從 0 抬到 1,讓樣本重新出現;佇列指標則因為訊息不依賴消費者而能自己醒。第一台醒過來仍要付冷啟動:拉映像、建連線池、填本地快取,這就是暖機的極端形。函式平台依並發自動長實例,是另一種控制迴路,不在這篇的 desired count 裡展開。

垂直擴縮(換更大的實例規格,或 VPA 調整 Pod 的 request)解決的是單行程記憶體或單執行緒上限。它通常要替換或重啟,對一個比暖機還短的 QPS 尖峰沒有幫助。水平擴縮才是在加平行度。兩者可以同時存在:垂直把單台容量定在一個不會頻繁 OOM 的尺寸,水平再依請求或佇列改台數。不要用垂直擴縮去回答「尖峰時要加幾台」。

一句話答法排程解決日曆看得到的峰,預測解決重複出現的形狀,目標追蹤補殘差;縮到零只能掛在不依賴實例本身的指標上,垂直擴縮則不提供平行度。
06

任務層與節點層要相加

desired 上去但 running 不動,先看有沒有機器可排,再看應用。

到目前為止的台數,預設「加一台就有地方跑」。自管節點時這不成立。任務層與節點層各是一條迴路,暖機串聯,不是取最大值。

請求或佇列
真實負載在這一層。CPU 只是下面兩層忙不忙的投影。
任務層
ECS Service Auto Scaling、HPA:改的是 desired tasks / pods。快的那一條,仍要付映像與暖機。
節點層
ASG、Cluster Autoscaler、Karpenter:補的是機器。慢的那一條,開機常常是數分鐘。

上面兩層都會擴,不代表容量相加。任務 desired 上升而節點滿了,新任務停在 PENDING,就緒容量維持原樣。

ECS on EC2 是這兩條的標準拼法:服務自動擴縮改任務數,capacity provider 的 managed scaling 再依預留容量去推 ASG。Fargate 把節點那一條收進平台,你只剩任務啟動的暖機。運算模型光譜比較的是這幾種運算你還擁有哪一層;這裡只計「你還握著 desired count 時,延遲加在哪」。GKE Standard 是 HPA 加節點池自動擴縮;Autopilot 同樣把節點層收走。GCE 託管實例群可以做預測式擴縮,GKE 的 Cluster Autoscaler 本身仍看排不進去的 Pod,不預測請求量。

Cluster Autoscaler 不看 CPU 目標。它看的是因 CPU 或記憶體 request 放不進現有節點的 Pod,以及節點是不是空到可以收。縮節點還有一段 unneeded 等待(預設約 10 分鐘)和 PDB。Karpenter 依 pending Pod 的資源形狀挑機型,consolidation 比傳統 autoscaler 積極,觸發條件仍是「有 Pod 排不進去」,不是負載預測。Pod 的 request 設太高,HPA 覺得利用率還低、節點卻先滿;設太低,很多 Pod 擠進同一台,節點層很晚才被叫醒。這組數字要跟 HPA 的指標一起看,不是只看叢集 CPU。

關鍵先分層,再談要不要再擴

desired 上升、running 不動:節點層或映像拉取。running 已跟上、p99 仍壞:應用、鎖、或下游。後者繼續擴任務只是把更多連線打進已經壞的依賴。

一句話答法任務層與節點層各付一次暖機,Fargate 或 Autopilot 只是把節點那一次收進平台;兩層都在時,尖峰要同時蓋過兩者,不是只調 HPA。
07

面試時怎麼取捨

先問尖峰比暖機長還是短、訊號會不會截斷、節點是不是你的。

聽到的需求 先問 傾向
每天固定時段變忙 峰比暖機長還是短 短就用排程把 min 提前抬起;長再加目標追蹤吃噪音
不定期短尖峰 暖機幾分鐘、指標會不會到頂 預測或排程;純 CPU 反應式一定慢半拍
佇列消費者 堆積的是訊息還是 CPU 每任務堆積,並定義零任務時怎麼醒
擴了還是逾時 running 有沒有跟上 desired 沒跟上先看節點與映像;跟上了就看下游,改走降級而不是再擴

四種開場白對應四個先問的問題。沒有「一律目標追蹤 CPU 50%」這種答案。

可以主動講的取捨就三個數字:目標利用率、冷卻(或穩定視窗)、暖機。目標調低,台數變多、比較不會截斷,帳單上升。冷卻調長,反轉變少、山谷裡的空轉變多,下一次擴容也變慢。暖機若估太短,新實例被算進容量但其實還不能接,下一期又補一輪;估太長,政策遲遲不把他們當容量,容易過度擴。預測的緩衝要比這段暖機長,並且不要讓另一條政策把緩衝縮掉。

陷阱三件擴縮不會替你做的事

它不會在暖機結束前變出容量,不會在 CPU 已經 100% 時告訴你還差幾倍,也不會分辨「負載真的上來了」和「下游壞了所以變慢」。第三件交給熔斷與降級;前兩件要在選指標與提前量時就承認。

一句話答法擴縮負責把容量對齊接下來幾分鐘的負載;對不齊的那一段用佇列或拒絕接住。台數公式、指標會不會截斷、任務層加節點層的暖機,三件一起講才完整。