3.12 Pod的扩缩容

在实际生产系统中,我们经常会遇到某个服务需要扩容的场景,也可能会遇到由于资源紧张或者工作负载降低而需要减少服务实例数量的场景。此时可以利用Deployment/RC的Scale机制来完成这些工作。

Kubernetes对Pod的扩缩容操作提供了手动和自动两种模式,手动模式通过运行kubectl scale命令或通过RESTful API对一个Deployment/RC进行Pod副本数量的设置,即可一键完成。自动模式则需要用户根据某个性能指标或者自定义业务指标,并指定Pod副本数量的范围,系统将自动在这个范围内根据性能指标的变化进行调整。

3.12.1 手动扩缩容机制

以Deployment nginx为例:

已运行的Pod副本数量为3个:

通过kubectl scale命令可以将Pod副本数量从初始的3个更新为5个:

将--replicas的值设置为比当前Pod副本数量更小的数字,系统将会“杀掉”一些运行中的Pod,以实现应用集群缩容:

3.12.2 自动扩缩容机制

Kubernetes从1.1版本开始,新增了名为Horizontal Pod Autoscaler(HPA)的控制器,用于实现基于CPU使用率进行自动Pod扩缩容的功能。HPA控制器基于Master的kube-controller-manager服务启动参数--horizontal-pod-autoscaler-sync-period定义的探测周期(默认值为15s),周期性地监测目标Pod的资源性能指标,并与HPA资源对象中的扩缩容条件进行对比,在满足条件时对Pod副本数量进行调整。

Kubernetes在早期版本中,只能基于Pod的CPU使用率进行自动扩缩容操作,关于CPU使用率的数据最早来源于组件,从1.11版本开始,Kubernetes正式弃用Heapster并全面转向基于Metrics Server完成数据采集。Metrics Server将采集到的Pod性能指标数据通过聚合API(Aggregated API)如metrics.k8s.io、custom.metrics.k8s.io和external.metrics.k8s.io提供给HPA控制器进行查询。关于聚合API和API聚合器(API Aggregator)的概念详见9.4.2节的说明。另外,Kubernetes从1.6版本开始,引入了基于应用自定义性能指标的HPA机制,并在1.9版本之后逐步成熟。

本节对Kubernetes的HPA的原理和实践进行详细说明。

1.HPA的工作原理

Kubernetes中的某个Metrics Server持续采集所有Pod副本的指标数据。HPA控制器通过Metrics Server的API获取这些数据,基于用户定义的扩缩容规则进行计算,得到目标Pod的副本数量。当目标Pod副本数量与当前副本数量不同时,HPA控制器就向Pod的副本控制器(Deployment、RC或ReplicaSet)发起scale操作,调整Pod的副本数量,完成扩缩容操作。图3.12展示了HPA体系中的关键组件和工作流程。

接下来首先对HPA能够管理的指标类型、扩缩容算法、HPA对象的配置进行详细说明,然后通过一个完整的示例对如何搭建和使用基于自定义指标的HPA体系进行说明。

图3.12 HPA体系中的关键组件和工作流程

2. 指标的类型

Master的kube-controller-manager服务持续监测目标Pod的某种性能指标,以计算是否需要调整副本数量。目前Kubernetes支持的指标类型如下。

◎ Pod资源使用率:Pod级别的性能指标,通常是一个比率值,例如CPU使用率。

◎ Pod自定义指标:Pod级别的性能指标,通常是一个数值,例如接收的请求数量。

◎ Object自定义指标或外部自定义指标:通常是一个数值,需要容器应用以某种方式提供,例如通过HTTP URL“/metrics”提供,或者使用外部服务提供的指标采集URL。

Kubernetes HPA当前有以下两个版本。

◎ autoscaling/v1版本仅支持基于CPU使用率指标的自动扩缩容。

◎ autoscaling/v2版本则支持基于内存使用率指标、自定义指标及外部指标的自动扩缩容,并且进一步扩展以支持多指标缩放能力,当定义了多个指标时,HPA会跟据每个指标进行计算,其中缩放幅度最大的指标会被采纳。

3. 扩缩容算法详解

Autoscaler控制器从聚合API获取到Pod性能指标数据之后,基于下面的算法计算出目标Pod副本数量,与当前运行的Pod副本数量进行对比,决定是否需要进行扩缩容操作:

即当前副本数×(当前指标值/期望的指标值),将结果向上取整。

以CPU请求数量为例,如果用户设置的期望指标值为100m,当前实际使用的指标值为200m,则计算得到期望的Pod副本数量应为两个(200/100=2)。如果当前实际使用的指标值为50m,计算结果为0.5,则向上取整,值为1,得到目标Pod副本数量应为1个。

