HPA

什么是HPA

  • HPA(Horizontal Pod Autoscaler)是 Kubernetes 的核心组件之一,用于实现应用的水平自动扩缩容。
  • 其核心思想是根据观测到的 CPU 使用率、内存使用率或其他自定义指标,自动增加或减少 Deployment、ReplicaSet 或 StatefulSet 中 Pod 的副本数量,从而确保应用能够平稳应对流量负载变化,同时优化资源使用成本。

HPA的作用

  • 应对流量波动:应用负载通常存在高峰和低谷(如白天 vs 深夜、促销活动等)。手动调整副本数效率低下且无法及时响应。
  • 保证服务稳定性与性能:在负载上升时,通过增加 Pod 副本数来分担压力,避免单个 Pod 过载导致请求延迟增高或服务不可用。
  • 节约资源成本:在负载较低时,自动减少 Pod 副本数,释放闲置的集群资源,从而降低成本。

HPA的工作原理

  • HPA 控制器默认每 15 秒从各种Metrics API中获取指标数据,与自身定义的指标相比较,经过计算得到目标副本数,通过修改对应 Deployment或者statefulset 的 replicas 字段来触发扩缩容。随后,Deployment Controller 会接管并实际完成 Pod 的创建或删除。

获取指标

  • 同步周期由 kube-controller-manager 的 --horizontal-pod-autoscaler-sync-period 参数控制,默认就是 15 秒

查询指标数据

  • HPA 通过 Metrics API 来获取其关注的指标数据。
  • 对于 CPU/内存等资源指标,它从 metrics.k8s.io API 获取,数据来源于 Metrics Server。
  • 对于自定义指标(如 QPS、应用内部指标),它从 custom.metrics.k8s.io API 获取,数据通常由 Prometheus Adapter 等组件提供。
  • 对于外部指标,它从 external.metrics.k8s.io API 获取。

计算目标副本数

  • 期望副本数 = ceil[当前副本数 * (当前指标值 / 目标指标值)]

  • 例如:当前有 4 个 Pod,每个 Pod 的 CPU 使用率为 90%,HPA 的目标 CPU 使用率设置为 50%。那么期望副本数 = ceil[4 * (90 / 50)] = ceil[7.2] = 8。HPA 会将 Pod 副本数扩容到 8 个。

冷却机制

  • 为了避免副本数量因指标瞬时波动而频繁抖动(Thrashing),HPA 引入了冷却机制。可以通过HPA Spec.behavior 字段进行非常精细的控制。主要有以下三个参数

冷却窗口(stabilizationWindowSeconds)

  • 这是一个时间窗口。HPA在扩/缩容之前,必须在该持续时间内持续看到扩/缩容的需求才会应用扩缩容
behavior: # 行为配置开始
    scaleUp:
      stabilizationWindowSeconds: 0 # 扩容无冷却窗,立即行动
      policies:
      - type: Pods
        value: 4
        periodSeconds: 60 # 策略1:每分钟最多扩容4个Pod
      - type: Percent
        value: 100
        periodSeconds: 60 # 策略2:每分钟最多扩容当前副本数的100%(即翻倍)
      selectPolicy: Max # 选择两个策略中允许扩容数量更大的那个(即更激进)
    scaleDown:
      stabilizationWindowSeconds: 300 # 缩容有5分钟的冷却窗
      policies:
      - type: Pods
        value: 2
        periodSeconds: 90 # 策略1:每90秒最多缩容2个Pod
      - type: Percent
        value: 10
        periodSeconds: 90 # 策略2:每90秒最多缩容当前副本数的10%
      selectPolicy: Min # 选择两个策略中允许缩容数量更小的那个(即更保守)

策略(Policies)

  • 用于限制在特定时间段内可以添加或删除的Pod数量。你可以定义多个策略,HPA会选择最激进(扩容)或最保守(缩容)的一个。
  • 策略有两种类型: 一种是按pod的数量(type: Pods),一种是按副本的百分比(type: Percent)

策略选择(SelectPolicy)

  • 当你定义了多个策略时,这个参数决定如何从这些策略中选择最终生效的那个
  • 有这几个选项。
    • Max: 选择所有策略计算结果中最大值(用于扩容,即选择最激进的策略,允许扩得最多)。
    • Min: 选择所有策略计算结果中最小值(用于缩容,即选择最保守的策略,允许缩得最少)。
    • Disabled: 完全禁用对应方向的扩缩容。

配置和使用HPA

