iT邦幫忙

2026 iThome 鐵人賽

DAY 27
0
Software Development

學校沒教的後端生存指南:30 天打造非同步 S3 微服務,部署 K8s 實現 HA 架構系列 第 27 篇

[ Day 27 ] HPA 逆襲!PUT 上傳突破單機瓶頸 0% 錯誤率,高可用彈性擴充與 AWS 雲端算力成本精算!

  • 分享至 

  • xImage
  •  

昨天我們剖析了 K8s 3-Node 叢集在 Service 與 kube-proxy 轉發下的網路開銷。今天,我們要來測試 k8s HPA (Horizontal Pod Autoscaler) 自動水平擴展!讓 K8s 根據即時 CPU 負載,自動像變形金剛一樣擴展長出更多 Pod 來應對突如其來的流量。今天除了手把手設定 Metrics Server 與 HPA (Horizontal Pod Autoscaler),更會和前幾天的內容做壓力測試的比較,進行跨架構實測對比!

測試 k8s HPA

HPA (Horizontal Pod Autoscaler) 高流量自動水平擴容

我們前兩天有提到的水平擴展(Scale-out)與垂直擴展(Scale-up)是擴展的方式和概念,而詳細實作時我們必須透過k8s中的自動化控制器機制 HPA來實現Scale-out

啟用 Metrics Server 與設定 HPA YAML 檔

  1. 啟用 Minikube Metrics Server(算力監控大腦),Metrics Server 是 HPA 獲取 Pod CPU/RAM 使用率的官方數據源:
minikube addons enable metrics-server
  1. 執行 kubectl get pods -n kube-system,確認 metrics-server 處於 Running 狀態。

https://ithelp.ithome.com.tw/upload/images/20260921/20183864MXIse5MDE4.png

kube-system 是 Kubernetes 存放控制面(Control Plane,大腦與器官)的核心命名空間。這些 Pod 就是 K8s 能夠自動運作的背後功臣:

  • kube-apiserver:K8s 的大腦入口(接收你的 kubectl 指令)。
  • etcd:K8s 的資料庫(儲存所有 Deployment 與 Pod 的狀態記錄)。
  • kube-controller-manager & kube-scheduler:負責監控狀態、決定 Pod 要派發到哪台機器的調度員。
  • coredns:K8s 叢集內部的 DNS 伺服器(讓 Pod 可以用服務名稱互相通訊)。
  • kube-proxy & kindnet:處理 K8s 內部網路與 Service IP 轉發的網路元件。
  • storage-provisioner:處理儲存卷掛載元件。
  1. 撰寫 k8s/s3-app-hpa.yaml
  • 在 k8s/ 資料夾下建立 s3-app-hpa.yaml,並加入以下程式碼
  • 宣告當所有 Pod 的平均 CPU 使用率超過 50% 時,自動將 Pod 數量在 3 ~ 10 個之間動態調整
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: s3-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: s3-app-deployment
  minReplicas: 3   # 平時保持 3 個 Pod 低功耗
  maxReplicas: 6  # 高流量時最多自動擴容到 6 個 Pod
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50 # CPU 50% 即觸發自動加開 Pod!
  1. 套用與實測:
kubectl apply -f k8s/s3-app-hpa.yaml

kubectl get hpa

https://ithelp.ithome.com.tw/upload/images/20260921/20183864P0waKkuhYP.png

  1. 打開Jmeter,設定實驗參數
    • list 併發200、500、800,迴圈都重複5次
    • put 併發 20、50、80,迴圈都重複5次
    • 先list在put是為了不讓每次list的數量不一致,且put測試做完後我會到aws console手動刪除檔案,只留下大約100筆檔案列出

實驗結果

我們啟用 Metrics Server 並配置 HPA(目標 CPU: 50%,Pod 數 3 ~ 6個),並使用Jmeter進行多階段併發測試:

HPA 模式 vs. 單機 Docker vs. K8s 3-Node 寫入 (PUT)

