目录

kubernetes应用-自动扩缩容神器-KEDA

解决业务服务在业务高峰期间产生的巨大波动和服务与资源的实际利用率问题.

KEDA 实现原理

KEDA 在 Kubernetes 里面有 3 个组件:

  1. Agent — KEDA 激活和停用 Kubernetes 的 Deployments 服务,以便在没有事件的情况下把服务的 Pod 数量缩容到零。它是 keda-operator 的主要角色之一。

  2. Metrics — KEDA 充当了 Kubernetes 度量服务器,向 HPA 提供了丰富的事件数据,如队列长度或消息堆积,并以此依据完成 Pod 的扩缩容。它是运行在 keda-operator-metrics-apiserver 容器的主要角色。。

  3. Admission Webhooks — 使用准入控制器自动验证资源变更,以防止错误配置被应用到集群内,是实施最佳实践的前提。 例如,它可以防止多个 ScaledObjects 控制了同一个目标 Deployment 的自动扩缩容。

KEDA 架构图

/kubernetes%E5%BA%94%E7%94%A8-%E8%87%AA%E5%8A%A8%E6%89%A9%E7%BC%A9%E5%AE%B9%E7%A5%9E%E5%99%A8-keda/KEDA%E6%9E%B6%E6%9E%84%E5%9B%BE.png
KEDA 架构图

CRD:

安装完 KEDA 后,我们可以使用属于它的 CRD 资源来配置自动扩缩容。

  • ScaledObjects — 表示事件源和目标资源对象的扩缩容映射,定义了用何种事件源来扩缩何种资源对象(Deployments 、StatefulSet 等)。

  • ScaledJobs 表示事件源和 Kubernetes job 资源对象的映射。

ScaledObject/ScaledJob 引入了 TriggerAuthentication 和 ClusterTriggerAuthentication 用于配置监听事件源所需的身份验证或秘钥。

在开始之前,我们先用一份 demo yaml 来简单认识一下 ScaledObject 的结构:

 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
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: {scaled-object-name}
spec:
  scaleTargetRef:
    apiVersion:    {api-version-of-target-resource}  # Optional. Default: apps/v1
    kind:          {kind-of-target-resource}         # Optional. Default: Deployment
    name:          {name-of-target-resource}         # Mandatory. Must be in the same namespace as the ScaledObject
    envSourceContainerName: {container-name}         # Optional. Default: .spec.template.spec.containers[0]
  pollingInterval:  30                               # Optional. Default: 30 seconds
  cooldownPeriod:   300                              # Optional. Default: 300 seconds
  idleReplicaCount: 0                                # Optional. Default: ignored, must be less than minReplicaCount 
  minReplicaCount:  1                                # Optional. Default: 0
  maxReplicaCount:  100                              # Optional. Default: 100
  fallback:                                          # Optional. Section to specify fallback options
    failureThreshold: 3                              # Mandatory if fallback section is included
    replicas: 6                                      # Mandatory if fallback section is included
  advanced:                                          # Optional. Section to specify advanced options
    restoreToOriginalReplicaCount: true/false        # Optional. Default: false
    horizontalPodAutoscalerConfig:                   # Optional. Section to specify HPA related options
      name: {name-of-hpa-resource}                   # Optional. Default: keda-hpa-{scaled-object-name}
      behavior:                                      # Optional. Use to modify HPA's scaling behavior
        scaleDown:
          stabilizationWindowSeconds: 300
          policies:
          - type: Percent
            value: 100
            periodSeconds: 15
  triggers:
  # {list of triggers to activate scaling of the target resource}

ScaledObject 大致分两部分:

  • scaleTargetRef

    目标资源对象的详细的详细配置参数。

  • triggers 使用何种事件源,以及事件源的详细配置参数。

前面我们提到过 KEDA 内置了几十种内置的缩放器,我们会拿其中几个比较有代表性的使用场景出来演示一下 demo 。

缩放器类型

它为 Kubernetes 资源提供了几十个内置缩放器,因此我们不必为我们的各种指标源编写自定义适配器。 KEDA 提供了将资源扩展到零的强大功能。它可以将资源从 0 扩展到 1 、从 1 扩展到 0 或从 1 扩展到 n 。 支持的事件源有 Kafka 、Redis 、Prometheus 、Mysql 、Cassandra 、Elasticsearch 等。

