Kubernetes弹性扩缩容之HPA和KEDA

语言: CN / TW / HK

政采云技术团队.png

渝米.png

前言

传统意义上说,弹性伸缩主要解决的问题是容量规划与实际负载的矛盾。而云计算为云原生中提供的优势之一就是弹性能力,从Kubernetes实战出发,不管是在业务稳定性保障还是成本治理角度,弹性扩缩容能力都是必要研究方向。当实际的负载随着业务量访问增大,而逐渐的向集群的资源瓶颈靠拢的时候,接近的时候,能不能快速的响应,来扩容集群的流量,从而来应对这种突发。在Kubernetes平台中,资源弹性分为两个维度:Node级别、Pod级别。针对Node负载,当集群资源池不足时,可以使用CA(Cluster Autoscaler)自动增加Node;针对Pod负载,当Pod资源不足时,可以使用HPA(Horizontal Pod Autoscaler)自动增加Pod副本数量。本文就从Pod级别出发,初探一下水平扩缩容方案中的HPA和KEDA。

1.HPA实现原理

1.1 HPA 介绍

HPA( HorizontalPodAutoscaler )水平自动扩缩容 适用对象:DeploymentStatefulSet等 不适用对象:无法扩缩的对象,例如DaemonSet HPA的演进历程 HPA 已经支持 autoscaling/v1、autoscaling/v2beta1和autoscaling/v2beta2 三个大版本 。 HPA四种类型的指标:Resource、Object、External、Pods autoscaling/v1:只支持基于CPU指标的缩放; autoscaling/v2beta1:支持Resource Metrics(资源指标,如pod的CPU)和Custom Metrics(自定义指标)的缩放; autoscaling/v2beta2:支持Resource Metrics(资源指标,如pod的CPU)和Custom Metrics(自定义指标)和ExternalMetrics(额外指标)的缩放。

通过kubectl api-versions |grep autoscal 查看集群内支持的版本

image.png HPA中metrics类型介绍 metrics中的type字段有四种类型的值:Object、Pods、Resource、External。

  • Resource:指的是当前伸缩对象下的pod的cpu和memory指标,只支持Utilization(使用率)和AverageValue类型的目标值。

# Resource类型的指标 - type: Resource   resource:     name: cpu      # Utilization类型的目标值,Resource类型的指标只支持Utilization和AverageValue类型的目标值     target:       type: Utilization       averageUtilization: 50

  • Object:指的是指定k8s内部对象的指标,数据需要第三方adapter提供,只支持Value和AverageValue类型的目标值。

# Object类型的指标 - type: Object   object:     metric:        # 指标名称       name: requests-per-second      # 监控指标的对象描述,指标数据来源于该对象     describedObject:       apiVersion: networking.k8s.io/v1beta1       kind: Ingress       name: main-route      # Value类型的目标值,Object类型的指标只支持Value和AverageValue类型的目标值     target:       type: Value       value: 10k

  • Pods:指的是伸缩对象Pods的指标,数据需要第三方的adapter提供,只允许AverageValue类型的目标值。

# Pods类型的指标 - type: Pods   pods:     metric:       name: packets-per-second      # AverageValue类型的目标值,Pods指标类型下只支持AverageValue类型的目标值     target:       type: AverageValue       averageValue: 1k

  • External:指的是k8s外部的指标,数据同样需要第三方的adapter提供,只支持Value和AverageValue类型的目标值。

# External类型的指标 - type: External   external:     metric:       name: queue_messages_ready        # 该字段与第三方的指标标签相关联,(此处官方文档有问题,正确的写法如下)       selector:         matchLabels:           env: "stage"           app: "myapp"      # External指标类型下只支持Value和AverageValue类型的目标值     target:       type: AverageValue       averageValue: 30

1.2 HPA实现原理

image.png 使用HPA生效前提: 必须定义 requests参数,必须安装metrics-server 工作流程: 1、创建HPA资源,设定目标CPU使用率限额,以及最大、最小实例数 2、收集一组中(PodSelector)每个Pod最近一分钟内的CPU使用率,并计算平均值 3、读取HPA中设定的CPU使用限额 4、计算:平均值之和/限额,求出目标调整的实例个数 5、目标调整的实例数不能超过1中设定的最大、最小实例数,如果没有超过,则扩容;超过,则扩容至最大的实例个数 6、回到2,不断循环 HPA通过kube-controller-manager定期(定期轮询的时间通过–horizontal-pod-autoscaler-sync-period选项来设置,默认的时间为30秒);如果指标变化太频繁,也可以使用--horizontal-pod-autoscaler-downscale-stabilization指令设置扩缩容延迟时间,表示是自从上次缩容执行结束后,多久可以再次执行缩容,默认是5m。