当计算结果与1非常接近时,可以设置一个容忍度让系统不做扩缩容操作。容忍度通过kube-controller-manager服务的启动参数--horizontal-pod-autoscaler-tolerance进行设置,默认值为0.1(即10%),表示基于上述算法得到的结果在[-10%,+10%]区间内,即[0.9,1.1]区间,控制器都不会进行扩缩容操作。

也可以将期望指标值(desiredMetricValue)设置为指标的平均值类型,例如targetAverageValue或targetAverageUtilization,此时当前指标值(currentMetricValue)的算法为所有Pod副本当前指标值的总和除以Pod副本数量得到的平均值。

此外,存在几种Pod异常的情况,如下所述。

◎ Pod正在被删除(设置了删除时间戳):将不会计入目标Pod副本数量。

◎ Pod的当前指标值无法获得:本次探测不会将这个Pod纳入目标Pod副本数量,后续的探测会被重新纳入计算范围。

◎ 如果指标类型是CPU使用率,则对于正在启动但是还未达到Ready状态的Pod,也暂时不会纳入目标副本数量范围。可以通过kube-controller-manager服务的启动参数--horizontal-pod-autoscaler-initial-readiness-delay设置首次探测Pod是否Ready的延时时间,默认值为30s。另一个启动参数--horizontal-pod-autoscaler-cpu-initialization-period设置首次采集Pod的CPU使用率的延时时间。

在计算“当前指标值/期望的指标值”(currentMetricValue/desiredMetricValue)时将不会包括上述这些异常Pod。

当存在缺失指标的Pod时,系统将更保守地重新计算平均值。系统会假设这些Pod在需要缩容(Scale Down)时消耗了期望指标值的100%,在需要扩容(Scale Up)时消耗了期望指标值的0%,这样可以抑制潜在的扩缩容操作。

此外,如果存在未达到Ready状态的Pod,并且系统原本会在不考虑缺失指标或NotReady的Pod情况下进行扩展,则系统仍然会保守地假设这些Pod消耗期望指标值的0%,从而进一步抑制扩容操作。

如果在HorizontalPodAutoscaler中设置了多个指标,系统就会对每个指标都执行上面的算法,在全部结果中以期望副本数量的最大值为最终结果。如果这些指标中的任意一个都无法被转换为期望的副本数量(例如无法获取指标的值),系统就会跳过扩缩容操作。

使用HPA特性时,可能因为指标动态的变化造成Pod副本数量频繁变动,这也被称为“抖动”。抖动会影响到业务系统的稳定性,Kubernetes 1.12之前的版本提供了一些系统参数来缓解这个问题,不过这些参数难以理解和设置。Kubernetes 1.12版本增加了全新的参数horizontal-pod-autoscaler-downscale-stabilization(kube-controller-manager的参数)来解决这个问题,它表示HPA扩缩容过程中的冷却时间,即从上次缩容执行结束后,需要经过最少多长时间才可以再次执行缩容动作。当前的默认时间是 5min,此配置可以让系统更为平滑地进行缩容操作,从而消除短时间内指标值快速波动产生的影响。对该参数的调整需要根据当前生产环境的实际情况进行并观察结果,若时间过短,则仍然可能抖动强烈,若时间过长,则可能导致HPA失效。

最后,在HPA控制器执行扩缩容操作之前,系统会记录扩缩容建议信息(Scale Recommendation)。控制器会在操作时间窗口(时间范围可以配置)中考虑所有的建议信息,并从中选择得分最高的建议。

4.HorizontalPodAutoscaler配置详解

Kubernetes将HorizontalPodAutoscaler资源对象提供给用户来定义扩缩容的规则,HorizontalPodAutoscaler资源对象处于Kubernetes的API组“autoscaling”中。下面对HorizontalPodAutoscaler的配置和用法进行说明。

(1)基于autoscaling/v1版本的HorizontalPodAutoscaler配置:

主要参数如下。

◎ scaleTargetRef:目标作用对象,可以是Deployment、ReplicationController或ReplicaSet。

◎ targetCPUUtilizationPercentage:期望每个Pod的CPU使用率都为50%,该使用率基于Pod设置的CPU Request值进行计算,例如该值为200m,那么系统将维持Pod的实际CPU使用值为100m。

◎ minReplicas和maxReplicas:Pod副本数量的最小值和最大值,系统将在这个范围内进行自动扩缩容操作,并维持每个Pod的CPU使用率为50%。

为了使用autoscaling/v1版本的HorizontalPodAutoscaler,需要预先安装Metrics Server,用于采集Pod的CPU使用率。关于Metrics Server的说明请参考9.4节的介绍,本节主要对基于自定义指标进行自动扩缩容的设置进行说明。

(2)基于autoscaling/v2beta2版本的HorizontalPodAutoscaler配置:

主要参数如下。