前提条件

  • 集群必须已部署 Metrics Server,用于提供核心资源指标(CPU/Memory)。
  • 如需使用自定义或外部指标,需部署对应的适配器,如 Prometheus Adapter。
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app  # 指定要扩缩容的目标对象
  minReplicas: 1  # 最小副本数
  maxReplicas: 10 # 最大副本数
  metrics:
  # 指标列表,可以定义多个指标,HPA 会按顺序计算,最终选择计算结果中副本数最大的那个
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization # 指标类型:Utilization(使用率)、AverageValue(平均值)
        averageUtilization: 50 # 目标 CPU 使用率 50%
  - type: Resource
    resource:
      name: memory
      target:
        type: AverageValue
        averageValue: 512Mi # 目标内存平均值 512Mi
  - type: Pods # Pods 类型指标(较少使用)
    pods:
      metric:
        name: packets-per-second
      target:
        type: AverageValue
        averageValue: 1k
  - type: Object # 对象类型指标(例如 Ingress 的 QPS)
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        name: my-app-ingress
      target:
        type: Value
        value: 10k # Ingress my-app-ingress 的 QPS 目标值为 10000
  behavior: # 行为配置(K8S 1.18+),可精细控制扩缩容行为
    scaleUp:
      policies:
      - type: Pods
        value: 2
        periodSeconds: 60 # 每分钟最多扩容 2 个 Pod
      - type: Percent
        value: 10
        periodSeconds: 60 # 每分钟最多扩容当前副本数的 10%
      selectPolicy: Max # 选择上述策略中最大值(最激进的扩容策略)
    scaleDown:
      policies:
      - type: Pods
        value: 1
        periodSeconds: 60 # 每分钟最多缩容 1 个 Pod
      selectPolicy: Min # 选择上述策略中最小值(最保守的缩容策略)

HPA使用注意

  • 确保 Pod 配置了合适的 resources.requests,没有设置 requests,HPA 将无法工作。
  • 如果设置了资源配额(ResourceQuota)需要确保命名空间的资源配额足够大
  • 确保集群资源充足
  • 单纯的使用cpu、内存作为指标的依据通常不能很好的反映应用的实际负载。通常使用QPS(每秒请求数)、请求延迟(p95/p99)、并发连接数、错误率。这些指标能更直接地反映用户体验和业务状态
  • 基于 CPU/内存的扩缩容对于有状态应用往往不是最有效的。应该使用与应用状态相关的自定义指标:
    • 消息队列:队列深度(消息积压数量)、生产者/消费者延迟。
    • 数据库:连接数、查询延迟、复制延迟。
    • 缓存:缓存命中率、内存使用量(注意是绝对值,而非百分比)。
    • 例如,一个 Kafka HPA 可以基于“主题分区leader的未同步副本数(ISR)”或“消费者组的消费延迟”来扩缩容。

PDB(Pod Disruption Budget)

  • Kubernetes 中 Pod 被删除的两种原因:

主动驱逐:

  • 由集群管理员或系统组件主动发起的、计划内的操作。例如:
  • 手动删除 Pod:kubectl delete pod
  • HPA 缩容:
  • 节点排水(Drain):在节点维护或升级前,需要先将节点上的 Pod 优雅地迁移到其他节点。kubectl drain node01
  • 更新 Deployment:更新镜像版本时,会先启动新 Pod,再删除旧 Pod(滚动更新)。kubectl set image deployment

被动故障

  • 由意外情况导致的故障。例如:
  • 节点硬件故障(如物理机宕机)。
  • 节点操作系统崩溃。
  • 网络分区导致节点失联。
  • 由于资源不足,节点被 kubelet 驱逐。

pdb只对主动驱逐生效

PDB 是什么?

  • PDB是一个 Kubernetes 策略对象,它为一个应用(一组 Pod)指定在主动驱逐期间,至少有多少个副本能正常干活,或者最多只能同时删除几个副本

为什么需要 PDB?一个生动的例子

