总篇135篇 2022年第10篇前言应用的资源使用率通常都有高峰和低谷的时候,如何削峰填谷,提高集群的整体资源利用率,让应用中的Pod个数自动调整呢?这就有赖于Horizontal Pod Autoscaling了,顾名思义,使Pod水平自动缩放。简介HPA全称是 Horizontal Pod Autoscaler,也就是对k8s的Pod进行自动水平扩缩容机制(scale),也是k8s里使用需求最广泛的一种Autoscaler机制,在开始详细介绍HPA之前,先简单梳理下k8s autoscale的整个大背景。其本质是解决资源与业务负载之间供需平衡的问题,随着业务需求和部署规模的增大,k8s集群就要相应扩容计算资源,集群扩容的最直接的办法是新增资源,一般单机器很难垂直扩展(k8s node也不支持),所以一般都是直接增加节点。但是随着机器的不断增加成本也不断加大,而实际上大量服务大部分时间负载很低导致机器的整体使用率很低,一方面业务为了应对每日随机流量高峰会把副本数尽量扩得很高,另一方面业务方并不能准确评估服务实际需要的CPU等资源,也出现大量浪费。工作机制核心的工作机制简单来说就是,先为应用设定一个期望的资源使用率或使用量,然后同过监控指标实时获取当前的资源使用率或使用量,然后计算出期望的副本数,对应用进行扩容或者缩容,监控指标主要来自各种metrics。首先可以看一下HPA在k8s中的结构,这里找了一个k8s官方给出的HPA例子,我在关键字段上给出一些注释方便理解。apiVersion: autoscaling/v2beta2kind: HorizontalPodAutoscalermetadata: name: php-apache namespace: defaultspec: # HPA的伸缩对象描述,HPA会动态修改该对象的pod数量 scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: php-apache # HPA的最小pod数量和最大pod数量 minReplicas: 1 maxReplicas: 10 # 监控的指标数组,支持多种类型的指标共存 metrics: # 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 # Resource类型的指标 - type: Resource resource: name: cpu # Utilization类型的目标值,Resource类型的指标只支持Utilization和AverageValue类型的目标值 target: type: Utilization averageUtilization: 50 # Pods类型的指标 - type: Pods pods: metric: name: packets-per-second # AverageValue类型的目标值,Pods指标类型下只支持AverageValue类型的目标值 target: type: AverageValue averageValue: 1k # External类型的指标 - type: External external: metric: name: queue_messages_ready # 该字段与第三方的指标标签相关联,(此处官方文档有问题,正确的写法如下) selector: matchLabels: env: "stage" app: "myapp" # External指标类型下只支持Value和AverageValue类型的目标值 target: type: AverageValueaverageValue:?30Metrics的分类Resource支持k8s里Pod的所有系统资源(包括cpu、memory等),但是一般只会用cpu,memory因为不太敏感而且跟语言相关:大多数语言都有内存池及内置GC机制导致进程内存监控不准确。Pods类型的metrics表示cpu,memory等系统资源之外且是由Pod自身提供的自定义metrics数据,比如用户可以在web服务的pod里提供一个promesheus metrics的自定义接口,里面暴露了本pod的实时QPS监控指标,这种情况下就应该在HPA里直接使用Pods类型的metrics。Object类型的metrics表示监控指标不是由Pod本身的服务提供,但是可以通过k8s的其他资源Object提供metrics查询,比如ingress等,一般Object是需要汇聚关联的Deployment下的所有的pods总的指标。External类型的metrics也属于自定义指标,与Pods和Object不同的是,其监控指标的来源跟k8s本身无关,metrics的数据完全取自外部的系统。核心代码基于 kubernetes 1.13 版本解读部分HPA controller核心代码,版本不同行数变化,但基本逻辑变化不大。篇幅限制,将会省略部分非核心逻辑代码, 使核心逻辑更清晰。1.Run函数位于 /pkg/controller/podautoscaler/horizontal .go:152func (a *HorizontalController) Run(stopCh reconcileAutoscaler?->computeReplicasForMetrics?->ScaleInterface?Update简单来说就是先从Informer中拿到 key 对应的HorizontalPodAutoscaler资源实例;然后通过HorizontalPodAutoscaler实例中的信息,检查目标资源的Pod 负载以及当前的副本数,得到期望的 Pod 副本数;最终通过 Scale API 来调整 Pod 的副本数。最后会将调整的原因、计算的结果等信息写入HorizontalPodAutoscaler实例的 condition 中。reconcileKeyfunc (a *HorizontalController) reconcileKey(key string) (deleted bool, err error) { ..... hpa, err := a.hpaLister.HorizontalPodAutoscalers(namespace).Get(name) ..... return false, a.reconcileAutoscaler(hpa, key)}reconcileAutoscalerfunc (a *HorizontalController) reconcileAutoscaler(hpav1Shared *autoscalingv1.HorizontalPodAutoscaler, key string) error { //1.转换hpav1到autoscalingv2.HorizontalPodAutoscaler版本,便于后面的逻辑处理; hpaRaw, err := unsafeConvertToVersionVia(hpav1, autoscalingv2.SchemeGroupVersion) ...... //2. 获取scale,包含当前的副本数和定义副本数等信息; scale, targetGR, err := a.scaleForResourceMappings(hpa.Namespace, hpa.Spec.ScaleTargetRef.Name, mappings) ..... rescale := true //3.对比scale中定于副本及当前副本和hpa中的MaxReplicas和MinReplicas if scale.Spec.Replicas == 0 { //如果scale定义副本数等于0,则此次不做scale操作; desiredReplicas = 0 rescale = false } else if currentReplicas > hpa.Spec.MaxReplicas { //如果scale定义副本数大于hpa定义的MaxReplicas,将期望副本数设定为MaxReplicas desiredReplicas = hpa.Spec.MaxReplicas } else if hpa.Spec.MinReplicas != nil & currentReplicas desiredReplicas { desiredReplicas = metricDesiredReplicas timestamp = metricTimestamp rescaleMetric = metricName } if desiredReplicas > currentReplicas { rescaleReason = fmt.Sprintf("%s above target", rescaleMetric) } if desiredReplicas