◎ scaleTargetRef:目标作用对象,可以是Deployment、ReplicationController或ReplicaSet。

◎ minReplicas和maxReplicas:Pod副本数量的最小值和最大值,系统将在这个范围内进行自动扩缩容操作,并维持每个Pod的CPU使用率为50%。

◎ metrics:目标指标值。在metrics中通过参数type定义指标的类型;通过参数target定义相应的指标目标值,系统将在指标数据达到目标值时(考虑容忍度的区间,见前面算法部分的说明)触发扩缩容操作。

可以将metrics中的type(指标类型)设置为以下四种,如下所述。

◎ Resource:指的是当前伸缩对象下Pod的CPU和Memory指标,只支持Utilization和AverageValue类型的目标值。对于CPU使用率,在target参数中设置averageUtilization定义目标平均CPU使用率。对于内存资源,在target参数中设置AverageValue定义目标平均内存使用值。

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

◎ Object:Kubernetes内部对象的指标,数据需要由第三方Adapter提供,只支持Value和AverageValue类型的目标值。

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

其中,AverageValue是根据Pod副本数量计算的平均值指标。Resource类型的指标来自Metrics Server自身,即从它所提供的aggregated APIs的metrics.k8s.io接口获取数据,Pod类型和Object类型都属于自定义指标类型,从Metrics Server的custom.metrics.k8s.io接口获取数据,但需要配套Metrics Server的第三方Adapter来提供数据,这些数据一般都属于Kubernetes集群自身的参数。而External属于外部指标,基本与Kuberntes无关,例如用户使用了公有云服务商提供的消息服务或外部负载均衡器,希望基于这些外部服务的性能指标(如消息服务的队列长度、负载均衡器的QPS)对自己部署在Kubernetes中的服务进行自动扩缩容操作,External指标从Metrics Server的external.metrics.k8s.io接口获取数据。

而具体的指标数据可以通过API“custom.metrics.k8s.io”进行查询,要求预先启动自定义Metrics Server服务。

下面是一个类型为Pods的Metrics示例:

其中,设置Pod的指标名为packets-per-second,在目标指标平均值为1000时触发扩缩容操作。

下面是几个类型为Object的Metrics示例。

例1,设置指标的名称为requests-per-second,其值来源于Ingress“main-route”,将目标值(value)设置为2000,即在Ingress的每秒请求数量达到2000个时触发扩缩容操作:

例2,设置指标的名称为http_requests,并且该资源对象具有标签verb=GET,在指标平均值达到500时触发扩缩容操作:

我们在使用autoscaling/v2beta1版本时,还可以在同一个HorizontalPodAutoscaler资源对象中定义多个类型的指标,系统将针对每种类型的指标都计算Pod副本的目标数量,以最大值为准进行扩缩容操作。下面是一个具体的示例:

下面是一个类型为External的Metrics示例(例3)。

例3,设置指标的名称为queue_messages_ready,具有queue=worker_tasks标签,在目标指标平均值为30时触发自动扩缩容操作:

在使用外部服务的指标时,要安装、部署能够对接到Kubernetes HPA模型的监控系统,并且完全了解监控系统采集这些指标的机制,后续的自动扩缩容操作才能完成。

Kubernetes推荐尽量使用类型为Object的HPA配置方式,这可以通过使用Operator模式,将外部指标通过CRD(自定义资源)定义为API资源对象来实现。

5. 基于自定义指标的HPA实践

下面通过一个完整的示例,对如何搭建和使用基于自定义指标的HPA体系进行说明。

基于自定义指标进行自动扩缩容时,需要预先部署自定义Metrics Server,目前可以使用基于Prometheus、Microsoft Azure、Datadog Cluster等系统的Adapter实现自定义Metrics Server,未来还将提供基于Google Stackdriver的实现自定义Metrics Server。本节基于Prometheus监控系统对HPA的基础组件部署和HPA配置进行详细说明。

基于Prometheus的HPA架构如图3.13所示。

图3.13 基于Prometheus的HPA架构

关键组件包括如下。

◎ Prometheus:定期采集各Pod的性能指标数据。

◎ Custom Metrics Server:自定义Metrics Server,用Prometheus Adapter进行具体实现。它从Prometheus服务采集性能指标数据,通过Kubernetes的Metrics Aggregation层将自定义指标API注册到Master的API Server中,以/apis/custom.metrics.k8s.io路径提供指标数据。

◎ HPA Controller:Kubernetes的HPA控制器,基于用户定义的HorizontalPodAutoscaler进行自动扩缩容操作。

接下来对整个系统的部署过程进行说明。

(1)在Master的API Server中启动Aggregation层,通过设置kube-apiserver服务的下列启动参数进行启动。

◎ --requestheader-client-ca-file=/etc/kubernetes/ssl_keys/ca.crt:客户端CA证书。