假设你有一个非常重要的在线支付服务 payment-service,它由一個 Deployment 管理,设置了 5 个副本(Pod),并通过 HPA 进行扩缩容。

  • 没有 PDB 的场景(危险的):

    1. 夜间流量下降,HPA 计算后决定从 5 个副本缩容到 2 个。
    2. HPA 会同时向 API Server 发送删除 3 个 Pod 的请求
    3. 几乎在同一时间,这 3 个 Pod 被终止。
    4. 在终止过程中,服务的处理能力瞬间从 5 个副本暴跌到 2 个副本。如果此时有突发请求,剩下的 2 个副本很可能无法承受,导致服务响应缓慢甚至完全不可用,造成线上事故
  • 有 PDB 的场景(安全的):

    1. 你为 payment-service 创建了一个 PDB,规定 maxUnavailable: 1(最多只能有 1 个 Pod 不可用)。
    2. 夜间流量下降,HPA 计算后决定从 5 个副本缩容到 2 个。
    3. HPA 试图删除第 1 个 Pod,成功。(当前可用副本:4)
    4. HPA 试图删除第 2 个 Pod,被 PDB 拦截并拒绝!因为如果同时删除 2 个,不可用副本数就变成了 2,违反了 maxUnavailable: 1 的规定。
    5. HPA 会等待。一段时间后,第一个被删除的 Pod 已完全终止,它的流量已被负载均衡器转移到其他 Pod。
    6. 现在,集群状态稳定了,当前有 4 个副本在运行。HPA 再次尝试删除下一个 Pod,成功。(当前可用副本:3)
    7. 如此循环,直到副本数达到目标值 2。整个过程始终保持了服务的稳定处理能力,实现了“优雅缩容”。

如何定义 PDB?

  • PDB 是通过选择器(Selector)来匹配一组 Pod 的(通常通过匹配 Pod 的标签)。
  • 只能使用 minAvailablemaxUnavailable 中的一个。
  • 值可以是整数(如 2)或百分比(如 50%)。
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: payment-service-pdb
  namespace: default
spec:
  # 选择器,匹配属于 payment-service 这个 Deployment 的 Pod
  #(假设你的 Deployment 为 Pod 设置了 app: payment-service 的标签)
  selector:
    matchLabels:
      app: payment-service
  # 两种配置方式,二选一即可:
  # 1. 保证最少可用的副本数(绝对数量或百分比)
  minAvailable: 3 # 任何时候,至少要有 3 个 payment-service Pod 是可用状态

  # 2. 允许最大不可用的副本数(绝对数量或百分比)
  # maxUnavailable: 1 # 任何时候,最多只能有 1 个 payment-service Pod 处于不可用状态

deployment 也可以控制扩缩容的规模为啥还需要PDB

  • 职责范围不同:deployment仅针对由该 Deployment 控制器发起的滚动更新操作生效。PDB针对所有主动驱逐操作生效,也包含了deployment的滚动更新
  • 设计意图不同:deploment主要是为了确保滚动更新过程本身能够顺利进行,强调的是“管理更新”这个动作。PDB保证应用的服务质量和高可用性,强调的是“应用的可用性”

metric server

好的,我们来详细讲解如何定义 Metrics Server 以及如何使用 Prometheus Adapter 来提供自定义和外部 Metrics API。


第一部分:定义与使用 Metrics Server

Metrics Server 是 Kubernetes 集群的核心组件,用于提供资源指标(CPU、内存)。

1. 什么是 Metrics Server?

它是一个集群范围的资源使用率数据聚合器。它通过每个节点上的 kubelet 提供的 Summary API 来收集 Pod 和 Node 的 CPU 和内存使用情况。它不存储历史数据,只保存最新值,非常轻量。

2. 如何部署 Metrics Server?

部署非常简单,通常使用官方提供的 YAML 清单即可。

步骤:

  1. 应用官方清单

    # 使用 Kubernetes 官方仓库的清单
    kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
    
  2. (常见问题处理)如果集群使用自签名证书
    某些环境(如 Kubeadm 搭建的集群、一些云环境)的节点证书可能无法被 Metrics Server 直接验证。你需要修改部署,增加 --kubelet-insecure-tls 参数。

    kubectl -n kube-system edit deploy metrics-server
    

    spec.template.spec.containers 部分的 args 中添加:

    args:
      - --kubelet-insecure-tls # 添加这一行
      - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
    
  3. 验证安装

    # 检查 Pod 是否运行成功
    kubectl get pods -n kube-system | grep metrics-server
    
    # 检查 APIService 是否注册成功(状态应为 True)
    kubectl get apiservice v1beta1.metrics.k8s.io
    
    # 测试命令:查看节点资源使用
    kubectl top nodes
    
    # 测试命令:查看 Pod 资源使用(需要等待1-2分钟数据采集)
    kubectl top pods -A
    

    如果 top 命令能正常返回数据,说明 metrics.k8s.io API 已就绪。

3. 此时 HPA 如何使用它?

一旦 Metrics Server 运行,你就可以创建基于 CPU/内存的 HPA。

示例 HPA YAML:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: cpu-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: your-app
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50 # 目标:所有Pod的平均CPU使用率为50%

