kubernetes的HPA模块深度讲解

栏目: 编程工具 · 发布时间: 7年前

内容简介:对于kubernetes基础性的知识,目前有很多资料,于是不会重复展开,想做一个对每个模块都深入讲解的系列,包括基础使用,源码解读,和实践中遇到的问题等,所以篇幅很比较长。(1) kubernetes版本:v1.9.2(2) 适合对kubernetes基础有一定了解的人群

对于kubernetes基础性的知识,目前有很多资料,于是不会重复展开,想做一个对每个模块都深入讲解的系列,包括基础使用,源码解读,和实践中遇到的问题等,所以篇幅很比较长。

二,HPA模块

1,相关说明

(1) kubernetes版本:v1.9.2

(2) 适合对kubernetes基础有一定了解的人群

2,基本概念和使用

(1) 概念

HPA是kubernetes中自动水平扩展模块,基于用户设定和获取到的指标(CPU,Memory,自定义metrics),对Pod进行伸缩(不是直接操作pod)。HPA controller属于Controller Manager的一个controller。

(2) 基本使用

我们可以在pkg/apis/autoscaling 下可以看到,目前是有两个版本:v1(仅支持CPU指标),v2beta1(支持CPU和Memory和自定义指标)。下面看一下,一个比较全面的hpa的写法。

kind: HorizontalPodAutoscaler
apiVersion: autoscaling/v2beta1
metadata:
  name: example-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: example-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      targetAverageUtilization: 50

  - type: Resource
    resource:
      name: memory
      targetAverageUtilization: 50

  - type: Pods
    pods:
      metricName: receive_bytes_total
      targetAverageValue: 100

  - type: Object
    object:
      target:
        kind: endpoints
        name: example-app
      metricName: request_total
      targetValue: 500m

复制代码

3,源码分析

kubernetes中的代码都是有一定的“套路”(后面会专门写一篇来深入分析这种“套路”),我们首先从api入手,再到controller

(1) api

这是一个标准的kubernetes的api写法(可使用官方 工具 生成),register.go中添加了三个type:HorizontalPodAutoscaler/HorizontalPodAutoscalerList/Scale。接下来看types.go中关于这几个的定义。对应上面的yaml定义来看。

// 1,HorizontalPodAutoscaler
type HorizontalPodAutoscaler struct {
    ......
	Spec HorizontalPodAutoscalerSpec
	Status HorizontalPodAutoscalerStatus
	......
}
// 用户设置的值
type HorizontalPodAutoscalerSpec struct {
	MinReplicas *int32  //设置的最小的replicas
	MaxReplicas int32   //设置的最大的replicas
	Metrics []MetricSpec
}
// hpa的目前的状态
type HorizontalPodAutoscalerStatus struct {
	ObservedGeneration *int64   //观察的最近的generaction
	LastScaleTime *metav1.Time  //上次伸缩的时间
	CurrentReplicas int32       //目前的replicas数量
	DesiredReplicas int32       //期望的replicas数量
	CurrentMetrics []MetricStatus   //最近一次观察到的metrics数据
	Conditions []HorizontalPodAutoscalerCondition   //在某个特定点的hpa状态
}
// Metrics定义
type MetricSpec struct {
	Type MetricSourceType   //metrics type
	Object *ObjectMetricSource  // Object类型的metrics定义
	Pods *PodsMetricSource  // pods类型的metrics定义
	Resource *ResourceMetricSource // Resource类型的metrics定义
}

// 2,Scale 是resources的一次scaling请求,最后hpa都是要使用这个来实现
type Scale struct {
	Spec ScaleSpec         // 期望到达的状态
	Status ScaleStatus     // 目前的状态
}

复制代码

(2) controller

api定义完后,需要有一个controller来保证系统的状态能符合我们定义的要求,这时候就需要hpa controller了,hpa controller通过从apiserver中获取各个指标的值,根据特定的伸缩算法,来维持在预期的状态。

上面说过,hpa controller属于controller manager,于是我们去cmd/kube-controller-manager下,经过一路跟踪,可以看到hpa controller的启动逻辑在options/autoscaling.go中

