HPA - Horizontal Pod Autoscaler 的缩写,Pod 程度主动伸缩。通过对 Pod 负载的监控,来主动减少或者缩小 Pod 的正本数量。
从字面意思来看,其次要蕴含了两局部:
- 监控 Pod 的负载
- 管制 Pod 的正本数量
那具体是如何实现的呢?以下基于1.17 源码,来剖析下 HPA 如何工作。
留神:文章中的代码在源码的根底上进行了精简:删掉了正文、序列化等信息,或保留了局部外围代码,加上新的正文。
资源
HPA 的资源是HorizontalPodAutoscaler
,在v1
版本中,只反对基于 CPU 指标的计算;在v2beta2
版本中退出了基于内存和自定义指标的计算。
v1
//staging/src/k8s.io/api/autoscaling/v1/types.gotype HorizontalPodAutoscaler struct { metav1.TypeMeta metav1.ObjectMeta Spec HorizontalPodAutoscalerSpec Status HorizontalPodAutoscalerStatus }type HorizontalPodAutoscalerSpec struct { ScaleTargetRef CrossVersionObjectReference //监控的指标资源 MinReplicas *int32 //最小正本数 MaxReplicas int32 //最大正本数 TargetCPUUtilizationPercentage *int32 //触发调整的CPU 使用率}
v2
//staging/src/k8s.io/api/autoscaling/v2beta2/types.gotype HorizontalPodAutoscaler struct { metav1.TypeMeta metav1.ObjectMeta Spec HorizontalPodAutoscalerSpec Status HorizontalPodAutoscalerStatus }type HorizontalPodAutoscalerSpec struct { ScaleTargetRef CrossVersionObjectReference //监控的指标资源 MinReplicas *int32 MaxReplicas int32 Metrics []MetricSpec //新退出的自定义指标}type MetricSpec struct { Type MetricSourceType //指标源的类型:Object(基于某个对象)、Pods(基于pod 数)、Resource(基于资源应用计算,比方v1 版本中cpu)、External(基于内部的指标)。对应 MetricsClient 接口的四个办法 Object *ObjectMetricSource //对应 Object 类型的指标源 Pods *PodsMetricSource //对应 Pod 类型的指标源 Resource *ResourceMetricSource //对应 Resource 类型的指标源 External *ExternalMetricSource //对应 External 类型的指标源}type ObjectMetricSource struct { DescribedObject CrossVersionObjectReference //指标对象 Target MetricTarget //指定指标的目标值、平均值或者均匀使用率 Metric MetricIdentifier //指标标识:名字、label选择器}type PodsMetricSource struct { Metric MetricIdentifier Target MetricTarget }type ResourceMetricSource struct { Name v1.ResourceName Target MetricTarget }type ExternalMetricSource struct { Metric MetricIdentifier Target MetricTarget}type MetricTarget struct { Type MetricTargetType //类型:Utilization、Value、AverageValue Value *resource.Quantity AverageValue *resource.Quantity AverageUtilization *int32}
控制器 HorizontalController
HorizontalController
被通过 key horizontalpodautoscaling
退出到 controller manager 中。用来管制HorizontalPodAutoscaler
实例。
///cmd/kube-controller-manager/app/controllermanager.gofunc NewControllerInitializers(loopMode ControllerLoopMode) map[string]InitFunc { ... controllers["horizontalpodautoscaling"] = startHPAController ...}
获取负载指标
既然 Pod 正本数量的计算是基于 Pod 的负载状况,那边须要路径获取负载数据,这个路径就是MetricsClient
。
MetricsClient
有两种实现:REST 形式和传统(Legacy)形式,别离是restMetricsClient
和HeapsterMetricsClient
。一个是REST 实现以反对自定义的指标;一个是传统的 Heapster 指标(heapster 曾经从 1.13 版本开始被废除了)。
//cmd/kube-controller-manager/app/autoscaling.gofunc startHPAController(ctx ControllerContext) (http.Handler, bool, error) { if !ctx.AvailableResources[schema.GroupVersionResource{Group: "autoscaling", Version: "v1", Resource: "horizontalpodautoscalers"}] { return nil, false, nil } if ctx.ComponentConfig.HPAController.HorizontalPodAutoscalerUseRESTClients { // use the new-style clients if support for custom metrics is enabled return startHPAControllerWithRESTClient(ctx) } return startHPAControllerWithLegacyClient(ctx)}
控制器逻辑HorizontalController#Run()
//pkg/controller/podautoscaler/horizontal.gofunc (a *HorizontalController) Run(stopCh <-chan struct{}) { defer utilruntime.HandleCrash() defer a.queue.ShutDown() klog.Infof("Starting HPA controller") defer klog.Infof("Shutting down HPA controller") // 期待 informer 实现HorizontalPodAutoscaler相干事件的同步 if !cache.WaitForNamedCacheSync("HPA", stopCh, a.hpaListerSynced, a.podListerSynced) { return } // start a single worker (we may wish to start more in the future) //执行 worker 逻辑,直到收到退出指令 go wait.Until(a.worker, time.Second, stopCh) <-stopCh}
worker
的外围是从工作队列中获取一个 key(格局为:namespace/name),而后对 key 进行 reconcile(这个词是Kubernetes 的外围,翻译为“和谐”、“和解”。集体更喜爱“调整”,即将实例的状态调整为冀望的状态。此处,对于 hpa 的实例的每个事件,都会依照特定的逻辑调整指标实例的 Pod 的正本数量。)。
//pkg/controller/podautoscaler/horizontal.gofunc (a *HorizontalController) worker() { for a.processNextWorkItem() { } klog.Infof("horizontal pod autoscaler controller worker shutting down")}func (a *HorizontalController) processNextWorkItem() bool { key, quit := a.queue.Get() if quit { return false } defer a.queue.Done(key) deleted, err := a.reconcileKey(key.(string)) if err != nil { utilruntime.HandleError(err) } if !deleted { a.queue.AddRateLimited(key) } return true}
对 key 进行 reconcile 的调用栈:HorizontalController#reconcileKey -> HorizontalController#reconcileAutoscaler -> HorizontalController#computeReplicasForMetrics -> ScaleInterface#Update
简略来说就是先从Informer
中拿到 key 对应的HorizontalPodAutoscaler
资源实例;而后通过HorizontalPodAutoscaler
实例中的信息,查看指标资源的Pod 负载以及以后的正本数,失去冀望的 Pod 正本数;最终通过 Scale API 来调整 Pod 的正本数。最初会将调整的起因、计算的后果等信息写入HorizontalPodAutoscaler
实例的 condition 中。
计算冀望的正本数
对每个指标进行计算,都会失去倡议的正本数,而后最大的那个就是最终的冀望正本数。
//pkg/controller/podautoscaler/horizontal.gofunc (a *HorizontalController) computeReplicasForMetrics(hpa *autoscalingv2.HorizontalPodAutoscaler, scale *autoscalingv1.Scale, metricSpecs []autoscalingv2.MetricSpec) (replicas int32, metric string, statuses []autoscalingv2.MetricStatus, timestamp time.Time, err error) { ...... for i, metricSpec := range metricSpecs { replicaCountProposal, metricNameProposal, timestampProposal, condition, err := a.computeReplicasForMetric(hpa, metricSpec, specReplicas, statusReplicas, selector, &statuses[i]) if err != nil { if invalidMetricsCount <= 0 { invalidMetricCondition = condition invalidMetricError = err } invalidMetricsCount++ } if err == nil && (replicas == 0 || replicaCountProposal > replicas) { timestamp = timestampProposal replicas = replicaCountProposal metric = metricNameProposal } } ......}
#computeStatusForObjectMetric
(留神这个办法名少了个 "s")应用MetricsClient
失去指定指标的值。
这个流程的细节还能够持续深挖,但到此已够咱们了解 HPA 的实现形式了。
文章对立公布在公众号
云原生指北