基于事件驱动的自动伸缩工具 KEDA 简单使用
KEDA 是 Kubernetes 基于事件驱动的自动伸缩工具,通过 KEDA 我们可以根据需要处理的事件数量来驱动 Kubernetes 中任何容器的扩展。KEDA 可以直接部署到任何 Kubernetes 集群中和标准的组件一起工作。
在 Kubernetes 中 KEDA 有两个关键的角色:
扩展客户端:用于激活和停用 Deployments 来扩展到配置的副本,并在没有事件的情况下将副本缩减回零。 Metrics Server:一种 Metrics 指标服务,暴露了大量与事件相关的数据,例如队列长度,允许基于事件的扩展,消耗特定类型的事件数据。
Metrics Server 与 HPA 进行通信,以驱动 Kubernetes 部署副本的扩展。然后由 Deployments 直接从源头消费事件。这样可以保留丰富的事件集成,让完成或丢弃队列消息之类的可以立即使用。Metrics Server 是安装 KEDA 时运行的 keda-operator-metrics-apiserver
容器的主要作用。
KEDA 有广泛的扩展器,既可以检测部署是否应该被激活或停用,也可以为特定事件源提供自定义指标。
当我们安装 KEDA 后,它会创建3个 CRD 资源,这些 CRD 可以使你能够将事件源(以及对该事件源的认证)映射到 Deployment、StatefulSet、自定义资源或 Job 上进行缩放。
scaledobjects.keda.sh
:ScaledObjects
表示事件源(例如 RabbitMQ)与 Kubernetes Deployment、StatefulSet 或定义/scale
子资源的任何自定义资源之间的所需映射。scaledjobs.keda.sh
:ScaledJobs
表示事件源和 Kubernetes Job 之间的映射。triggerauthentications.keda.sh
:ScaledObject/ScaledJob
也可以引用TriggerAuthentication
,其中包含了监控事件源的认证配置或 Secret。
安装
安装 KEDA 有很多方式,这里我们直接使用 Helm 来进行安装,首先添加对应的 Helm 仓库:
➜ helm repo add kedacore https://kedacore.github.io/charts
➜ helm repo update
然后直接使用下面的命令安装即可:
➜ kubectl create namespace keda
➜ helm install keda kedacore/keda --namespace keda
NAME: keda
LAST DEPLOYED: Wed Dec 23 11:45:02 2020
NAMESPACE: keda
STATUS: deployed
REVISION: 1
TEST SUITE: None
安装完成后在 keda 命名空间下面会运行两个 KEDA 相关的 Pod:
➜ kubectl get pods -n keda
NAME READY STATUS RESTARTS AGE
keda-operator-8488964969-d4svr 1/1 Running 0 14m
keda-operator-metrics-apiserver-5b488bc7f6-zrzzm 1/1 Running 0 14m
简单示例
这里我们使用一个简单的 golang 结合 rabbitmq 的示例进行演示,该示例应用将接收来自 RabbitMQ 队列的消息并通过 KEDA 进行扩展。接收方一次(每个实例)将收到一条消息,并 sleep 1秒钟以模拟执行工作,当添加大量队列消息时,KEDA 将驱动容器根据事件源(RabbitMQ)进行扩展。
首先我们需要在 Kubernetes 集群上部署 RabbitMQ 队列,同样我们使用 Helm 来快速安装:
➜ helm repo add bitnami https://charts.bitnami.com/bitnami
➜ helm repo update
➜ helm install rabbitmq --set auth.username=user --set auth.password=PASSWORD,persistence.enabled=false bitnami/rabbitmq
➜ kubectl get pods
NAME READY STATUS RESTARTS AGE
rabbitmq-0 1/1 Running 0 7m52s
......
“注意:对于 RabbitMQ Helm Chart 版本 6.xx 或更早版本,应使用
”Rabbitmq.username
和 rabbitmq.password` 参数指定用户名和密码。
安装完成后 Clone 示例项目:
➜ git clone https://github.com/cnych/sample-go-rabbitmq.git
➜ cd sample-go-rabbitmq
首先我们部署一个 consumer:
➜ kubectl apply -f deploy/deploy-consumer.yaml
secret/rabbitmq-consumer-secret created
deployment.apps/rabbitmq-consumer created
scaledobject.keda.sh/rabbitmq-consumer created
triggerauthentication.keda.sh/rabbitmq-consumer-trigger created
其中最主要的就是 ScaledObject
这个 CRD 对象的定义:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: rabbitmq-consumer
namespace: default
spec:
scaleTargetRef: # scale 的目标引用
name: rabbitmq-consumer
pollingInterval: 5 # 可选. 默认: 30 seconds
cooldownPeriod: 30 # 可选. 默认: 300 seconds
maxReplicaCount: 30 # 可选. 默认: 100
triggers:
- type: rabbitmq # 基于 rabbitmq 进行伸缩
metadata:
queueName: hello # 监听的队列名
queueLength: "5"
authenticationRef:
name: rabbitmq-consumer-trigger
---
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: rabbitmq-consumer-trigger
namespace: default
spec:
secretTargetRef:
- parameter: host
name: rabbitmq-consumer-secret
key: RabbitMqHost
这个消费者被设置为每个实例消耗一条消息,sleep 1秒,然后确认消息的完成。上面的 ScaledObject
被设置为在无事件的情况下最小可扩展到0个副本,最大可扩展到30个副本(优化为每个副本5条消息的队列长度)。在30秒的无事件后,副本将被缩减(冷却期)。这些设置可以根据需要在 ScaledObject
上进行更改。
上面的消费者部署完成后可以看到现在是0个副本,这是因为现在没有任何事件产生,所以最小缩放到0个副本:
➜ kubectl get deploy rabbitmq-consumer
NAME READY UP-TO-DATE AVAILABLE AGE
rabbitmq-consumer 0/0 0 0 27s
➜ kubectl get rs
NAME DESIRED CURRENT READY AGE
rabbitmq-consumer-5c486c869c 0 0 0 52s
➜ kubectl describe rs rabbitmq-consumer-5c486c869c
Name: rabbitmq-consumer-5c486c869c
Namespace: default
Selector: app=rabbitmq-consumer,pod-template-hash=5c486c869c
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 88s replicaset-controller Created pod: rabbitmq-consumer-5c486c869c-xnx47
Normal SuccessfulDelete 87s replicaset-controller Deleted pod: rabbitmq-consumer-5c486c869c-xnx47
然后接下来我们部署生产者来产生消息:
➜ kubectl apply -f deploy/deploy-publisher-job.yaml
job.batch/rabbitmq-publish created
上面的 Job 任务会向正在监听 RabbitMQ 的 "hello"
队列发布300条消息,随着队列的建立,KEDA 将进行自动水平伸缩,直到队列在大约 2 分钟后耗尽,并发 Pod 最多 30 个。我们可以通过下面的命令来观察消费者的变化:
➜ kubectl get deploy -w
NAME READY UP-TO-DATE AVAILABLE AGE
rabbitmq-consumer 6/30 30 6 7m6s
我们也可以看到 Pods 在开始扩容处理队列消息了。随着消息长度的不断增加,更多的 Pods 将被主动创建出来。同样这个时候我们也可以查看 HPA 资源对象的变化:
➜ kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
keda-hpa-rabbitmq-consumer Deployment/rabbitmq-consumer /5 (avg) 1 30 0 8m23s
➜ kubectl describe hpa keda-hpa-rabbitmq-consumer
Name: keda-hpa-rabbitmq-consumer
Namespace: default
......
Conditions:
Type Status Reason Message
---- ------ ------ -------
AbleToScale True SucceededGetScale the HPA controller was able to get the target's current scale
ScalingActive False ScalingDisabled scaling is disabled since the replica count of the target is zero
ScalingLimited True TooManyReplicas the desired replica count is more than the maximum replica count
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedRescale 3m47s horizontal-pod-autoscaler New size: 4; reason: external metric rabbitmq-hello(&LabelSelector{MatchLabels:map[string]string{scaledObjectName: rabbitmq-consumer,},MatchExpressions:[]LabelSelectorRequirement{},}) above target; error: Operation cannot be fulfilled on deployments.apps "rabbitmq-consumer": the object has been modified; please apply your changes to the latest version and try again
Normal SuccessfulRescale 3m30s horizontal-pod-autoscaler New size: 4; reason: external metric rabbitmq-hello(&LabelSelector{MatchLabels:map[string]string{scaledObjectName: rabbitmq-consumer,},MatchExpressions:[]LabelSelectorRequirement{},}) above target
Normal SuccessfulRescale 3m15s horizontal-pod-autoscaler New size: 8; reason: external metric rabbitmq-hello(&LabelSelector{MatchLabels:map[string]string{scaledObjectName: rabbitmq-consumer,},MatchExpressions:[]LabelSelectorRequirement{},}) above target
Normal SuccessfulRescale 2m58s horizontal-pod-autoscaler New size: 16; reason: external metric rabbitmq-hello(&LabelSelector{MatchLabels:map[string]string{scaledObjectName: rabbitmq-consumer,},MatchExpressions:[]LabelSelectorRequirement{},}) above target
Normal SuccessfulRescale 2m43s horizontal-pod-autoscaler New size: 30; reason: external metric rabbitmq-hello(&LabelSelector{MatchLabels:map[string]string{scaledObjectName: rabbitmq-consumer,},MatchExpressions:[]LabelSelectorRequirement{},}) above target
在队列清空和指定的冷却期(ScaledObject
的一个属性,默认为300秒)后,最后一个副本将缩减为零。通过查看 HPA 的事件也可以清楚地看到缩放的过程。