func startHPAController(ctx ControllerContext) (bool, error) {
    // 需要包含v1版本
	if !ctx.AvailableResources[schema.GroupVersionResource{Group: "autoscaling", Version: "v1", Resource: "horizontalpodautoscalers"}] {
		return false, nil
	}
    // 如果要使用自定义metrics,需要开启该选项
	if ctx.Options.HorizontalPodAutoscalerUseRESTClients {
		return startHPAControllerWithRESTClient(ctx)
	}
    // 从Heapster拉取数据
	return startHPAControllerWithLegacyClient(ctx)
}

func startHPAControllerWithMetricsClient(ctx ControllerContext, metricsClient metrics.MetricsClient) (bool, error) {
    .......
    // 核心参数,根据metrics计算相应的replicas
    replicaCalc := podautoscaler.NewReplicaCalculator(
		metricsClient,
		hpaClient.CoreV1(),
		ctx.Options.HorizontalPodAutoscalerTolerance,
	)
	// 新建HorizontalController
	go podautoscaler.NewHorizontalController(
		hpaClientGoClient.CoreV1(),
		scaleClient, // scale相关客户端,实现最终的pod伸缩
		hpaClient.AutoscalingV1(),
		restMapper,
		replicaCalc, // 副本计算器
		ctx.InformerFactory.Autoscaling().V1().HorizontalPodAutoscalers(), //infomer
		ctx.Options.HorizontalPodAutoscalerSyncPeriod.Duration, // hpa获取数据的间隔
		ctx.Options.HorizontalPodAutoscalerUpscaleForbiddenWindow.Duration, // hpa扩容最低间隔
		ctx.Options.HorizontalPodAutoscalerDownscaleForbiddenWindow.Duration, // hpa缩容最低间隔
	).Run(ctx.Stop)
	return true, nil
}

// 接下来看HorizontalController的定义
type HorizontalController struct {
	scaleNamespacer scaleclient.ScalesGetter   // 负责scale的get和update
	hpaNamespacer   autoscalingclient.HorizontalPodAutoscalersGetter // 负责HorizontalPodAutoscaler的Create, Update, UpdateStatus, Delete, Get, List, Watch等
	mapper          apimeta.RESTMapper 
	replicaCalc   *ReplicaCalculator // 负责根据指标计算replicas
	eventRecorder record.EventRecorder  //event记录
	upscaleForbiddenWindow   time.Duration
	downscaleForbiddenWindow time.Duration
    // 从informer中list/get数据
	hpaLister       autoscalinglisters.HorizontalPodAutoscalerLister
	hpaListerSynced cache.InformerSynced

	queue workqueue.RateLimitingInterface
}
复制代码

开始Run后,就是controller开发的那一套流程了,设计到相关的informer,workerqueue就不展开了,最关键的是下面的reconcileAutoscaler,其实就是通过一系列算法调节当前副本数,期望副本数,边界(最大最小)副本数三者的关系。(接下来分析可能比较长,只截取部分关键代码,注意看注释)