算法说明:desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )] currentMetricValue表示当前度量值,desiredMetricValue表示期望度量值,desiredReplicas表示期望副本数。例如,当前度量值为 200m,目标设定值为 100m,那么由于 200.0/100.0 == 2.0, 副本数量将会翻倍。 如果当前指标为 50m,副本数量将会减半,因为50.0/100.0 == 0.5

算法源代码说明: 必须要定义requests参数源码

// 获取Pod resource request func calculatePodRequests(pods []*v1.Pod, resource v1.ResourceName) (map[string]int64, error) {    requests := make(map[string]int64, len(pods))    for _, pod := range pods {     podSum := int64(0)     for _, container := range pod.Spec.Containers {      if containerRequest, ok := container.Resources.Requests[resource]; ok {       podSum += containerRequest.MilliValue()   } else {       return nil, fmt.Errorf("missing request for %s", resource)   } }     requests[pod.Name] = podSum }    return requests, nil }

使用率算法源码

// 计算使用率 func GetResourceUtilizationRatio(metrics PodMetricsInfo, requests map[string]int64, targetUtilization int32) (utilizationRatio float64, currentUtilization int32, rawAverageValue int64, err error) {    metricsTotal := int64(0)    requestsTotal := int64(0)    numEntries := 0 ​    for podName, metric := range metrics {     request, hasRequest := requests[podName]     if !hasRequest {      // we check for missing requests elsewhere, so assuming missing requests == extraneous metrics      continue } ​     metricsTotal += metric.Value     requestsTotal += request     numEntries++ } ​    currentUtilization = int32((metricsTotal * 100) / requestsTotal) ​    return float64(currentUtilization) / float64(targetUtilization), currentUtilization, metricsTotal / int64(numEntries), nil }

1.3 弹性伸缩小例子

实验小例子:创建nginx服务,为nginx服务创建一个HPA资源,当时nginx服务CPU使用率超过30%时则触发水平扩容机制。(依赖metrics数据,集群中需要提前部署好metrics-server)

``` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-hpa labels:   app: nginx-hpa spec: replicas: 2 selector:   matchLabels:     app: nginx-hpa   template:   metadata:     labels:       app: nginx-hpa   spec:     containers:     - name: nginx-hpa       image: nginx:1.7.9       ports:       - containerPort: 80       resources:         requests:                         ##必须设置,不然HPA无法运行。           cpu: 200m


apiVersion: v1 kind: Service metadata: labels:   app: nginx-hpa name: nginx-hpa spec: ports: - port: 80   protocol: TCP   targetPort: 80 selector:   app: nginx-hpa    


kind: HorizontalPodAutoscaler metadata: name: nginx-hpa-hpa spec: scaleTargetRef:   apiVersion: apps/v1   kind: Deployment   name: nginx-hpa minReplicas: 1 maxReplicas: 3 metrics: - type: Resource   resource:     name: cpu     target:       type: Utilization       averageUtilization: 30 ​

也可以通过kubectl autoscale来创建 HPA 对象。

将会为名为nginx-hpa的ReplicationSet 创建一个HPA对象,目标CPU 使用率为%,副本数量配置为1到3之间

kubectl autoscale rs nginx-hpa --min=1 --max=3 --cpu-percent=30 ```

使用ab命令创建一个简易http服务压测逻辑

yum install httpd -y ​ for i in {1..600} do   ab -c 1000 -n 100000000 http://ServiceIP/   sleep done

2.HPA实现局限性

1、 HPA 无法将pod实例缩到0,即不能从n->0,或者从0-1,根据算法实现说明可以看出desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )] 。desiredMetricValue作为分母,期望值不能是'0';currentMetricValue当前值是'0'的时候,任务数乘'0'都为零。 2、使用率计算方式 计算方式存在争议,在Resource类型中,使用率计算是通过request而不是limit,如果按照request来计算使用率(会超过100%)是不符合预期的。但也是可以修改源码,或者使用自定义指标来代替。 3、多容器Pod使用率问题 1.20版本中已经支持了ContainerResource可以配置基于某个容器的资源使用率来进行扩缩,如果是之前的版本建议使用自定义指标替换。 4、性能问题 单线程架构:默认的hpa-controller是单个Goroutine执行的,随着集群规模的增多,势必会成为性能瓶颈,目前默认hpa资源同步周期会15s,假设每个metric请求延时为100ms,当前架构只能支持150个HPA资源(保证在15s内同步一次)。

