KEDA:Kubernetes 事件驱动自动扩缩容
Kubernetes 原生的 HPA(Horizontal Pod Autoscaler)只能基于 CPU 和内存指标进行扩缩容,面对消息队列积压、数据库连接数、自定义业务指标等场景时显得力不从心。KEDA(Kubernetes Event-driven Autoscaling)正是为填补这一空缺而生。KEDA 作为 HPA 的扩展层,支持 50+ 种外部事件源(Kafka、Redis、RabbitMQ、Prometheus、AWS SQS、Azure Queue 等),能够根据这些外部事件动态调整 Pod 副本数,甚至可以将副本数缩减至零以节省资源,并在有事件到来时迅速从零扩容。KEDA 已是 CNCF 毕业项目,在生产环境中被广泛采用。
服务器配置
KEDA 控制器本身资源占用很少,但要发挥弹性扩缩容的价值,集群需要有足够的资源池来承载突发流量下的 Pod 扩容需求。推荐配置如下:
| 组件 | 规格 |
|---|---|
| CPU | 4 核 |
| 内存 | 8 GB |
| 系统盘 | 60 GB SSD |
| 操作系统 | Ubuntu 22.04 LTS |
| 网络 | 公网 IP |
搭建 K3s 集群可以考虑 雨云服务器 rainyun。注册填2026off 领 5 折,4 核 8GB 机型在演示 KEDA 弹性扩缩容时,能够为多个 Kafka 消费者 Pod 的快速启停提供充足的资源缓冲,同时还可以部署 Kafka、Redis 等消息中间件作为测试用的事件源。
确认集群状态:
bash
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# k3s-node1 Ready control-plane,master 3d v1.29.0+k3s1
安装
添加 Helm 仓库
bash
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
安装 KEDA
bash
kubectl create namespace keda
helm install keda kedacore/keda \
--namespace keda \
--set watchNamespace="" \
--set resources.operator.requests.cpu=100m \
--set resources.operator.requests.memory=100Mi \
--set resources.metricServer.requests.cpu=100m \
--set resources.metricServer.requests.memory=100Mi
验证安装
bash
kubectl get pods -n keda
# NAME READY STATUS AGE
# keda-operator-7d8f5b6b8-xk9pv 1/1 Running 2m
# keda-operator-metrics-apiserver-abc123 1/1 Running 2m
# keda-admission-webhooks-6b9b4f9-lmn2p 1/1 Running 2m
# 验证 CRD 已注册
kubectl get crd | grep keda
# scaledobjects.keda.sh
# scaledjobs.keda.sh
# triggerauthentications.keda.sh
# clustertriggerauthentications.keda.sh
核心概念
ScaledObject
ScaledObject 是 KEDA 的核心 CRD,用于将外部事件源绑定到一个 Deployment、StatefulSet 或自定义资源上,控制其副本数的伸缩。你在 ScaledObject 中定义:
- scaleTargetRef:目标工作负载(Deployment/StatefulSet 等)
- minReplicaCount:最小副本数(设为 0 即启用缩零)
- maxReplicaCount:最大副本数上限
- triggers:触发扩缩容的事件源列表,支持多个触发器
- cooldownPeriod:缩容前的冷却等待时间(秒)
- pollingInterval:KEDA 轮询事件源的间隔(秒)
ScaledJob
ScaledJob 用于基于事件驱动的批处理场景。与 ScaledObject 不同,ScaledJob 每次扩容时创建新的 Job 来处理消息,处理完成后 Job 自动删除,适合每条消息都需要一个独立 Pod 处理的场景(如视频转码、报告生成等)。
TriggerAuthentication / ClusterTriggerAuthentication
TriggerAuthentication 用于安全地将认证信息(API Key、连接字符串、证书等)传递给触发器,避免直接在 ScaledObject 中硬编码敏感信息。ClusterTriggerAuthentication 是集群级别的,可跨命名空间共用。
与 HPA 的关系
KEDA 并不替代 HPA,而是作为 HPA 的外部指标提供者(External Metrics Provider)。KEDA 创建 ScaledObject 后,会自动为目标工作负载生成对应的 HPA 对象,并向 HPA 暴露外部指标。你可以通过 kubectl get hpa 看到 KEDA 自动创建的 HPA 资源。
实战示例
1. 基于 Kafka 消费者积压扩缩容
场景:根据 Kafka Topic 中未消费的消息数量,动态调整消费者 Pod 数量。
首先创建 Kafka 连接认证:
yaml
# kafka-trigger-auth.yaml
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: kafka-trigger-auth
namespace: default
spec:
secretTargetRef:
- parameter: sasl
name: kafka-credentials
key: sasl
- parameter: username
name: kafka-credentials
key: username
- parameter: password
name: kafka-credentials
key: password
- parameter: tls
name: kafka-credentials
key: tls
bash
kubectl create secret generic kafka-credentials \
--from-literal=sasl=plaintext \
--from-literal=username=kafka-user \
--from-literal=password=kafka-password \
--from-literal=tls=disable
创建 ScaledObject:
yaml
# kafka-scaledobject.yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: kafka-consumer-scaler
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: kafka-consumer
minReplicaCount: 0 # 无消息时缩减为零
maxReplicaCount: 20 # 最多扩容到 20 个 Pod
pollingInterval: 15 # 每 15 秒检查一次
cooldownPeriod: 60 # 缩容前等待 60 秒
triggers:
- type: kafka
metadata:
bootstrapServers: kafka.kafka.svc.cluster.local:9092
consumerGroup: my-consumer-group
topic: orders
lagThreshold: "50" # 每个 Pod 处理 50 条积压消息
offsetResetPolicy: latest
authenticationRef:
name: kafka-trigger-auth
bash
kubectl apply -f kafka-trigger-auth.yaml
kubectl apply -f kafka-scaledobject.yaml
2. 基于 Redis List 队列深度扩缩容
场景:根据 Redis List 中待处理任务数量,自动调整 Worker Pod 数量:
yaml
# redis-scaledobject.yaml
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: redis-trigger-auth
namespace: default
spec:
secretTargetRef:
- parameter: address
name: redis-secret
key: address
- parameter: password
name: redis-secret
key: password
---
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: redis-worker-scaler
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: task-worker
minReplicaCount: 1
maxReplicaCount: 30
pollingInterval: 10
cooldownPeriod: 120
triggers:
- type: redis
authenticationRef:
name: redis-trigger-auth
metadata:
listName: task-queue
listLength: "20" # 每个 Pod 处理 20 个队列项
enableTLS: "false"
databaseIndex: "0"
bash
kubectl create secret generic redis-secret \
--from-literal=address=redis.default.svc.cluster.local:6379 \
--from-literal=password=your-redis-password
kubectl apply -f redis-scaledobject.yaml
3. 基于 Prometheus 指标扩缩容
场景:根据业务自定义指标(如 HTTP 请求 QPS)进行扩缩容:
yaml
# prometheus-scaledobject.yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: api-server-scaler
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicaCount: 2
maxReplicaCount: 50
pollingInterval: 30
cooldownPeriod: 180
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus.monitoring.svc.cluster.local:9090
metricName: http_requests_per_second
# 当平均每个 Pod QPS 超过 100 时扩容
threshold: "100"
query: sum(rate(http_requests_total{namespace="default",deployment="api-server"}[1m]))
bash
kubectl apply -f prometheus-scaledobject.yaml
4. ScaledJob 批处理任务
场景:视频转码任务队列,每条消息独立启动一个 Pod 进行处理:
yaml
# video-transcode-scaledjob.yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledJob
metadata:
name: video-transcode-job
namespace: default
spec:
jobTargetRef:
parallelism: 5 # 最多同时运行 5 个 Job
completions: 1
template:
spec:
containers:
- name: transcoder
image: my-transcoder:latest
env:
- name: QUEUE_URL
value: "sqs://my-video-queue"
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"
restartPolicy: Never
pollingInterval: 30
maxReplicaCount: 20
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
triggers:
- type: redis
authenticationRef:
name: redis-trigger-auth
metadata:
listName: video-transcode-queue
listLength: "1" # 每条消息触发一个 Job
bash
kubectl apply -f video-transcode-scaledjob.yaml
5. Cron 触发器(定时扩缩容)
场景:工作时间保持最少 5 个副本,非工作时间缩减至 1 个:
yaml
# cron-scaledobject.yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: business-hours-scaler
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
minReplicaCount: 1
maxReplicaCount: 20
triggers:
- type: cron
metadata:
timezone: Asia/Shanghai
start: "0 9 * * 1-5" # 工作日早 9 点开始
end: "0 18 * * 1-5" # 工作日晚 6 点结束
desiredReplicas: "5" # 工作时间维持 5 个副本
常用命令
bash
# 查看所有 ScaledObject 及其状态
kubectl get scaledobject -A
kubectl describe scaledobject kafka-consumer-scaler -n default
# 查看 KEDA 自动创建的 HPA
kubectl get hpa -n default
kubectl describe hpa keda-hpa-kafka-consumer-scaler -n default
# 查看 ScaledJob
kubectl get scaledjob -A
# 查看 TriggerAuthentication
kubectl get triggerauthentication -A
kubectl get clustertriggerauthentication -A
# 查看 KEDA 控制器日志(排查扩缩容问题)
kubectl logs -n keda deploy/keda-operator -f
kubectl logs -n keda deploy/keda-operator-metrics-apiserver -f
# 监控扩缩容事件
kubectl get events -n default --field-selector reason=KEDAScaleTargetActivated
kubectl get events -n default --field-selector reason=KEDAScaleTargetDeactivated
# 查看当前外部指标值(用于验证触发器是否正常工作)
kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1/namespaces/default/s0-kafka-orders" | python3 -m json.tool
# 强制立即触发扩缩容检查(通过重启 KEDA operator)
kubectl rollout restart deploy/keda-operator -n keda
# 临时暂停 ScaledObject(不影响工作负载,只暂停 KEDA 管理)
kubectl patch scaledobject kafka-consumer-scaler -n default \
--type merge \
-p '{"metadata":{"annotations":{"autoscaling.keda.sh/paused-replicas":"2"}}}'
# 恢复 ScaledObject
kubectl patch scaledobject kafka-consumer-scaler -n default \
--type merge \
-p '{"metadata":{"annotations":{"autoscaling.keda.sh/paused-replicas":null}}}'
KEDA 将 Kubernetes 的弹性扩缩容能力提升到了一个全新的维度。通过与 Kafka、Redis、Prometheus 等生产级事件源的深度集成,以及独特的缩零能力,KEDA 在显著提升系统弹性的同时大幅降低了空闲资源的浪费。无论是高并发消息处理、批量数据处理还是业务流量的波峰波谷应对,KEDA 都能给出优雅的解决方案。