func (a *HorizontalController) reconcileAutoscaler(hpav1Shared *autoscalingv1.HorizontalPodAutoscaler) error {

    // 通过namespace和name获取对应的scale
	scale, targetGR, err := a.scaleForResourceMappings(hpa.Namespace, hpa.Spec.ScaleTargetRef.Name, mappings)
	// 获取当前副本
	currentReplicas := scale.Status.Replicas

	rescale := true
    // 副本为0,则不进行scale操作
	if scale.Spec.Replicas == 0 {
		desiredReplicas = 0
		rescale = false
	// 当前副本大于期望的最大副本数量,不进行操作
	} else if currentReplicas > hpa.Spec.MaxReplicas {
		rescaleReason = "Current number of replicas above Spec.MaxReplicas"
		desiredReplicas = hpa.Spec.MaxReplicas
    // 当前副本数小于期望的最小值
	} else if hpa.Spec.MinReplicas != nil && currentReplicas < *hpa.Spec.MinReplicas {
		rescaleReason = "Current number of replicas below Spec.MinReplicas"
		desiredReplicas = *hpa.Spec.MinReplicas
	} 
	// 当前副本为0也不进行操作
	else if currentReplicas == 0 {
		rescaleReason = "Current number of replicas must be greater than 0"
		desiredReplicas = 1
	} 
	// 当前副本数量处于设置的Min和Max之间才进行操作
	else { 
	    // 根据metrics指标计算对应的副本数
		metricDesiredReplicas, metricName, metricStatuses, metricTimestamp, err = a.computeReplicasForMetrics(hpa, scale, hpa.Spec.Metrics)
		rescaleMetric := ""
		// 这里是防止扩容过快,限制了最大只能扩当前实例的两倍
		desiredReplicas = a.normalizeDesiredReplicas(hpa, currentReplicas, desiredReplicas)
        // 限制扩容和缩容间隔,默认是两次缩容的间隔不得小于5min,两次扩容的间隔不得小于3min
		rescale = a.shouldScale(hpa, currentReplicas, desiredReplicas, timestamp)
	}

    // 如果上面的限制条件都通过,则进行扩容,扩容注意通过scale实现
	if rescale {
		scale.Spec.Replicas = desiredReplicas
		_, err = a.scaleNamespacer.Scales(hpa.Namespace).Update(targetGR, scale)
	} else {
		desiredReplicas = currentReplicas
	}
}


复制代码

(3) 整套流程如下

kubernetes的HPA模块深度讲解

4,HPA实战经验

虽然说hpa能根据指标对pod进行弹性伸缩,达到根据使用量扩展机器的功能,但是,在实际运用中,我发现了以下的问题,希望能给要使用该模块的人带来一些启发。

(1) 问题详情

我们遇到了这样的一个业务场景:在某个时间段会突然流量剧增十倍,此时由于之前是处于低流量状态,replicas一直处于较低值,那么此时扩容由于扩容算法的限制(最多为2倍),此时扩容的数量是不足够的。然后,同样由于扩容算法的限制,两次扩容周期默认为不低于三分钟,那么将无法在短期内到达一个理想的副本数。此时从监控上看pod的数量图如下:

kubernetes的HPA模块深度讲解

那么这样将会造成很大的问题,无法及时处理这种实时性高的业务场景。同时,我们还遇到了这样的业务情况,在一次大量扩容后,流量剧减,pod数量降到了一个极低值,但是由于出现业务流量的抖动,在接下来很短时间内,再一次出现大流量,此时pod数量无法处理如此高的流量,影响业务的SLA等。

(2) 问题解决思路

1,利用多指标 如果只使用单一指标,例如CPU,整个hpa将严重依赖于这项指标,该指标的准确性等直接影响整个hpa。在这里,我们使用CRD进行了多指标的开发,结合某个业务的具体场景,开发合适的指标,然后结合着CPU等指标一起使用。

2,调整默认参数 默认的扩容和缩容周期不一定是最合适你们的业务的,所以可以根据业务自身的情况进行调整。

3,自行开发hpa controller 这里还有一个思路是修改hpa controller,但是这样将会不利于以后的升级。所以可以自行开发hpa controller,自行定义最使用你们业务的扩容缩容算法即可。但是这样的开发成本就稍微有点大。

三,总结

(1) TODO

接下来将会调研新版本中hpa的变化,看看是否有什么好玩的新特性,再回来更新

(2) 如果是我,会如何来设计

提出自己的愚蠢的思路:如果我是hpa这块的负责人,那么我将会将扩容和缩容算法这块写成是可扩展的,用户可自定义的,这样将会大大方便使用。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Transcending CSS

Transcending CSS

Andy Clarke、Molly E. Holzschlag / New Riders / November 15, 2006 / $49.99

As the Web evolves to incorporate new standards and the latest browsers offer new possibilities for creative design, the art of creating Web sites is also changing. Few Web designers are experienced p......一起来看看 《Transcending CSS》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

URL 编码/解码
URL 编码/解码

URL 编码/解码