kubernetes pod的弹性伸缩(三)(基于事件驱动伸缩)

软件 版本
keda 2.3.0
Kubernetes 1.20.7

概述

目前在Kubernetes中做POD弹性伸缩HPA有基于CPU、memory这种资源指标实现和通过custom-metrics-apiserver和Prometheus-adapter实现的自定义业务监控指标的弹性伸缩,但这些都不是很灵活有效,基于cpu、memory实际对业务带来价值并不大,并不能很准确代表业务实际是否需要扩缩容。基于自定义业务监控指标配置起来相当繁琐。所以社区这些问题背景下开发了KEDA,基于事件的伸缩

KEDA是什么?

KEDA是红帽和微软联合开发的一个开源项目,后面贡献给了CNCF基金会,目前处于sandbox阶段,实现KEDA的目的主要为了更好和更加灵活使用HPA。

项目地址
https://github.com/kedacore/keda

文档地址
https://keda.sh/

什么是事件驱动?

举个简单例子,常见的消息队列系统,有生产者和消费者,生产者往消息队列中吐数据,消费者从消息队列中消费数据,当出现消息堆积时,此时是消费者处理不过来了,应该及时扩容消费者。这就是keda可以实现的,通过对应的接口去查看消息队列系统的情况,进行判断,调用HPA进行扩缩容。

keda能做什么?

图片来源:https://developer.ibm.com/technologies/messaging/articles/introduction-to-keda/


图片来源:https://keda.sh/docs/1.4/concepts/

KEDA 监控定义的事件源,并定期检查是否有任何事件。达到设置的阈值后,KEDA 会根据部署的复制件计数设置为 1 或 0,根据配置最小副本计数,激活或停用POD,具体取决于是否有任何事件。
通过上面两幅架构图可以看见keda和HPA是相辅相成的,keda实现的是事件收集和pod副本数0->1,1->0的调整,HPA实现1->n,n->1的POD副本调整.

keda组成

keda由两个组件组成:
keda-operator:创建维护hpa资源对象,同时激活hpa伸缩(0->1,1->0)。

keda-metrics-apiserver: 实现了hpa中external metrics,根据事件源配置返回计算结果,推动hpa进行。

keda定义的CRD对象主要有三个

  • ScaledObjects:定义事件源和资源映射以及控制的范围。
  • ScaledJobs:定义事件源和job资源映射以及控制的范围。
  • TriggerAuthentication:监控事件源的认证配置

测试使用

部署

keda部署非常简单,有三种方式

  • Helm charts
  • Operator Hub
  • YAML declarations

这里,我们直接用yaml进行,一个yaml即可搞定

1
kubectl apply -f https://github.com/kedacore/keda/releases/download/v2.3.0/keda-2.3.0.yaml

部署完后检查

1
2
3
4
kubectl get pod -n keda
NAME READY STATUS RESTARTS AGE
keda-metrics-apiserver-84d8fc689d-62kc2 1/1 Running 0 5h23m
keda-operator-54f95d8598-l5vjg 1/1 Running 0 5h23m

看状态是否正常,也建议看看这两个组件的log是否有报错。

接下来进行测试使用,直接使用官方github里面的例子。

RabbitMQ弹性

实现效果

