Kubernetes HPA 自动扩缩容实战:CPU 指标、扩缩容抖动与稳定窗口调优

流量高峰手动加 Pod、低谷手动减,是很多团队的日常。HPA(HorizontalPodAutoscaler)本该把这事自动化,但真配上去往往遇到两个尴尬:要么 HPA 一直显示 <unknown> 根本不扩容,要么扩缩容像抽风一样反复横跳(flapping)。这篇从能跑起来到调稳,一步步来。

前置:没有 metrics-server,HPA 就是瞎子

HPA 靠 CPU/内存指标决策,这些指标由 metrics-server 提供。很多人 HPA 配好了却发现 TARGETS 永远是 <unknown>/50%,九成是没装 metrics-server:

# 检查是否已安装
kubectl get deployment metrics-server -n kube-system

# 没有就装(裸机/自建集群常需要加 --kubelet-insecure-tls)
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# 验证能拿到指标(能列出各 Pod 的 CPU/内存才算 OK)
kubectl top pods

kubectl top pods 能出数据,HPA 才有的谈。

关键前提:Deployment 必须声明 resources.requests

这是第二个高频坑。HPA 的 CPU 百分比是相对于 request 算的,不是相对于节点总量。如果 Deployment 没写 requests.cpu,HPA 拿不到基准,同样显示 <unknown>:

# deployment.yaml —— 必须有 resources.requests
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 2
  selector:
    matchLabels: { app: web }
  template:
    metadata:
      labels: { app: web }
    spec:
      containers:
        - name: web
          image: your-web:1.0
          resources:
            requests:
              cpu: 200m      # HPA 按这个算利用率:实际用 100m 就是 50%
              memory: 256Mi
            limits:
              cpu: 500m
              memory: 512Mi

记住这条换算:如果 request 是 200m,HPA 目标设 50%,那么当每个 Pod 平均用到 100m CPU 时就触发扩容。没有 request,一切免谈。

配一个基础 HPA

autoscaling/v2 版本(v1 只支持单一 CPU 指标,别用了):

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60   # 平均 CPU 超过 request 的 60% 就扩容

应用并观察:

kubectl apply -f hpa.yaml
kubectl get hpa web -w   # -w 持续观察 TARGETS 和 REPLICAS 变化

TARGETS 显示 45%/60% 这种真实数字,就说明链路通了。

治抖动:behavior 稳定窗口

基础 HPA 跑起来后,你可能发现流量一波动 Pod 数就来回跳——扩上去马上又缩,缩下来又扩,这叫 flapping,对服务和成本都不友好。根因是扩容很激进、缩容也很激进v2behavior 字段就是用来驯服它的:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60
  behavior:
    scaleDown:
      # 缩容前先观察 5 分钟,确认负载真的降下来了才缩,避免刚缩就又要扩
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 50          # 每次最多缩掉当前副本数的 50%
          periodSeconds: 60
    scaleUp:
      # 扩容要快,窗口设 0,负载一高立刻响应
      stabilizationWindowSeconds: 0
      policies:
        - type: Percent
          value: 100         # 每分钟最多翻倍
          periodSeconds: 60
        - type: Pods
          value: 4           # 或每分钟最多加 4 个,取两者中更激进的
          periodSeconds: 60

调优的核心思路是扩容快、缩容慢:

  • scaleUp.stabilizationWindowSeconds: 0 —— 流量一涨立刻扩,别让用户等。
  • scaleDown.stabilizationWindowSeconds: 300 —— 缩容前先观察 5 分钟,是 HPA 在这个窗口里取「最大推荐副本数」作为决策,相当于「宁可多扛一会儿也别急着缩」,这是消除抖动最有效的一招。

kubectl describe hpa web 能看到它每次决策的事件日志,调参时对着这个看最直观。

小结

  • HPA 依赖 metrics-server(kubectl top pods 能出数)+ Deployment 的 resources.requests.cpu,缺一个都会卡在 <unknown>
  • CPU 利用率是相对于 request 算的,不是节点总量,配目标值前先想清楚这个基准。
  • autoscaling/v2behavior 治抖动,原则是扩容窗口设 0(快)、缩容窗口设几分钟(慢)

一句话记忆:HPA 不出数先查 metrics-server 和 requests;HPA 抖动就把缩容的 stabilizationWindowSeconds 拉长——扩容要果断,缩容要克制。

更多推荐