1.资源类型

这种类型的扩缩容跟原生的一样,我们也演示一下。Deployment 还是使用原生 HPA 的那个 hpa-demo 。

我们创建一份 ScaledObject ,triggers 使用 cpu 资源类型来做事件源,cpu 使用率超过 50% 以后,HPA 会使用公式计算是否开始扩缩容。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: hpa-demo-cpu
  namespace: default
spec:
  scaleTargetRef:
    name: hpa-demo
  minReplicaCount: 1
  maxReplicaCount: 10
  triggers:
  - type: cpu
    metricType: Utilization # Allowed types are 'Utilization' or 'AverageValue'
    metadata:
      value: "50"

我们看到 ScaledObject 已经被创建。

/kubernetes%E5%BA%94%E7%94%A8-%E8%87%AA%E5%8A%A8%E6%89%A9%E7%BC%A9%E5%AE%B9%E7%A5%9E%E5%99%A8-keda/ScaledObject-cpu.png

它的原理也是通过创建原生 HPA 来实现的。

/kubernetes%E5%BA%94%E7%94%A8-%E8%87%AA%E5%8A%A8%E6%89%A9%E7%BC%A9%E5%AE%B9%E7%A5%9E%E5%99%A8-keda/ScaledObject-cpu-hpa.png

启动无限发送请求,观察扩缩容的情况,效果跟原生是一样的。

/kubernetes%E5%BA%94%E7%94%A8-%E8%87%AA%E5%8A%A8%E6%89%A9%E7%BC%A9%E5%AE%B9%E7%A5%9E%E5%99%A8-keda/ScaledObject-cpu-pod.png

2.prometheus

很多时候,使用 cpu 和 memory 资源类型并不能很好地反应服务的真实负载情况,比如 web 服务,qps 达到一定量的时候,有可能 cpu 和 内存使用率都很低,但是后端 Pod 的连接数已经满了这种情况。

我们使用 qps 的场景来演示一下,Deployment 还是使用原生 HPA 的那个 hpa-demo 。yaml 配置如下:

 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: hpa-demo-prom
  namespace: default
spec:
  scaleTargetRef:
    name: hpa-demo
  minReplicaCount: 1
  maxReplicaCount: 10
  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://rancher-monitoring-prometheus.cattle-monitoring-system.svc:9090
      metricName: nginx_ingress_controller_requests # DEPRECATED: This parameter is deprecated as of KEDA v2.10 and will be removed in version 2.12
      threshold: '100'
      query: sum(rate(nginx_ingress_controller_requests{ingress="hpa-demo-ingress"}[2m]))
  • triggers 参数解释

    1. type 类型我们使用 prometheus 。

    2. serverAddress 填写 prometheus 地址。

    3. metricName 填写我们要监控的指标。

    4. threshold 填写达到多少阈值就扩容 (需要注意的点:这里的 1 等于 HPA 的 1000m)

    5. query 填写查询表达式。

配置文件应用到集群,开始对 prometheus 指定的指标进行监控。

/kubernetes%E5%BA%94%E7%94%A8-%E8%87%AA%E5%8A%A8%E6%89%A9%E7%BC%A9%E5%AE%B9%E7%A5%9E%E5%99%A8-keda/ScaledObject-prom.png

我们看到它也自动创建了 HPA 实例。

/kubernetes%E5%BA%94%E7%94%A8-%E8%87%AA%E5%8A%A8%E6%89%A9%E7%BC%A9%E5%AE%B9%E7%A5%9E%E5%99%A8-keda/ScaledObject-prom-hpa.png

我们启动无限循环的请求,看到它已经扩容了。

/kubernetes%E5%BA%94%E7%94%A8-%E8%87%AA%E5%8A%A8%E6%89%A9%E7%BC%A9%E5%AE%B9%E7%A5%9E%E5%99%A8-keda/ScaledObject-prom-pod.png

3.kafka 堆积

