Kubernetes Resource Quota 與 HPA 自動擴展
在 Kubernetes 叢集中,資源管理是穩定運行的關鍵。若沒有適當限制,單一異常的 Pod 可能耗盡整個 Node 的 CPU 或記憶體,導致其他正常的服務被「餓死」或驅逐。
本文將深入探討 Kubernetes 的資源管理機制,包括單個 Pod 的 Requests & Limits、Namespace 層級的 ResourceQuota & LimitRange,以及如何利用 HPA 實現自動擴展。
Pod 資源請求與限制 (Requests & Limits)
這是最基礎的資源管理單位。在定義 Pod (或 Deployment) 時,我們可以為每個 Container 設定 resources:
# nginx-resources.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-resources
spec:
containers:
- name: nginx
image: nginx
resources:
# Requests: K8s 調度依據 (保證有的資源)
requests:
memory: '64Mi'
cpu: '250m'
# Limits: 容器執行的硬上限 (超過會被限制或殺掉)
limits:
memory: '128Mi'
cpu: '500m'
Requests vs Limits
| 屬性 | 用途 | 說明 |
|---|---|---|
| Requests (請求) | 調度 (Scaling) | Kubernetes Scheduler 會根據 Node 剩餘的可用資源 (Capacity - Sum of Requests) 來決定 Pod 該跑在哪台 Node 上。K8s 保證 Pod 至少能獲得這麼多資源。 |
| Limits (上限) | 執行 (Runtime) | Container 執行時能使用的資源上限。這是由 Linux Kernel (Cgroups) 強制執行的。 |
CPU 與 Memory 的行為差異
- CPU (可壓縮資源):
- 單位:
1代表 1 個 vCPU (AWS vCPU, GCP Core)。100m(millicores) 代表0.1vCPU。 - 超過 Limits 時: 程式不會崩潰,但會被 Throttle (節流/降頻),導致效能變差、延遲增加。
- 單位:
- Memory (不可壓縮資源):
- 單位: 推薦使用
Mi(Mebibytes) 或Gi(Gibibytes)。- 二進位制 (
Mi,Gi): 以 1024 為換算基底 (2 的次方)。1Gi = 1024Mi。這是電腦作業系統中實際顯示的容量算法。 - 十進位制 (
M,G): 以 1000 為換算基底 (10 的次方)。1G = 1000M。 - 陷阱:
1Gi(約 1073MB) 大於1G(1000MB)。兩者容易搞混,在 K8s 資源設定中建議統一使用Mi/Gi以免發生預期外的誤差。
- 二進位制 (
- 超過 Limits 時: 若 Container 嘗試使用超過 Limit 的記憶體,該 Process 會被 Kernel 的 OOM Killer (Out Of Memory Killer) 殺掉,導致 Pod 重啟 (狀態顯示
OOMKilled)。
- 單位: 推薦使用
QoS Classes (服務品質等級)
Kubernetes 根據 Requests 和 Limits 的設定,會自動賦予 Pod 一個 QoS Class,決定資源不足時誰先被犧牲:
- Guaranteed (最高級):
- 設定:Requests == Limits (CPU & RAM 都要設且相等)。
- 行為:最不可能被驅逐。適合核心資料庫或關鍵服務。
- Burstable (中級):
- 設定:Requests < Limits。
- 行為:允許突發使用資源。當 Node 資源不足時,若並沒有超過 Requests 則安全,若超過 Requests 則可能被驅逐。適合大多數 Web Server。
- BestEffort (最低級):
- 設定:完全沒設 Requests 和 Limits。
- 行為:資源有剩就能用,但 Node 缺資源時 第一個被殺掉。只適合非關鍵的批次作業或測試。
Namespace 資源管理 (ResourceQuota & LimitRange)
除了限制單一 Pod,管理員通常需要限制整個 Namespace 的資源總量,避免某個團隊佔用過多資源。
ResourceQuota (資源配額)
限制 Namespace 內所有 Pod 的資源加總上限。
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-quota
namespace: team-a
spec:
hard:
pods: '10' # 最多只能有 10 個 Pod
requests.cpu: '4' # 所有 Pod request 加總不超過 4 vCPU
requests.memory: '8Gi' # 所有 Pod request 加總不超過 8 GiB
limits.cpu: '8'
limits.memory: '16Gi'
LimitRange (預設限制)
如果使用者忘記設定 Pod 的 resources,LimitRange 可以幫忙加上「預設值」,或是限制單一 Pod 可設定的最小/最大範圍。
apiVersion: v1
kind: LimitRange
metadata:
name: team-a-limits
namespace: team-a
spec:
limits:
- default: # 預設 Limit (若 Pod 沒寫)
cpu: 500m
memory: 512Mi
defaultRequest: # 預設 Request (若 Pod 沒寫)
cpu: 100m
memory: 128Mi
type: Container
如何套用設定
上述設定都指定了 namespace: team-a,因此請先建立該 Namespace,並將設定存為 YAML 檔 (例如 quota-limits.yaml) 後套用:
# 1. 建立 Namespace
kubectl create namespace team-a
# 2. 套用 ResourceQuota 與 LimitRange
kubectl apply -f quota-limits.yaml
# 3. 查看設定是否生效
kubectl get resourcequota,limitrange -n team-a
Horizontal Pod Autoscaler (HPA)
設定好資源限制後,我們可以利用 HPA 讓服務根據負載自動伸縮。
運作原理
- Metrics Server: 必須先安裝,它會定時蒐集 Pod 的 CPU/RAM 使用數據。
- HPA Controller: 定期 (預設 15秒) 檢查 Pod 的使用率。
- 計算公式: 核心概念是「負載變幾倍,Pod 就變幾倍」。
- 公式:
期望副本數 = ceil[目前副本數 * ( 目前指標 / 目標指標 )] - 舉例: 假設你有 2 個 Pod,目前 CPU 平均使用率飆到 80%,但你設定的目標是 50%。
- 計算:
2 * (80 / 50) = 3.2-> 無條件進位成 4 個 Pod。
- 公式:
- Scale: 調整 Deployment 的
replicas數量。
安裝 Metrics Server
HPA 依賴 Metrics Server 提供監控數據,不同環境的安裝方式如下:
1. Minikube (測試環境)
minikube addons enable metrics-server
2. 標準自建叢集 (Production) 通常使用官方提供的 YAML 檔安裝:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
--kubelet-insecure-tls 參數 (僅限測試環境,生產環境建議設定正確憑證)。3. 公有雲託管服務 (EKS, GKE, AKS) 大部分雲端服務 (如 GKE, AKS) 預設已安裝。EKS 則需透過 add-on 安裝。
驗證安裝
安裝完成後,請執行 kubectl top nodes,若能看到 CPU/Memory 數據即代表成功。
HPA 設定範例 (v2 API)
建議使用 autoscaling/v2 API,功能較完整。
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa
spec:
# 指定要擴縮容的目標 (通常是 Deployment)
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx-deploy
# 副本數的上下限
minReplicas: 2
maxReplicas: 10
# 觸發擴縮容的指標。K8s 會計算每個指標建議的副本數,並取「最大值」
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50 # 當平均 CPU 使用率超過 50% 時擴容
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 70 # 當平均 Memory 使用率超過 70% 時擴容
# (進階) 行為設定:用來微調擴縮容的反應速度,防止震盪 (Flapping)
behavior:
# 縮容策略 (Scale Down): 預設有 5 分鐘冷卻期,防止流量抖動導致頻繁縮容
scaleDown:
stabilizationWindowSeconds: 300 # 穩定視窗:需持續 300 秒 (5分鐘) 建議縮容才會真正執行
selectPolicy: Max # 若有多個策略,取變動幅度最大者
policies:
- type: Percent
value: 100 # 允許每 15 秒最多縮容 100% 的副本 (即不做額外速率限制)
periodSeconds: 15
# 擴容策略 (Scale Up): 通常希望能盡快反應
scaleUp:
stabilizationWindowSeconds: 0 # 擴容通常不需要等待,立即反應
selectPolicy: Max
policies:
- type: Percent
value: 100 # 允許每 15 秒最多擴容 100% (即翻倍)
periodSeconds: 15
- type: Pods
value: 4 # 允許每 15 秒最多增加 4 個 Pod
periodSeconds: 15
測試 HPA
使用 kubectl get hpa -w 觀察。啟動壓力測試後,應能看到:
TARGETS數值上升 (e.g.,200%/50%)。REPLICAS增加。- 壓力停止後,經過
stabilizationWindowSeconds(預設 5 分鐘),副本數才會慢慢降回來,避免服務忽大忽小 (Flapping)。
進階話題:VPA (Vertical Pod Autoscaler)
除了水平擴展 (加副本數),也有 VPA 可以垂直擴展 (自動調整 Pod 的 CPU/RAM Requests & Limits)。
- 適用場景: 無法水平擴展的服務 (如單體式資料庫),或是很難預抓資源用量的 App。
- 限制: 通常不建議 HPA 和 VPA 同時針對 CPU/Memory 控制同一個 Deployment,會造成衝突。通常是 HPA 管副本數,VPA 管資源大小 (但在 "Auto" 模式下 VPA 需要重啟 Pod 才能生效)。
總結與最佳實踐
- Requests 務必要設: 原則上 Requests 應設定為「正常運作下的平均值」,Limits 設定為「允許的突發最大值」。
- Namespace Quota 當保險: 防止單一團隊資源溢用。
- 善用 HPA: 讓服務具備彈性,但要留意
initialDelaySeconds(在 Probes 設定) 與 HPA 的配合,避免 Pod 還沒啟動好就被視為低負載而縮容,或高負載而誤判。 - 避免 OOM: 監控 Memory Usage,如果常觸發 OOMKilled,應調高 Memory Limit 或檢查程式是否有 Memory Leak。