为什么需要 KEDA?传统 HPA 的局限性
Kubernetes 原生的 Horizontal Pod Autoscaler(HPA)基于 CPU 和内存等资源指标进行自动扩缩容,这在大多数常规 Web 服务场景下工作良好。然而,当你的应用需要根据消息队列长度、数据库连接数、自定义业务指标或外部系统的负载来动态调整副本数时,HPA 就显得力不从心了。
考虑以下真实场景:
- 消息队列消费者:RabbitMQ 或 Kafka 队列中堆积了 10 万条消息,但 CPU 利用率只有 20%,HPA 不会触发扩容,导致消息处理延迟持续飙升。
- 定时批处理任务:每天凌晨需要处理大量数据,白天几乎空闲,但 HPA 无法根据”当前时间”或”队列深度”这种非资源类指标做决策。
- 外部 SaaS 集成:你的服务需要根据第三方 API 的调用配额剩余量来调整吞吐量,HPA 完全没有感知外部指标的能力。
KEDA(Kubernetes Event-Driven Autoscaling)正是为解决这些问题而生。它由 Microsoft 和 Red Hat 联合开发,目前已加入 CNCF 作为孵化项目。KEDA 的核心设计思想是:让 Kubernetes 应用可以根据任何可观测的事件源(Event Source)自动扩缩容,从 “Watch CPU” 进化到 “Watch Events”。