◎ --requestheader-allowed-names=:允许访问的客户端common names列表,通过header中由--requestheader-username-headers参数指定的字段获取。客户端common names的名称需要在client-ca-file中进行配置,将其设置为空值时,表示任意客户端都可以访问。

◎ --requestheader-extra-headers-prefix=X-Remote-Extra-:请求头中需要检查的前缀名。

◎ --requestheader-group-headers=X-Remote-Group:请求头中需要检查的组名。

◎ --requestheader-username-headers=X-Remote-User:请求头中需要检查的用户名。

◎ --proxy-client-cert-file=/etc/kubernetes/ssl_keys/kubelet_client.crt:在请求期间验证Aggregator的客户端CA证书。

◎ --proxy-client-key-file=/etc/kubernetes/ssl_keys/kubelet_client.key:在请求期间验证Aggregator的客户端私钥。

配置kube-controller-manager服务中HPA的相关启动参数(可选配置)如下。

◎ --horizontal-pod-autoscaler-sync-period=10s:HPA控制器同步Pod副本数量的时间间隔,默认值为15s。

◎ --horizontal-pod-autoscaler-downscale-stabilization=1m0s:执行缩容操作的等待时长,默认值为5min。

◎ --horizontal-pod-autoscaler-initial-readiness-delay=30s:等待Pod达到Ready状态的时延,默认值为30min。

◎ --horizontal-pod-autoscaler-tolerance=0.1:扩缩容计算结果的容忍度,默认值为0.1,表示[-10%,+10%]。

(2)部署Prometheus,这里使用Operator模式进行部署。

首先,使用下面的YAML文件部署prometheus-operator:

这个prometheus-operator会自动创建名为monitoring.coreos.com的CRD资源。

然后,通过Operator的配置部署Prometheus服务:

确认Prometheus Operator和Prometheus服务正常运行:

(3)部署自定义Metrics Server,这里以Prometheus Adapter的实现进行部署,这里将它们部署在一个新的命名空间custom-metrics中。下面的YAML文件主要包含Namespace、ConfigMap、Deployment、Service和自定义API资源custom.metrics.k8s.io/v1beta1。

Namespace的定义如下:

ConfigMap的定义如下:

Deployment的定义如下:

Service的定义如下:

APIService的定义如下:

通过kubectl创建完成后,确认custom-metrics-server容器正常运行:

(4)部署应用程序,它会在HTTP URL“/metrics”路径提供名为http_requests_total的指标值:

部署成功之后,可以在应用的URL“/metrics”中查看指标http_requests_total的值:

(5)创建一个Prometheus的ServiceMonitor对象,用于监控应用程序提供的指标:

关键配置参数如下。

◎ Selector:设置为Pod的Label“app:sample-app”。

◎ Endpoints:设置为在Service中定义的端口名称“http”。

(6)创建一个HorizontalPodAutoscaler对象,用于为HPA控制器提供用户期望的自动扩缩容配置:

关键配置参数如下。

◎ scaleTargetRef:设置HPA的作用对象为之前部署的Deployment“sample-app”。

◎ type=Pods:设置指标类型为Pods,表示从Pod中获取指标数据。

◎ metric.name=http_requests:将指标的名称设置为“http_requests”,是自定义Metrics Server将应用程序提供的指标“http_requests_total”经过计算转换成的一个新比率值,即sum(rate(http_requests_total{namespace="xx",pod="xx"}[1m])) by pod,指过去1min内全部Pod指标http_requests_total总和的每秒平均值。

◎ target:将指标http_requests的目标值设置为500m,类型为AverageValue,表示基于全部Pod副本数据计算平均值。目标Pod的副本数量将使用公式“http_requests当前值/500m”进行计算。

◎ minReplicas和maxReplicas:将扩缩容区间设置为1~10(单位是Pod副本)。

此时可以通过查看自定义Metrics Server提供的URL“custom.metrics.k8s.io/v1beta1”查看Pod的指标是否已被成功采集,并通过聚合API进行查询:

从结果中可以看到正确的value值,说明自定义Metrics Server工作正常。

查看HorizontalPodAutoscaler的详细信息,可以看到其成功从自定义Metrics Server处获取了应用的指标数据,可以进行扩缩容操作:

(7)对应用的服务地址发起HTTP访问请求,验证HPA自动扩容机制。例如,可以使用如下脚本对应用进行压力测试:

一段时间之后,观察HorizontalPodAutoscaler和Pod数量的变化,可以看到自动扩容的过程:

发现Pod数量扩容到了10个(被maxReplicas参数限制的最大值):

停止访问应用服务,等待一段时间后,观察HorizontalPodAutoscaler和Pod数量的变化,可以看到缩容操作:

发现Pod的数量已经缩容到最小值1: