Kubernetes v1.37 将 HPA 缩容到零副本推进到 Beta,并默认启用。对于队列消费者、批处理器这类并非持续接收 HTTP 请求的工作负载,空闲时可以不保留任何 Pod,有任务到来时再自动拉起。
这项能力减少了空闲资源消耗,尤其适合预留专用 CPU 或 GPU 的 Pod。但它也带来明确代价:从零恢复需要重新读取指标、调度 Pod、启动容器和初始化应用。因此,适用场景的关键不是“能不能缩到零”,而是“业务是否能承受冷启动”。
为什么 CPU 指标无法唤醒零副本工作负载
传统 HPA 经常根据 CPU 或内存使用率计算副本数。这些指标都来自正在运行的 Pod。当 Deployment 已经缩容到零时,没有 Pod,也就没有 CPU 或内存数据可以继续观察。
对象指标和外部指标不受这个限制。队列长度、任务积压量或外部系统中的待处理数量独立于消费者 Pod 存在。即使消费者全部停止,HPA 仍然可以读取队列指标,并据此决定重新创建 Pod。
典型流程如下:
- 队列为空,HPA 将消费者 Deployment 缩容到零。
- 新任务进入持久化队列,外部指标变大。
- HPA 读取指标并计算所需副本数。
- Deployment 创建 Pod,消费者开始处理任务。
- 队列再次变空,HPA 在稳定窗口后缩容。
用外部指标配置 HPA
下面的例子假设 Prometheus 已经采集名为 queue_consumer_lag 的指标,并且指标包含 namespace 和 name 标签:
queue_consumer_lag{namespace="default",name="worker_tasks"} 75
Kubernetes 需要一个指标适配器,将 Prometheus 数据暴露为 External Metrics API。可以这样实践 Prometheus Adapter 的规则配置:
externalRules:
- seriesQuery: '{__name__="queue_consumer_lag",name!=""}'
metricsQuery: 'sum(<<.Series>>{<<.LabelMatchers>>}) by (name)'
resources:
overrides:
namespace:
resource: namespace
适配器的安装方式和完整发现规则取决于现有监控系统。创建 HPA 前,先确认 Kubernetes 能读到指标:
kubectl get --raw \
'/apis/external.metrics.k8s.io/v1beta1/namespaces/default/queue_consumer_lag?labelSelector=name%3Dworker_tasks'
如果 API 返回当前指标值,说明指标链路基本可用。若返回错误或空结果,应先修复 Prometheus、适配器或标签匹配问题。指标不可用时,HPA 无法从零副本状态恢复容量。
下面是一个可改造的完整 HPA 示例。它管理名为 queue-worker 的 Deployment,最多运行 10 个副本,每 30 个排队任务对应 1 个副本:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: queue-worker
annotations:
kubernetes.io/description: Scales queue-worker from the external queue metric
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: queue-worker
minReplicas: 0
maxReplicas: 10
metrics:
- type: External
external:
metric:
name: queue_consumer_lag
selector:
matchLabels:
name: worker_tasks
target:
type: Value
value: "30"
应用前,先让 Deployment 至少运行一个副本:
kubectl scale deployment queue-worker --replicas=1
kubectl apply -f queue-worker-hpa.yaml
kubectl describe hpa queue-worker
当队列为空时,HPA 可以将 Deployment 降到零。当 queue_consumer_lag 上升时,HPA 会重新计算副本数,并受 maxReplicas: 10 限制。
零副本与手动暂停不是一回事
副本数为零可能有两种含义:HPA 自动缩容,或者运维人员手动暂停。Kubernetes v1.37 使用 ScaledToZero 状态条件区分这两种情况。
当 HPA 自己把工作负载从一个或多个副本缩容到零时,会记录:
ScaledToZero=True
之后控制器仍会继续评估对象指标或外部指标,并允许工作负载被唤醒。恢复到非零副本后,该条件会变为 ScaledToZero=False,原因通常是 NotScaledToZero。
如果某个 Deployment 被手动设置为零,且没有 ScaledToZero=True 条件,HPA 会把它视为人为暂停,不会擅自唤醒它。这保留了手动缩容用于暂停业务的既有语义。
可以通过以下命令检查状态:
kubectl describe hpa queue-worker
如果指标适配器无法返回配置的指标,通常可以看到 ScalingActive=False,以及类似 FailedGetExternalMetric 的原因。此时应恢复指标链路,或者手动将工作负载扩容到至少一个副本以恢复处理能力。
HPA 的默认缩容稳定窗口仍然适用,默认值为 5 分钟。它可以避免队列短暂下降就立即删除全部消费者。需要更快或更慢缩容时,可以显式配置 spec.behavior.scaleDown,但要结合任务持续时间、冷启动开销和消息处理能力验证。
适用边界与升级注意事项
缩容到零最适合任务可以等待的场景,例如:
- 持久化消息队列的消费者
- 可以延迟执行的批处理任务
- 具有明确积压指标的异步处理器
- 单个 Pod 资源成本较高的 CPU 或 GPU 工作负载
它不适合直接作为普通 HTTP 服务的请求缓冲方案。Kubernetes Service 在没有 Ready Pod 时不会替客户端缓存请求。HTTP 请求驱动的工作负载需要额外的队列、网关缓冲或其他能够承受冷启动的架构,否则请求可能在 Pod 启动前失败。
v1.37 中,HPAScaleToZero 特性门控在 kube-apiserver 和 kube-controller-manager 中默认启用。控制平面滚动升级期间,应等待两个组件都支持并启用该能力后,再创建 minReplicas: 0 的 HPA。若 controller manager 仍处于旧版本或禁用了特性门控,可能把零副本视为手动暂停,导致工作负载无法自动恢复。
在禁用特性门控或回滚到不支持该实现的版本前,应完成以下操作:
- 将相关 HPA 的
minReplicas改为至少 1。 - 将当前处于零副本状态的工作负载扩容到至少 1 个副本。
- 确认 HPA 至少配置了一个对象指标或外部指标。
API Server 会拒绝只使用 CPU、内存等资源指标、却将 minReplicas 设置为零的 HPA,因为这类指标无法在零 Pod 时提供唤醒信号。
落地检查清单
采用这项 Beta 能力前,可以逐项确认:
- 队列或任务状态是否持久化,能否承受冷启动期间的等待?
- 外部指标是否在零 Pod 时仍然存在并持续更新?
- External Metrics API 是否能返回正确的标签和值?
maxReplicas是否覆盖突发积压?- 应用启动时间、镜像拉取时间和初始化时间是否已纳入延迟预算?
- 是否准备了指标不可用时的告警和人工恢复步骤?
- 控制平面升级、回滚流程是否处理了
minReplicas: 0?
Kubernetes v1.37 的重点不只是允许把 minReplicas 写成零,而是让 HPA 能区分“自动缩容到零”和“人为暂停”,并继续依赖对象指标或外部指标完成唤醒。对异步工作负载来说,这使缩容到零从外部插件能力变成了 Kubernetes 核心能力;对请求驱动服务来说,冷启动和请求缓冲仍然需要单独设计。