3.KEDA引入

3.1 KEDA是什么、KEDA和HPA是什么关系

KEDA是什么: KEDA 是一个基于 Kubernetes 的事件驱动自动扩缩器。 它为 Kubernetes 资源提供了30 多个内置缩放器,因此我们不必担心为我们需要的各种指标源编写自定义适配器。 KEDA 提供了将资源扩展到零的强大功能。KEDA 可以将资源从0扩展到 1或从 1 扩展到 0,从1 到 n 以及向后扩展由 HPA 负责。 KEDA安装使用要求Kubernetes集群 1.16 或以上版本 KEDA和HPA是什么关系: “既生瑜何生亮”? 主要是 HPA 这哥们天生有缺陷,无法基于灵活的事件源进行伸缩,KEDA去帮助实现,当然没有一个事物诞生是完美的,只有在特定的场景下才能相较谁更完美。

3.2 KEDA实现原理

KEDA 哪些核心组件: Metrics Adapter: 将 Scaler 获取的指标转化成 HPA 可以使用的格式并传递给 HPA Controller:负责创建和更新一个 HPA 对象,并负责扩缩到零 Scaler:连接到外部组件(例如 Prometheus或者例如,RabbitMQ并获取指标(例如,待处理消息队列大小) )获取指标 KEDA实现原理图: image.png KEDA源码怎样实现n-0的:

​ //当前副本数为0,并是所有scaler属于active状态,则修改副本数为MinReplicaCount 或 1 if currentScale.Spec.Replicas == 0 && isActive {    e.scaleFromZero(ctx, logger, scaledObject, currentScale) } else if !isActive && currentScale.Spec.Replicas > 0 && (scaledObject.Spec.MinReplicaCount == nil || *scaledObject.Spec.MinReplicaCount == 0) {     // 所有scaler都处理not active状态,并且当前副本数大于0,且MinReplicaCount设定为0     // 则缩容副本数为0     e.scaleToZero(ctx, logger, scaledObject, currentScale) } else if !isActive && scaledObject.Spec.MinReplicaCount != nil && currentScale.Spec.Replicas < *scaledObject.Spec.MinReplicaCount {     // 所有scaler都处理not active状态,并且当前副本数小于MinReplicaCount,则修改为MinReplicaCount     currentScale.Spec.Replicas = *scaledObject.Spec.MinReplicaCount ​     err := e.updateScaleOnScaleTarget(ctx, scaledObject, currentScale)     .... } else if isActive {     // 处理active状态,并且副本数大于0,则更新LastActiveTime     e.updateLastActiveTime(ctx, logger, scaledObject) } else {     // 不处理     logger.V(1).Info("ScaleTarget no change")

3.3 KEDA配置介绍

环境介绍 Kubernetes :v1.17.2 Keda:2.6.1 部署

kubectl apply -f http://github.com/kedacore/keda/releases/download/v2.4.0/keda-2.6.1.yaml

组件介绍 keda-operator 负责创建维护hpa对象资源,同时激活和停止hpa伸缩。在无事件的时候将副本数降低为0(如果未设置minReplicaCount的话) keda-metrics-apiserver 实现了hpa中external metrics,根据事件源配置返回计算结果。 创建ScaledObject资源

``` apiVersion: keda.sh/v1alpha1

由 Keda 运营商提供的自定义 CRD

kind: ScaledObject metadata: name: nginx-scaledobject namespace: hpa-tmp spec: advanced:    # HPA config    # Read about it here: http://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/   horizontalPodAutoscalerConfig:     behavior:       scaleDown:         policies:         - periodSeconds: 30           type: Pods           value: 1         stabilizationWindowSeconds: 30       scaleUp:         policies:         - periodSeconds: 10           type: Pods           value: 1         stabilizationWindowSeconds: 0  # 在将部署缩放回 1 之前,最后一个触发报告活动后等待的时间 cooldownPeriod: 30  # keda 将扩展到的最大副本数 maxReplicaCount: 3  # keda 将扩展到的最小副本数 minReplicaCount: 1  # 查询 Prometheus 的时间间隔 pollingInterval: 15 scaleTargetRef:  # 针对哪个deployment   name: nginx-hpa triggers: - type: cpu   metadata:     type: Utilization     value: "30" ```