图片来源:(https://jstobigdata.com/rabbitmq/direct-exchange-in-amqp-rabbitmq/)
当rabbimq队列达到设定的长度后扩容consumer副本数,当队列长度降下去后将副本数缩小为对应的配置值。

部署Rabbimq-server

1
helm repo add bitnami https://charts.bitnami.com/bitnami

部署rabbitmq

1
helm install rabbitmq --set auth.username=user --set auth.password=PASSWORD bitnami/rabbitmq

查看是否部署成功

1
2
3
kubectl get pod
NAME READY STATUS RESTARTS AGE
rabbitmq-0 1/1 Running 0 7h37m

部署 RabbitMQ consumer

repo里面是配置rabbimq-consumer和keda规则

1
git clone https://github.com/kedacore/sample-go-rabbitmq.git

1
cd sample-go-rabbitmq

部署

1
2
3
4
5
6
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: rabbitmq-consumer
namespace: default
spec:
scaleTargetRef:
name: rabbitmq-consumer
pollingInterval: 5 # Optional. Default: 30 seconds
cooldownPeriod: 30 # Optional. Default: 300 seconds
maxReplicaCount: 30 # Optional. Default: 100
triggers:
- type: rabbitmq
metadata:
queueName: hello
queueLength: "5"
authenticationRef:
name: rabbitmq-consumer-trigger
参数 含义
pollingInterval 定时获取scaler的时间间隔,默认30s
cooldownPeriod 上次active至缩容为0需要等待的时间,默认300s
maxReplicaCount 最大POD副本数
minReplicaCount 最小POD副本数,默认为0
triggers.type 事件类型是keda支持的哪种,如是redis就写redis,是kafka是写kafka
trigger.metadata.queueName 这个根实际事件类型有关的触发器指标了,这里定义的是rabbitmq的队列名
trigger.metadata.queueLength 触发器定义的队列长度
trigger.authenticationRef 认证关联,关联TriggerAuthentication

部署后会发现rabbitmq-consumer副本数会自动变为0,因为此时队列是空的

1
2
3
kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
rabbitmq-consumer 0/0 0 0 1h53m

此时get hpa也创建出来了

1
2
3
kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
keda-hpa-rabbitmq-consumer Deployment/rabbitmq-consumer <unknown>/5 (avg) 1 30 0 1h

部署produce往Rabbitmq里面推数据制造拥堵

这个job将推送 300信息到 “hello” 这个队列 keda将在2分种扩容 deployment到30个

1
kubectl apply -f deploy/deploy-publisher-job.yaml

对应的我们也可以观察hpa和pod的扩缩容

已经在迅速扩容了

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
ubectl get pod
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-68df844cc-dclwb 1/1 Running 0 8h
rabbitmq-0 1/1 Running 0 8h
rabbitmq-consumer-5c486c869c-4pwxj 0/1 ContainerCreating 0 40s
rabbitmq-consumer-5c486c869c-5wd9k 0/1 ContainerCreating 0 40s
rabbitmq-consumer-5c486c869c-6zgs9 1/1 Running 0 56s
rabbitmq-consumer-5c486c869c-78lx9 0/1 ContainerCreating 0 40s
rabbitmq-consumer-5c486c869c-7g4wf 1/1 Running 0 71s
rabbitmq-consumer-5c486c869c-7qhqn 0/1 ContainerCreating 0 40s
rabbitmq-consumer-5c486c869c-8rj6l 1/1 Running 0 56s
rabbitmq-consumer-5c486c869c-bmjqx 0/1 ContainerCreating 0 40s
rabbitmq-consumer-5c486c869c-db2ts 1/1 Running 0 71s
rabbitmq-consumer-5c486c869c-ddzhp 1/1 Running 0 56s
rabbitmq-consumer-5c486c869c-dgwn4 0/1 ContainerCreating 0 40s
rabbitmq-consumer-5c486c869c-f4k8l 1/1 Running 0 87s
rabbitmq-consumer-5c486c869c-jqbhl 1/1 Running 0 87s
rabbitmq-consumer-5c486c869c-k2mdw 0/1 ContainerCreating 0 40s
rabbitmq-consumer-5c486c869c-kpgc2 0/1 ContainerCreating 0 56s
rabbitmq-consumer-5c486c869c-kzlwl 0/1 ContainerCreating 0 56s
rabbitmq-consumer-5c486c869c-ls4dm 1/1 Running 0 56s
rabbitmq-consumer-5c486c869c-lvtx8 0/1 ContainerCreating 0 40s
rabbitmq-consumer-5c486c869c-ngmnj 1/1 Running 0 71s
rabbitmq-consumer-5c486c869c-nxp4n 0/1 ContainerCreating 0 40s
rabbitmq-consumer-5c486c869c-p27lk 1/1 Running 0 71s
rabbitmq-consumer-5c486c869c-p4d59 0/1 ContainerCreating 0 40s
rabbitmq-consumer-5c486c869c-q9wdl 0/1 ContainerCreating 0 40s
rabbitmq-consumer-5c486c869c-qqgdw 1/1 Running 0 56s
rabbitmq-consumer-5c486c869c-rpghb 1/1 Running 0 89s
rabbitmq-consumer-5c486c869c-sr7hd 0/1 ContainerCreating 0 40s
rabbitmq-consumer-5c486c869c-sxl8t 1/1 Running 0 87s
rabbitmq-consumer-5c486c869c-vk9wp 0/1 ContainerCreating 0 56s
rabbitmq-consumer-5c486c869c-xgnbf 0/1 ContainerCreating 0 40s
rabbitmq-consumer-5c486c869c-xn76k 0/1 ContainerCreating 0 40s
rabbitmq-publish-rxjpl 0/1 Completed 0 109s

等待队列数据消费完了,等待冷却期,pod会自行销毁。

基于Prometheus自定义监控指标弹性

使用自定义HPA监控的例子测试

clone代码

1
git clone https://github.com/stefanprodan/k8s-prom-hpa

切换到k8s-prom-hpa目录

1
2
kubectl create namespace monitoring
kubectl create -f ./prometheus

查看prometheus

1
2
3
4
5
6
7
kubectl get pod -n monitoring
NAME READY STATUS RESTARTS AGE
prometheus-64bc56989f-qcs4p 1/1 Running 1 22h
kubectl get svc -n monitoring
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
prometheus NodePort 10.104.215.207 <none> 9090:31190/TCP 22h

访问prometheus
http://node_ip:31190

prometheus内prometheus-cfg.yaml 配置了自动发生规则,会自动将组件注册。

部署Podinfo应用测试custom-metric autoscale

1
kubectl create -f ./podinfo/podinfo-svc.yaml,./podinfo/podinfo-dep.yaml

prometheus配置了自动发现规则,在podinfo-dep.yaml里面配置了对应的规则

1
2
annotations:
prometheus.io/scrape: 'true'

所以应用一启动就能直接在prometheus-target中发现。

配置keda

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: prometheus-scaledobject
namespace: default
spec:
scaleTargetRef:
name: podinfo
pollingInterval: 5# Optional. Default: 30 seconds
cooldownPeriod: 30 # Optional. Default: 300 seconds
maxReplicaCount: 30 # Optional. Default: 100
triggers:
- type: prometheus
metadata:
serverAddress: http://192.168.0.32:31190/
metricName: http_requests_total
threshold: '10'
query: sum(rate(http_requests_total[1m]))

这里10指的是每秒10个请求,按照定义的规则metricsQuery中的时间范围1分钟,这就意味着过去1分钟内每秒如果达到10个请求则会进行扩容。

配置完后使用ab进行压力测试

使用ab进行压力测试,模拟1000个并发量

安装ab

1
apt install apache2-utils -y

ab命令最基本的参数是-n-c

1
2
3
4
5
6
7
8
-n 执行的请求数量
-c 并发请求个数
其他参数:
-t 测试所进行的最大秒数
-p 包含了需要POST的数据的文件
-T POST数据所使用的Content-type头信息
-k 启用HTTP KeepAlive功能,即在一个HTTP会话中执行多个请求,默认时,不启用KeepAlive功能

1
2
ab -n 10000 -c 1000 http://192.168.0.32:31198/

查看是否进行弹性伸缩。

1
2
3
4
5
6
7
8
9
kubectl get pod
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-68df844cc-dclwb 1/1 Running 3 3d8h
podinfo-56874dc7f8-4n9m4 0/1 Running 4 74m
podinfo-56874dc7f8-g89vr 0/1 Running 0 43s
podinfo-56874dc7f8-v2pn8 1/1 Running 4 74m
podinfo-56874dc7f8-xgrz6 0/1 ContainerCreating 0 12s
rabbitmq-0 1/1 Running 0 3d8h
rabbitmq-publish-rxjpl 0/1 Completed 0 2d23h

keda会根据实际的值计算需要弹性的副本数,保证业务可用性。

1
2
3
root@cka01:~# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
keda-hpa-prometheus-scaledobject Deployment/podinfo 13/10 (avg) 1 30 3 3m3s

总结

总的感受下来,还是非常好用的,像之前配一些业务自定义指标的HPA监控还需要配置Prometheus-adapter
配置规则,但通过keda直接可以对接Prometheus,配置对应表达式尽快。其他一些集成的事件触发器也直接使用就可以了。

keda支持的业务事件触发器

参考链接:

https://keda.sh