架構 併發總數 (Threads x Loop) 平均延遲 (ms) 最小延遲 (ms) 最大延遲 (ms) 錯誤率 (%) 吞吐量 (RPS) 網路發送 (KB/s)
Docker put 80 x 5 (400) 1,342 1 5,192 36.75% 🚨 26.3/s 1,821.0 KB/s
K8s put 80 x 5 (400) 4,457 874 13,117 0.00% 14.5/s 1,004.0 KB/s
HPA put 20 x 5 (100) 1,102 435 2,753 0.00% 🎉 10.4/s 721.7 KB/s
HPA put 50 x 5 (250) 2,400 473 6,970 0.00% 🎉 15.2/s 1,048.0 KB/s
HPA put 80 x 5 (400) 3,906 777 13,696 0.00% 🚀 16.2/s 1,121.6 KB/s
HPA list 100 x 5 (500) 5,837 120 30,104 46% 🚀 2.59/s 5.6 KB/s

🎯 HPA 逆襲關鍵:當 80 併發打進來時,HPA 偵測到 CPU 使用率衝破 88%,在 20 秒內自動將 Pod 從 3 個動態擴充至 6 ~ 8 個 Pods!多個容器平分 Semaphore(50) 控流與 JIT 編譯開銷,成功將單機時代的 36.75% 崩潰錯誤率徹底歸零 (0.00%)!

Grafana 監控

HPA 在 PUT 20, 50, 80 併發下的 Grafana 監控畫面,展現 Pod 隨流量自動長大,三個CPU高峰代表三次的壓力測試,可以看到負載平滑分流,錯誤率維持完美的 0.00%!

https://ithelp.ithome.com.tw/upload/images/20260923/20183864ePohYclKiv.png

  • CPU 使用率降低 (35% ~ 45%):不同併發從 20、50、80下,CPU 負載被動態長大的 Pods 均勻分攤,沒有像單機 Docker 爆衝至 95% 飽和。
  • JVM Memory 記憶體鋸齒線:證明在 Semaphore(50) 控流下,記憶體波動健康,完全沒有觸發任何 JVM OOM。
  • 延遲下降與 0% 錯誤率:平均回應時間從固定 3-Node 的 4,457ms 下降至 3,906ms,且錯誤率保持強大的 0.00%!

pod觀察

Terminal 控制台視窗,記錄了 HPA 在高負載下動態拉起 Pod 4、Pod 5 至 Pod 8 的即時擴容軌跡。

https://ithelp.ithome.com.tw/upload/images/20260923/20183864jcFNXoiJG8.png

  • 可以看到 jfcgr 的pod由0/1 轉 1/1 ,說明當流量打進來時,HPA 動態拉起新 Pods(如 4tt2f, 9942, stlk2)。剛拉起時顯示 STATUS: Running 但 READY: 0/1,經過約 10 秒通過 Readiness Probe 後轉為 1/1,正式加入 Service 接收流量!
  • 觀察中可以看到Restarts的數字每個pod不一樣
    • RESTARTS: 1:代表 Pod 在剛啟動瞬間遇到 CPU 爭奪,探針微小 Timeout 觸發一次自癒重啟,隨後順利通過變為 1/1。
    • RESTARTS: 5~7:這是先前我們頻繁修改 YAML 執行滾動更新 (Rolling Update) 時,舊版本 ReplicaSet 留下的歷史重啟記錄,證明 K8s 會保留 Pod 的生命週期歷史,讀者看到完全不需要驚慌。
  • 我們會看到有一個CrashLoopBackOff的狀態,代表死鎖了,詳見後續除錯方式的章節

為什麼 HPA 救得了 PUT,卻救不了 LIST?

在測試中,我們發現 List 200 併發在 HPA 下依然全數卡死崩潰。這揭示了 HPA 的核心運作機制與盲點:併發型態與擴容時效

https://ithelp.ithome.com.tw/upload/images/20260923/20183864tRgC3yY0uJ.jpg

  1. PUT 上傳在 HPA 下能達成 0.00% 錯誤率:
    • 寫入含有 Semaphore(50) 控流與長連線等待:PUT 上傳需要傳輸 74KB 檔案與 S3 簽章,請求在時間上是平滑流入的。
    • 給予 HPA 充裕的反應時間:當 CPU 衝破 50% 門檻時,HPA 在 15~20 秒內自動將 Pod 從 3 個拉大到 6 ~ 8 個 Pods。全叢集總 Semaphore 門閥擴充至 300+,新打進來的請求被新 Pod 完美接管,錯誤率當場歸零 (0.00%)!
  2. LIST 200 併發在 HPA 下依然爆掉:
    • 瞬間海嘯 (Cache Stampede) vs. HPA 採集滯後:200 個 List 請求是在 1 秒內同時發射 的高 CPU 序列化任務。
    • 15 秒指標盲區:K8s Metrics Server 每 15 秒才採集一次 CPU 指標。當 200 個 List 請求進入時,本地 port-forward 佇列在第 1 秒就已經塞爆;等 HPA 在 15 秒後發現 CPU 100% 並拉起 Pod 時,這 200 個請求早已超時宣告死亡!

3.結論:寫入靠背壓 (Backpressure) 與 HPA 自動擴容;讀取靠 Redis 快取 (Cache) 吸收 99% 瞬間海嘯!


實戰 Debug:7 個 Pod 連環 CrashLoopBackOff

我自己在實際壓力測的時候其實遇到非常多問題,首先HPA測試到一半服務整個壞掉了,api都打不到一直出現socket error

在 HPA 測試中,若 YAML 配置不當,常常會看到以下令人崩潰的畫面:

https://ithelp.ithome.com.tw/upload/images/20260922/201838644TKmB6Mn3n.png

  • 多世代 ReplicaSet 殘留 (594cb, 5fc8d):頻繁修改 limits 並套用,導致舊版 ReplicaSet 的 Pod 還沒死透,新版又拉不起來。
  • 資源餓死與探針超時死鎖:8 個 Java Pod 在 8 核筆電上同時啟動,Spring Boot Context 載入搶奪 CPU。由於 CPU 被吃滿,Readiness Probe 打進去連續 Timeout,K8s 認定 Pod 死亡並強行 Kill 重啟(RESTARTS: 5~7),陷入死鎖!

除錯方式

在 K8s 叢集中面對未知的錯誤時,千萬不能瞎猜!我們時可以按照以下步驟慢慢找到問題點:

  1. 先看看是不是硬體效能滿了 kubectl top pods 實時查看哪一個 Pod 算力飆滿或記憶體洩漏

    • 取得各 Pod 的真實 CPU 消耗後也可以計算總算力 229 m + 237 m + 184 m + 224 m + 74 m + 42 m + 229 m + 246 m = 1,465m (1.465核心) 這證明所有的 Pod 加起來精準吃滿了我們給予的 1.5 核上限,且其餘 CPU 被 K8s 控制面(etcd, apiserver, coredns)順暢使用。
  2. 檢查 Pod 底部 Events 區塊 kubectl describe pod <Pod名> 抓出 Reason: OOMKilled 或探針 Fail 原因。

  3. 查看容器重啟前的崩潰前的日誌 kubectl logs <Pod名> --previous

  4. 如果是像前面pod的圖片中遇到 CrashLoopBackOff 死鎖!可以重新建立乾淨的叢集

NAME                                 READY   STATUS            RESTARTS       AGE
s3-app-deployment-594cbbf47b-bbh2g   0/1     CrashLoopBackOff  7 (2m31s ago)  9h
s3-app-deployment-5fc8d7c66d-4tt2f   0/1     Running           5 (25s ago)    8m
s3-app-deployment-5fc8d7c66d-5xjb9   1/1     Running           1 (8m27s ago)  9m
- 刪除舊的 : `kubectl delete deployment s3-app-deployment`
- 重新建立 : `kubectl apply -f k8s/s3-app-deployment.yaml` 

補充 : 量預估公式與微服務擴充

一個好的服務硬體資源要設定多少,絕不能憑感覺設定 Pod 數量!而需要量化估算框架:

1. 尖峰 QPS 量化估算公式:

Peak QPS = [(每日活躍使用者 (DAU) x 每人每日 API 呼叫次數) / 每日尖峰時間 (秒)]x 安全壓測係數 (3 ~ 5)