deployment 复用上文 nginx-hpa,创建好 ScaledObject 后会自动创建出hpa资源 image.png 更多type 类型配置介绍通过多个指标控制更精准的控制扩缩容动作,支持 Prometheus 指标、Metrics-server 指标、计划任务指标等。

triggers:   - metadata:      # Prometheus指标支持       metricName: istio_request_qps_1m       query: istio_request_qps_1m{app_name="test-hpa"}       serverAddress: http://prometheus-service.monitoring.svc.cluster.local:9090       threshold: "100"     name: istio-qps-trigger       type: prometheus   - metadata:      # metrics-server 指标支持       type: AverageValue       value: "10"     name: cpu-trigger     type: cpu   - metadata:      # 计划任务指标支持       desiredReplicas: "3"       end: 45 * * * *       start: 40 * * * *       timezone: Asia/Shanghai     type: cron

3.4 社区介绍

KEDA社区了解和活跃性 2019 年 5 月 Microsoft 和 Red Hat 共同发起了 KEDA 项目,该项目旨在简化应用程序的自动伸缩。用户只需创建 ScaledObject或 ScaledJob定义想要伸缩的对象和要使用的触发器,支持缩减到零 (scale-to-zero)。 KEDA 在 2020 年 3 月成为 CNCF 沙盒项目。自从成为沙盒项目以来,KEDA 用户数量大幅增长,这些用户主要来自 Alibaba、CastAI、KPMG、Meltwater、Microsoft 等公司。目前 KEDA 已经从 CNCF 的沙箱项目升级为孵化项目。 KEDA 的开源代码仓库地址为 http://github.com/kedacore/keda,目前已获得 5.6k 多颗 Star,超过 100 位贡献者。KEDA 主要由 Microsoft、Red Hat 和 Codit这三个组织人员进行维护

4.使用场景和后续深究切入点后续后续

1、大数据应用定时抽取任务应用,夜间高峰时可以扩容,白天缩减,可以结合CronHPA进行定时扩缩容; 2、事件驱动架构应用可以尝试在平时未有使用量的时候将应用实例数置为0; 3、一些转换类服务扩缩容时并行度控制。使用 Kubernetes 生命周期管理钩子SIGTERM,可以利用它来延迟终止或者可以使用 KEDA 的 ScaledJob对象来创建和扩展它,而不是针对事件创建部署。这样,它可以控制并行度,并且这些转换作业可以运行直到完成; 4、不同场景下的扩容速度控制; 5、根据流量削峰填谷,自动化的成本管控。

参考资料

HPA相关:

http://kubernetes.io/zh-cn/docs/tasks/run-application/horizontal-pod-autoscale/ http://kubernetes.io/zh-cn/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/ http://kubernetes.io/zh-cn/docs/tasks/run-application/horizontal-pod-autoscale/#algorithm-details http://help.aliyun.com/document_detail/86554.html http://help.aliyun.com/document_detail/151557.html?spm=a2c4g.11186623.0.0.5e86135f1tiy60 http://cloud.tencent.com/developer/article/1839418 http://github.com/kubernetes/kubernetes/blob/2dba4034f8424574c42cebfce91664c4b7e08c61/pkg/controller/podautoscaler/replica_calculator.go

KEDA相关: http://github.com/kedacore/keda http://keda.sh/ 官网

推荐阅读

从线上死锁分析到 Next-Key Lock 理解(2)

Spring Boot 优雅停机

浅谈大数据指标体系建设流程

Spock单元测试框架简介及实践

算法应用之搜推系统的简介

招贤纳士

政采云技术团队(Zero),一个富有激情、创造力和执行力的团队,Base 在风景如画的杭州。团队现有 500 多名研发小伙伴,既有来自阿里、华为、网易的“老”兵,也有来自浙大、中科大、杭电等校的新人。团队在日常业务开发之外,还分别在云原生、区块链、人工智能、低代码平台、中间件、大数据、物料体系、工程平台、性能体验、可视化等领域进行技术探索和实践,推动并落地了一系列的内部技术产品,持续探索技术的新边界。此外,团队还纷纷投身社区建设,目前已经是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等众多优秀开源社区的贡献者。如果你想改变一直被事折腾,希望开始折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊……如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的技术团队的成长过程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 [email protected]

微信公众号

文章同步发布,政采云技术团队公众号,欢迎关注

政采云技术团队.png