第二部分:使用 Prometheus Adapter 提供自定义 Metrics API

当你需要基于更复杂的指标(如 QPS、消息队列长度、业务指标)进行扩缩容时,就需要 Prometheus Adapter。

1. 工作原理

  1. 前提:你已经在集群中部署了 Prometheus,并且它正在抓取你的应用指标(应用需要暴露 /metrics 端点)。
  2. 部署 Adapter:部署 Prometheus Adapter。它会被配置为查询你的 Prometheus 服务。
  3. 注册 API:Adapter 会向 Kubernetes API 聚合层注册 custom.metrics.k8s.ioexternal.metrics.k8s.io API。
  4. 配置规则:你通过 ConfigMap 告诉 Adapter:“如何将 Prometheus 中的查询语句映射成 Kubernetes 可识别的自定义指标”
  5. HPA 查询:HPA 控制器向 custom.metrics.k8s.io API 发起请求,Adapter 收到后,将其转换为自己配置的 PromQL 查询语句,从 Prometheus 获取数据,再将结果格式化成 Kubernetes 理解的格式返回给 HPA。

2. 部署与使用步骤

步骤 1: 部署 Prometheus
如果你还没有,可以使用 Prometheus Operator (如 kube-prometheus-stack) 或直接部署 Prometheus Server。

步骤 2: 部署 Prometheus Adapter
最推荐的方式是使用 Helm chart。

# 添加 Helm 仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

# 安装 Chart,命名为 prometheus-adapter
helm install prometheus-adapter prometheus-community/prometheus-adapter -n monitoring

安装后,检查 API 是否可用:

kubectl get apiservice | grep custom.metrics.k8s.io
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq .

步骤 3: (核心)配置适配器规则
这是最关键的一步。你需要创建一个 values.yaml 文件来覆盖 Helm chart 的默认配置,定义你的自定义指标规则。

示例:创建一个基于每秒请求数 (QPS) 的 HPA

假设你的应用暴露了一个名为 http_requests_total 的 Prometheus 计数器(Counter)。

  1. 创建 adapter-values.yaml

    rules:
      default: false # 不使用任何默认规则
      custom:
      # 系列查询 (Series Query):用于在Prometheus中发现可用的指标
      - seriesQuery: 'http_requests_total{namespace!="",pod!=""}'
        resources:
          # 将Prometheus标签与Kubernetes资源关联
          overrides:
            namespace: { resource: "namespace" }
            pod: { resource: "pod" }
        name:
          # 定义指标在HPA中引用的名称
          # 将 `http_requests_total` 转换为 `http_requests_per_second`
          matches: "^(.*)_total$"
          as: "${1}_per_second"
        metricsQuery: |
          # 最终执行的PromQL查询语句
          # rate() 计算每秒速率
          # <<.GroupBy>> 和 <<.LabelMatchers>> 由adapter自动填充
          rate(<<.Series>>{<<.LabelMatchers>>}[2m]) * 100
    
  2. 使用自定义配置升级 Adapter

    helm upgrade -i prometheus-adapter prometheus-community/prometheus-adapter -n monitoring -f adapter-values.yaml
    
  3. 验证自定义指标

    # 列出所有可用的自定义指标
    kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq .
    
    # 查看特定Pod的特定指标(命名空间+Pod名)
    kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/http_requests_per_second" | jq .
    

    如果命令返回了数据,说明配置成功!

步骤 4: 创建基于自定义指标的 HPA
现在你可以创建一个基于 QPS 的 HPA。

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: http-requests-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: your-app
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Pods # 使用Pods类型的指标
    pods:
      metric:
        name: http_requests_per_second # 与adapter配置中定义的name匹配
      target:
        type: AverageValue
        averageValue: 10 # 目标:每个Pod平均每秒处理10个请求

总结

  1. Metrics Server

    • 提供:核心资源指标(CPU/Mem)。
    • 部署kubectl apply -f ...components.yaml
    • HPA使用type: Resource
  2. Prometheus Adapter

    • 提供:自定义指标(QPS等)和外部指标。
    • 部署:使用 Helm chart。
    • 核心:通过 rules.custom 配置 values.yaml 文件,将 PromQL 查询映射为 Kubernetes 指标。
    • HPA使用type: Podstype: Object,并指定 metric.name

通过这两者的组合,你就能实现从简单的资源监控到复杂的基于应用业务的全面自动扩缩容。

posted @ 2026-03-27 11:36  立勋  阅读(69)  评论(0)    收藏  举报