Kafka 消费堆积这种情况,我们不能使用上面那种类型的服务了,需要新创建一个服务来进行模拟,废话不多,我们先自己撸一套生产者/消费组示例来演示。

 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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 消费者
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hpa-demo-consumer
spec:
  selector:
    matchLabels:
      run: hpa-demo-consumer
  replicas: 1
  template:
    metadata:
      labels:
        run: hpa-demo-consumer
    spec:
      hostAliases:
        - ip: "192.168.21.3"
          hostnames:
          - "xxx-dev-21-3"
      containers:
      - name: hpa-demo-consumer
        imagePullPolicy: IfNotPresent
        image: xxx/hpa-demo-consumer:v1.0
        resources:
          limits:
            cpu: 200m
          requests:
            cpu: 100m
        env:
          - name: ADDR
            value: "192.168.21.3:9093"
          - name: TOPIC
            value: "hpa-demo-topic"
          - name: GROUP
            value: "hpa-demo-group"
---
# 生产者
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hpa-demo-producer
spec:
  selector:
    matchLabels:
      run: hpa-demo-producer
  replicas: 8
  template:
    metadata:
      labels:
        run: hpa-demo-producer
    spec:
      hostAliases:
        - ip: "192.168.21.3"
          hostnames:
          - "xxx-dev-21-3"
      containers:
      - name: hpa-demo-consumer
        imagePullPolicy: IfNotPresent
        image: xxx/hpa-demo-producer:v1.0
        resources:
          limits:
            cpu: 2
          requests:
            cpu: 2
        env:
          - name: ADDR
            value: "192.168.21.3:9093"
          - name: TOPIC
            value: "hpa-demo-topic"

我们开 8 个生产者,1个消费组,可以看到,消息堆积一下子就飞上去了。

/kubernetes%E5%BA%94%E7%94%A8-%E8%87%AA%E5%8A%A8%E6%89%A9%E7%BC%A9%E5%AE%B9%E7%A5%9E%E5%99%A8-keda/ScaledObject-lag-up.png

创建 KEDA kafka 类型的缩放器。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: hpa-demo-kafka
  namespace: default
spec:
  scaleTargetRef:
    name: hpa-demo-consumer
  pollingInterval: 30
  minReplicaCount: 1
  # Pod 数量不能大于分区数
  maxReplicaCount: 3
  triggers:
  - type: kafka
    metadata:
      bootstrapServers: 192.168.21.3:9093
      consumerGroup: hpa-demo-group
      topic: hpa-demo-topic
      # Optional
      lagThreshold: "5000"
      offsetResetPolicy: latest
注意
maxReplicaCount 不能大于 topic 的分区数,虽然大于这个数扩缩功能也没什么问题,但是多出来的 Pod 不会起到消费的作用,只会白白浪费资源而已。

看到它也自动给创建了 HPA 实例。

/kubernetes%E5%BA%94%E7%94%A8-%E8%87%AA%E5%8A%A8%E6%89%A9%E7%BC%A9%E5%AE%B9%E7%A5%9E%E5%99%A8-keda/ScaledObject-kafka-hpa.png

30s 的周期,读取到堆积的情况后,马上给弹出到 3 个 pod 。

/kubernetes%E5%BA%94%E7%94%A8-%E8%87%AA%E5%8A%A8%E6%89%A9%E7%BC%A9%E5%AE%B9%E7%A5%9E%E5%99%A8-keda/ScaledObject-kafka-pod.png

堆积断崖式下跌。

/kubernetes%E5%BA%94%E7%94%A8-%E8%87%AA%E5%8A%A8%E6%89%A9%E7%BC%A9%E5%AE%B9%E7%A5%9E%E5%99%A8-keda/ScaledObject-lag-down.png

总结

我们之前也有用过 prometheus adapter 去实现自定义指标 hpa ,同样也能实现我们想要的效果,但是过程过于繁琐,出了问题也不好排查。

使用 KEDA 的好处就是,它内置了几十种缩放器,事件源含盖了大部分主流的应用,使得我们无需重复造轮子,这种开箱即用的便利,加上声明式的配置,大大减轻我们运维人员的工作,简直是运维人士的福音。

其中 prometheus 类型事件源也可以代替 prometheus adapter ,用来做自定义指标的扩缩容,妥妥的扩缩容神奇。