KEDA 架构与核心概念
KEDA 在 Kubernetes 集群中运行,充当了事件源和 HPA 之间的桥梁。理解它的架构有助于你在实际部署中做出正确的配置决策。
三大核心组件
| 组件 | 角色 | 说明 |
|---|---|---|
| Operator | 控制面 | 管理 ScaledObject 和 ScaledJob 的 CRD,监听事件源的指标并驱动 HPA |
| Metrics Server | 数据面 | 将外部指标暴露为 Kubernetes 自定义指标 API(external.metrics.k8s.io) |
| Admission Webhooks | 验证 | 对 ScaledObject 配置进行合法性校验,避免无效配置导致扩缩容异常 |
ScaledObject 是 KEDA 最核心的自定义资源,它定义了三个关键信息:
- scaleTargetRef:指定要自动伸缩的目标工作负载(Deployment、StatefulSet 或 Custom Resource)
- triggers:指定一个或多个事件源及其连接参数
- minReplicas / maxReplicas:副本数的上下限,覆盖 Deployment 或 HPA 中的对应配置
KEDA 的工作流程
当 KEDA 检测到一个 ScaledObject 被创建或更新时,它会自动创建一个对应的 HPA 资源。这个 HPA 的指标来源被设置为 KEDA Metrics Server 暴露的自定义指标端点。Metrics Server 会按照 ScaledObject 中配置的轮询间隔(
1 | pollingInterval |
,默认 30 秒),主动查询事件源获取当前指标值,然后通过标准的外部指标 API 返回给 HPA。HPA 根据这些指标值计算出期望的副本数并调整 Deployment 的 replicas 字段。
这个流程的关键优势在于:
- 无需修改应用代码——KEDA 从外部观测事件源,应用本身不需要感知扩缩容逻辑
- 解耦事件源和扩缩容策略——更换消息队列或添加新的事件源只需更新 ScaledObject 配置
- 与原生 HPA 完全兼容——KEDA 创建的 HPA 和手动创建的 HPA 在行为上没有区别
部署 KEDA 到你的集群
部署 KEDA 只需要几分钟。官方推荐使用 Helm Chart 进行安装:
1
2
3
4
5
6
7
8
9
10
11
12
13 # 添加 KEDA Helm 仓库
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
# 安装 KEDA(最新稳定版)
helm install keda kedacore/keda \
--namespace keda \
--create-namespace \
--set podIdentity.activeDirectory.operatorMode=true
# 验证安装
kubectl get pods -n keda
# 期望输出:3 个 running pod(operator、metrics-apiserver、admission-webhooks)
如果你不使用 Helm,也可以通过 YAML 直接部署:
1
2 # 部署 KEDA 核心组件(推荐在生产环境使用特定版本)
kubectl apply --server-side -f https://github.com/kedacore/keda/releases/download/v2.14.0/keda-2.14.0.yaml
部署完成后,确认自定义资源已注册:
1
2
3
4
5 kubectl get crd | grep keda
# scaledobjects.keda.sh
# scaledjobs.keda.sh
# triggerauthentications.keda.sh
# clustertriggerauthentications.keda.sh
实战案例一:Kafka 消费者自动扩缩容
先来看一个最常见的场景。假设你有一个 Kafka 消费者服务,需要根据 Lag(堆积消息数)自动扩容。当 Lag 超过阈值时增加消费者副本,当 Lag 下降时回收多余的副本以节省资源。
前置条件
首先需要准备 Kafka 的连接凭据。使用 TriggerAuthentication 资源来安全地管理敏感信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: kafka-creds
namespace: default
spec:
secretTargetRef:
- parameter: sasl
name: kafka-secret # 指向下面创建的 Secret
key: sasl-username
- parameter: sasl-password
name: kafka-secret
key: sasl-password
---
apiVersion: v1
kind: Secret
metadata:
name: kafka-secret
namespace: default
type: Opaque
stringData:
sasl-username: "your-kafka-user"
sasl-password: "your-kafka-password"
注意:TriggerAuthentication 可以定义在任意命名空间中。对于集群范围内共享的凭据,可以使用
1 | ClusterTriggerAuthentication |
资源,它不受命名空间的限制。
创建 ScaledObject
以下是让 Kafka 消费者 Deployment 根据 Lag 自动扩缩容的完整配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: kafka-consumer-scaler
namespace: default
spec:
scaleTargetRef:
name: kafka-consumer # 目标 Deployment 的名称
apiVersion: apps/v1
kind: Deployment
pollingInterval: 15 # 每 15 秒检查一次 Lag
cooldownPeriod: 120 # 无事件后等待 120 秒再缩容
minReplicaCount: 1 # 最少保留 1 个副本
maxReplicaCount: 20 # 最多扩到 20 个副本
fallback:
failureThreshold: 3 # 连续 3 次拉取失败后启用回退
replicas: 5 # 回退到 5 个副本
advanced:
horizontalPodAutoscalerConfig:
behavior:
scaleDown:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 200
periodSeconds: 60
triggers:
- type: kafka
metadata:
bootstrapServers: kafka-cluster:9092
topic: order-events
consumerGroup: order-processor-group
lagThreshold: "100" # 当 Lag > 100 时触发扩容
offsetResetPolicy: latest
# 可选:将 Lag 值在扩容时除以这个因子,更适合高吞吐场景
# activationLagThreshold: "50"
authenticationRef:
name: kafka-creds
这里有几个值得注意的参数:
- cooldownPeriod:缩容的冷却时间。设置太短会导致频繁扩缩容(thrashing),太长则浪费资源。120 秒是一个不错的起始值。
- fallback:当 KEDA 无法从 Kafka 获取指标时的安全网。如果连续 3 次探测失败,自动回退到 5 个副本,防止因监控系统故障导致服务不可用。
- advanced.horizontalPodAutoscalerConfig.behavior:利用 HPA v2 的扩容行为策略。这里设置了扩容更快(允许一次扩 200%),缩容更保守(稳定窗口 60 秒,每次最多缩 50%)。
实战案例二:RabbitMQ 消息队列自动扩缩容
RabbitMQ 和 Kafka 的配置非常相似,但 RabbitMQ 使用队列深度(queue depth)作为扩缩容指标:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: rabbitmq-worker-scaler
namespace: default
spec:
scaleTargetRef:
name: rabbitmq-worker
pollingInterval: 10
cooldownPeriod: 60
minReplicaCount: 0 # 允许缩到 0!(队列为空时不运行任何 Pod)
maxReplicaCount: 30
triggers:
- type: rabbitmq
metadata:
protocol: amqp
host: amqp://guest:guest@rabbitmq.default.svc:5672/
queueName: task-queue
queueLength: "30" # 队列深度超过 30 开始扩容
activationQueueLength: "10" # 小于 10 时缩到 minReplicaCount
authenticationRef:
name: rabbitmq-creds
注意这里
1 | minReplicaCount: 0 |
。KEDA 支持缩容到零副本,这是原生 HPA 做不到的。当队列为空且无消息积压时,KEDA 会直接将 Deployment 的副本数设为 0,从而完全释放资源。当新消息进入队列时,重新从 0 启动 Pod 需要一定的启动时间,因此在延迟敏感的场景下需要合理设置
1 | activationQueueLength |
来提前预热。
实战案例三:基于 Prometheus 自定义指标扩缩容
很多团队已经在使用 Prometheus 监控系统。KEDA 可以直接利用已有的 Prometheus 指标进行扩缩容,无需额外部署任何采集组件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: api-server-scaler
namespace: default
spec:
scaleTargetRef:
name: api-server
minReplicaCount: 2
maxReplicaCount: 50
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus.monitoring.svc:9090
metricName: http_requests_per_second
query: |
sum(rate(http_requests_total{handler="/api/orders", status=~"5.."}[2m]))
threshold: "50" # 当每秒请求数 > 50 时扩容
activationThreshold: "10" # 低于 10 时缩容
namespace: default
这个配置的关键技巧:
- 查询表达式:使用 PromQL 可以编写极其灵活的指标查询。上面这个例子只对 5xx 错误请求做扩缩容——当错误率升高时自动增加副本以稀释流量,这在应对故障时有奇效。
- metricName:KEDA 要求提供一个唯一的指标名称标识符,在同一个 ScaledObject 的多个 Prometheus trigger 中不能重复。
- threshold 与 activationThreshold 的区别:threshold 决定扩容阈值,activationThreshold 决定缩容/停止扩容的阈值。两者的配合可以避免在临界值附近频繁抖动。
实战案例四:基于 Cron 定时缩放到零
开发环境和测试环境在非工作时间不需要运行任何 Pod,但每天上班前需要自动启动。KEDA 的 cron trigger 可以完美实现这个需求:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: dev-environment-scaler
namespace: dev
spec:
scaleTargetRef:
name: dev-api-server
minReplicaCount: 0
maxReplicaCount: 3
triggers:
- type: cron
metadata:
timezone: Asia/Shanghai
start: "0 9 * * 1-5" # 工作日早 9 点开始扩容
end: "0 18 * * 1-5" # 工作日晚 6 点开始缩容
desiredReplicas: "2" # 运行期间保持 2 个副本
通过 cron trigger,你可以精确控制集群资源的使用时间窗口,非工作时段自动释放资源。对于拥有数十个开发/测试环境的团队来说,仅此一项每月就能节省可观的云资源开支。
ScaledJob:事件驱动的批处理任务
除了常规的 Deployment 和 StatefulSet,KEDA 还支持通过 ScaledJob 来驱动 Kubernetes Job 的自动创建。每个事件触发一个或多个 Job 实例,任务完成后自动清理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 apiVersion: keda.sh/v1alpha1
kind: ScaledJob
metadata:
name: image-processor-job
namespace: default
spec:
jobTargetRef:
template:
spec:
containers:
- name: worker
image: registry.example.com/image-processor:latest
command: ["/app/process", "--input", "$(MESSAGE)"]
env:
- name: MESSAGE
valueFrom:
secretKeyRef:
name: job-payload
key: message
restartPolicy: Never
backoffLimit: 1
pollingInterval: 10
maxScale: 50 # 最多同时运行 50 个 Job
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
triggers:
- type: aws-sqs-queue
authenticationRef:
name: aws-creds
metadata:
queueURL: https://sqs.ap-northeast-1.amazonaws.com/1234567890/image-tasks
queueLength: "1" # 队列中有 1 条消息就创建一个 Job
awsRegion: ap-northeast-1
ScaledJob 非常适合以下场景:
- 视频/图片转码:每个上传文件触发一个 Job
- 数据导出报表:用户请求导出时触发一个 Job
- AI 推理批处理:模型调用队列中每个请求触发一个推理 Job
- 数据库迁移脚本:基于消息触发一次性数据库操作
生产环境的最佳实践
1. 合理设置 pollingInterval 和 cooldownPeriod
| 场景 | pollingInterval | cooldownPeriod | 说明 |
|---|---|---|---|
| Kafka 消费者(高吞吐) | 10-15s | 120-300s | 频繁探测 Lag 变化,冷却时间长避免抖动 |
| Prometheus 指标 | 30-60s | 180s | Prometheus 本身有采集间隔,没必要探测太频繁 |
| HTTP 请求数 | 15-30s | 120s | 流量变化快,需要较快响应 |
| 数据库连接池 | 60s | 300s | 数据库连接建立开销大,缩容应更保守 |
2. 使用 Pod Identity 替代明文凭据
KEDA 支持多种身份认证机制,在生产环境中应优先使用云厂商的原生 Pod Identity 而非明文 Secret:
1
2
3
4
5
6
7 apiVersion: keda.sh/v1alpha1
kind: ClusterTriggerAuthentication
metadata:
name: azure-pod-identity
spec:
podIdentity:
provider: azure-workload # 支持 aws-kiam, aws-eks, gcp, azure-workload, azure-pod 等
这样 KEDA Metrics Server 可以直接通过云厂商的临时凭证访问事件源(如 Azure Service Bus、AWS SQS、GCP Pub/Sub 等),无需在集群中存储任何长期有效的密钥。
3. 设置 ScaledObject 的 fallback 策略
事件源可能因为网络故障或认证问题暂时不可达。没有 fallback 策略的话,HPA 会因为拉不到指标而保持当前副本数不变。更糟糕的是,如果正好在流量高峰期发生故障,过时的副本数可能无法承载实际流量。
务必为生产环境的每个 ScaledObject 设置 fallback:
1
2
3
4 spec:
fallback:
failureThreshold: 3
replicas: 5
4. 监控 KEDA 自身的健康状态
KEDA 暴露了丰富的 Prometheus 指标用于自身监控:
1
2
3
4
5
6
7
8 # 查看 KEDA Operator 的指标
curl http://keda-operator.keda.svc:8080/metrics | grep keda_
# 关键指标
keda_scaler_errors_total # 累计错误数
keda_scaler_metrics_value # 当前指标值
keda_scaler_polling_duration_seconds # 轮询耗时分布
keda_trigger_registration_count # 已注册的 ScaledObject 数量
在 Grafana 中为这些指标配置告警:如果某个 ScaledObject 的轮询错误率在 5 分钟内超过 20%,应立即告警处理。
5. 渐进式推广策略
不要一次性将全部工作负载迁移到 KEDA。推荐的循序渐进路径:
- 第一步:在非生产环境的非关键服务上试点(如开发/测试环境的 cron 缩放到零)
- 第二步:将低优先级的消息队列消费者迁移到 KEDA,观察扩缩容行为是否正常
- 第三步:为 Prometheus 类指标(已有数据积累的指标)创建 ScaledObject,与传统 HPA 并存运行
- 第四步:逐步淘汰传统的基于资源的 HPA,全面转向事件驱动的 KEDA 策略
常见陷阱与避坑指南
陷阱一:minReplicaCount 为 0 时的冷启动延迟
当
1 | minReplicaCount: 0 |
且长时间没有事件时,KEDA 将副本数设为 0。当新事件到来时,KEDA 需要先将副本数从 0 改为 1,然后 Deployment Controller 创建 Pod,Pod 经历拉取镜像、初始化、就绪探测等流程,整个过程可能需要 30 秒到数分钟(取决于镜像大小和应用启动时间)。
解决方案:对于延迟敏感的服务,设置
1 | minReplicaCount: 1 |
或使用
1 | activationThreshold |
提前预热。对于可以容忍启动延迟的批处理服务,使用 ScaledJob 而非 ScaledObject。
陷阱二:多个 trigger 的扩缩容逻辑
当 ScaledObject 配置了多个 trigger 时,KEDA 会取所有 trigger 中最大的期望副本数作为最终值。例如 Kafka trigger 期望 10 个副本,Prometheus trigger 期望 5 个副本,最终结果为 10。这意味着混合使用不同类型的 trigger 时,需要确保它们之间的逻辑不冲突。
陷阱三:扩缩容抖动(Thrashing)
如果队列深度或自定义指标频繁在阈值附近波动,HPA 会反复创建和删除 Pod,导致服务不稳定和 API Server 负载升高。缓解措施:
- 设置合理的
1cooldownPeriod
(至少 60-120 秒)
- 利用 HPA behavior 策略中的
1stabilizationWindowSeconds
- 对阈值应用一定的滞回区间(hysteresis),例如扩容阈值设为 100,缩容阈值设为 50
总结
KEDA 将 Kubernetes 的自动扩缩容从”基于资源”提升到了”基于事件”的层面,让应用可以根据消息队列深度、数据库连接数、自定义 Prometheus 指标、甚至是定时计划来动态调整容量。它与原生 HPA 完全兼容,部署简单,无需修改应用代码,是构建弹性和成本优化 Kubernetes 基础设施的关键工具。
本文介绍的四个实战案例(Kafka、RabbitMQ、Prometheus、Cron)覆盖了最常见的生产场景。如果你正在寻找一种比传统 HPA 更灵活、更经济的自动扩缩容方案,KEDA 无疑是当前 CNCF 生态中最成熟的选择。建议先在开发环境中通过本文的配置模板进行试点,逐步积累经验后再推广到生产集群。
延伸阅读:
汤不热吧