範例(電商 / S3 微服務):

  • DAU = 100,000,每人每天上傳/查詢 10 次,總請求量 = 1,000,000 次
  • 集中在晚間 2 小時(7,200 秒):平均 QPS = 1,000,000/7,200 = 138.8 QPS
  • 考慮 5 倍突發尖峰:Target Peak QPS = 138.8 x 5 = 700 QPS!

2. 記憶體 (RAM) 預算公式 與 Pod Resource 配額規劃:

  • 在 Day 19 中我們曾精算出容器記憶體的防線公式:Memory per Pod = (Semaphore x 單一請求 Buffer 大小) + JVM/Netty 基礎開銷
  • 我們服務中 Semaphore =50 時,Memory = (50 x 5) + 200 = 450MB ➔ 配額設定 limits: 512Mi。
  • CPU Requests vs. Limits 比例:
    • requests: 100m ~ 250m(設小:保底預約,讓 HPA 敏感偵測)。
    • limits: 500m ~ 1000m(設大:允許 JVM 啟動與 JIT 爆發,留出 2x~4x 突發空間)。

3. AWS EC2 雲端成本與 Auto-Scaling 財務對比:

假設我們的 API 服務需要應付日常 200 QPS,尖峰時間(晚上 8~10 點)會爆發至 2,000 QPS:

  • 方案 A:傳統靜態配置 (Static Provisioning - 無 HPA)
    • 為了扛住尖峰 2,000 QPS,被迫 24 小時開滿 10 台 c6i.xlarge (4vCPU /8GB RAM,單台$122 USD/月)
    • 每月 AWS 帳單:10 x $122 = $1,220 USD / 月
    • 痛點:每天有 22 小時離峰時間,80% 的 EC2 CPU 都在發呆,白白浪費公司幾萬元台幣!
  • 方案 B:K8s + HPA 彈性自動水平擴充 (Cloud-Native)
    • 離峰時間,維持 3 個 Pods 跑在 2 台 c6i.large 上 (2 vCPU / 4GB RAM,單台約 $61 USD/月)。
    • 尖峰 2 小時打進來時,HPA 自動拉起 Pods 擴容,過後自動縮容。
    • 每月 AWS 帳單:基底 2 台 EC2 ($122) + 尖峰擴容開銷 (40)= $162 USD / 月!
    • 商業價值:直接幫公司省下高達 86.7% 的雲端帳單! 這就是走向 K8s HPA 的真正核心魅力!

補充: QPS vs. RPS 差異

或許有人好奇不是應該用RPS(Requests per second)來計算嗎?

  • RPS (Requests Per Second):前端/客戶端每秒發送到 Web 網關的 HTTP 請求總數。
  • QPS (Queries Per Second):後端服務每秒向 S3 或 Database 發起的 查詢/寫入指令總數。
  • 實務差異:在 REST API 中,若 1 個上傳請求包含 1 次 HEAD 二次確認 + 1 次 PUT 上傳,則 1 RPS = 2 QPS!計算雲端容量時必須以 QPS 為準。

總結

這兩天我們完整走過了從單機 Docker、K8s 3-Node 到 HPA 自動擴展的全套實測歷程!我們用數據證明了 HPA 在高併發寫入時的震撼威力,也釐清了讀取 API 必須依賴快取的物理邊界,也實證了:

  1. 沒有完美的技術,只有最適合的權衡 (Trade-off)。
  2. 寫入靠背壓 (Backpressure) 與 HPA 彈性擴容,保證數據 0% 遺失。
  3. 讀取靠快取 (Cache),避免瞬間海嘯擊穿 HPA 的冷啟動時間窗!

明天,我們將正式邁入 DevOps 自動化交付,實作 GitHub Actions + AWS ECR + K8s 一鍵 CI/CD 流水線!


上一篇
[ Day 26 ] K8s 真的比較快嗎?用 JMeter 實測數據拆解:Docker vs k8s 3 副本叢集的 QPS、延遲與抗壓邊界
下一篇
[ Day 28 ] Pod 重啟資料卻失蹤 : K8s Volume 與 ConfigMap / Secret 打造狀態與機密不崩潰的高可用微服務!
系列文
學校沒教的後端生存指南:30 天打造非同步 S3 微服務,部署 K8s 實現 HA 架構 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言