前言
pod手动横向扩展:通过ReplicationController、ReplicaSet、Deployment等可伸缩资源的replicas字段,来手动实现pod中应用的横向扩展。
pod纵向扩展:通过增加pod容器的资源请求和限制(pod创建时)
pod自动横向扩展:kubernetes监控pod,检测cpu使用率或其他度量增长时自动扩容。
一、pod横向自动伸缩
Horizontal控制器来实现横向pod自动伸缩
通过创建一个HorizontalpodAutoscaler(HPA)资源来启用和创建Horizontal控制器,该控制器周期性检查pod度量,计算满足HPA资源所配置的目标数值所需的副本数量,进而调整目标资源(如Deployment、ReplicaSet、ReplicationController、StatefulSet等)的replicas字段
自动伸缩过程
分为三个步骤:
获取被伸缩资源对象所管理的所有pod度量
计算使度量数据到达(或接近)所制定目标数值所需的pod数量
更新被伸缩资源的replicas字段
获取pod度量
Autoscaler不负责采集pod度量数据,pod与节点度量数据由每个节点的kubelet上cAdvisor的agent采集;这些数据由集群级的组件Heapster聚合,HPA控制器向Heapester发起REST调用来获取所有pod度量数据。
度量数据从pod到HPA的流动
这样的数据流意味着在集群中必须运行Heapster才能实现自动伸缩。
关于Autoscaler采集度量数据方式的改变
在kubernetes 1.6版本之前,HPA直接从Heapster采集度量,在1.8版本中,如果用--horizontal-pod-autoscaler-use-rest-clients=true参数启动ControllerManager,AutoscalerIU能通过聚合版资源度量api拉取度量了。该行为从1.9版本开始讲变为默认
计算所需pod数量
Autoscaler获得他所调整的资源(Deployment、ReplicaSet、ReplicationController或StaefulSet)所管辖pod的全部度量,他便可以利用这些度量计算出所需的副本数量。他需要计算出一个合适的副本数量,以使所有副本上度量的平均值尽量接近配置的目标值。输入是一组pod度量(每个pod可能有多个),输出是一个整数(pod副本数量)
如果Autoscaler配置为单个度量,副本计算:所有pod度量求和除以HPA资源上配置的目标值,向上取整。
如果Autoscaler配置为多个度量(如cpu使用率和每秒查询率【qps】的计算也并不复杂)Autoscaler单独计算每个度量的副本数,然后取最大值。
如:需要4个pod打到目标cpu使用率,以及需要3个pod打到目标qps,那么Autoscaler将扩展4个pod。
更新被伸缩资源的副本数
自动伸缩操作的最后一步是更新被伸缩资源对象(比如ReplicaSet)上的副本数字段,然后让ReplicaSet控制器负责启动更多pod或者删除多余pod
Autoscaler控制器通过Scale子资源来修改伸缩资源的replicas字段。这样Autoscaler不必了解他所管理的资源的细节,而只需通过Scale子资源暴露的界面就可以完成他的工作
只要api服务器未某个可伸缩资源暴露了Scale子资源,Autoscaler即可操作该资源。目前暴露了Scale子资源的资源有:
Deployment
ReplicaSet
ReplicationController
StatefulSet
目前也只有这些对下可以附着Autoscaler
了解整个自动伸缩过程
从pod指向cAdvisor,再经过Heapster,而最终到达HPA的箭头代表度量数据的流向。
注意:每个组件从其他组件拉取数据的动作是周期性的(即cAdvisor用一个无限循环从pod中采集数据;Heapster与HPA控制器亦是如此)。这意味着度量数据的传播与响应动作的触发都需要相当一段时间,不是立即发生的。
基于CPU使用率进行自动伸缩
如果你用几个pod来提供服务,如果他们的cpu使用率达到了100%,显然他们已经扛不住压力了,要么进行纵向扩容(scale up),增加他们可用的cpu时间,要么进行横向扩容(Scale out),增加pod数。这里我们主要关注横向扩容。这样一来,平均cpu使用率就应该下降了。
因为cpu使用率通常是不稳定的,比较靠谱的做法是在cpu被压垮之前横向扩容--可能平均负载达到或超过80%的时候进行扩容。
提示:一定要把目标cpu使用率设置远远低于100%(一定不要超过90%),以预留充分空间给突发的流量洪峰。
就Autoscaler而言,只有pod的保证cpu量(cpu请求)才与确认pod的cpu使用有关。Autoscaler对比pod的实际cpu使用与他的请求。这要求你给pod设置cpu请求,不管是直接设置还是通过LimitRange对象见解设置,这样Autoscaler才能确定cpu使用率。
基于cpu使用率创建HPA
创建Deployment,确保Deployment所创建的所有pod都指定了cpu资源请求,这样才有可能实现自动伸缩。需要给Deployment的pod模版添加一个cpu资源请求
deployment.yaml文件
apiVersion:extensions/v1beta1
kind:Deployment
metadata:
name:kubia
spec:
replicas:3
teplate:
metadata:
name:kubia
labels:
app:kubia
spec:
containers:
-image:luska/kubia:v1
name:nodejs
resources:
requests:
cpu:100m
创建了Deployment之后,为了给他的pod启用横向自动伸缩,需要创建一个HorizontalpodAutoscaler(HPA)对象,并把它指向该Deployment。可以使用kubectl autoscale命令:
kubectl autoscale deployment kukbia --cpu-percent=30 --min=1 --max=5
HAP对象创建,设置pod的目标cpu使用率30%,副本最小和最大数。
Autoscaler会持续调整副本数量以使cpu使用率接近30%,但他永远不会调整到少于1个或者多于5个
提示:一定要确保自动伸缩的目标是Deployment而不是底层的ReplicaSet。这样才能确保预期的副本数在应用更新后继续保持。手动伸缩也是同样道理。
基于内存使用进行自动伸缩
基于内存的自动伸缩比基于cpu的困难很多,主要原因在于,扩容之后原有的pod需要有办法释放内存。这只能由应用完成,系统无法代劳。
系统所能做的只有杀死并重启应用,希望他能比之前少占用一些内存;但如果应用使用了跟之前一样多的内存,Autoscaler就会扩容、扩容、再扩容,直到达到HPA资源商配置的最大pod数量。显然没有人想要这样做。基于内存使用的自动伸缩在kubernetes1.8中得到支持,配置方法与基于cpu的自动伸缩完全相同。
基于其他自定义度量进行自动伸缩
基于cpu自动伸缩的HorizontalpodAutoscaler定义
...
spec:
maxReplicas:5
metrics:
-type:Resource
resource:
name:cpu
targetAverageUtilization:30
....
metric字段运行你定义多个度量供使用。如上使用了单个度量。每个条目都指定相应度量类型,如上中为一个Resource度量,可以在HPA对象中使用三种度量:
定义metric类型
使用情况会被监控的资源
资源的目标使用量
Resource度量类型
Resource类型使Autoscaler基于一个资源度量做出自动伸缩决策。在容器的资源请求中指定的那些度量即为一例。
Pods度量类型
pods类型用来引用任何其他种类(包括自定义)与pod直接相关的度量。如没面查询次数(qps),或消息队列中的消息数量(当消息队列服务运行在pod中)都属于这种度量。要配置Autoscaler使用pod的qps度量,HPA对象的metrics字段中就包含以下代码清单所示条目
...
spec:
metrics:
-type:Pods
resource:
metricName:qps
targetAverageValue:100
...
Object度量类型
Obect度量类型备用来让Autoscaler基于并非直接与pod关联的度量进行伸缩。比如说,可能希望基于另一个集群对象,比如Ingress对象,来伸缩你的pod,这度量可能是qps,可能是平均请求延迟,或完全是不相干的其他东西。
与之前不同的是,使用Obejct度量类型,Autoscaler只会从这个单个对象中获取单个度量数据;在此之前的例子中,Autoscaler需要从所有下属pod中获取度量,并使用他们的平均值。需要在HPA对象的定义中指定目标对象与目标值。
...
spec:
metrics:
-type:Object
resource:
metricName:latencyMills
target:
apiVersion:exensions/v1beta1
kind:Ingress
name:frontend
targetValue:20
scaleTargetRef:
apiVersion:extensions/v1beta1
kind:Deployment
name:kubia
...
如上配置为使用Ingress对象frontend的latencyMills度量,目标值20.HPA会监控该Ingress对象的度量,如果该度量超狗目标值太多,autoscaler便会对kubia Deployment资源进行扩容
确定那些度量适合用于自动伸缩
不是所有的度量都适合作为自动伸缩的基础,如之前的pod中容器的内存占比并不是自动伸缩的一个好度量。如果增加副本数不能导致被观测度量平均值的线性(或者至少接近线性)下降,那么autoscaler就不能正常工作。
在决定基于应用自由的自定义度量来伸缩之前,一定要思考pod数量增加或减少,他的值会如何变化
缩容到0个副本
HPA目前不允许设置minReplicas字段为0,所以autoscaler永远不会缩容到0个副本,即使pod什么都没有做也不会。
允许pod数量缩容到0可以大幅提升硬件利用率:如果你运行的服务几个小时甚至几天才会收到一次请求,就没有道理留着他们一直运行,占用本来可以给其他服务利用的资源;然而一旦客户端请求进来,你仍然还想让这些服务马上可用。这叫空载(idling)与解除空载(un-idling),即允许提供特定服务的pod被缩容到0副本,在新请求到来时,请求辉县被阻塞,直到pod被启动,从而请求被转发到新pod为止。
说明:kubernetes目前没有提供这个特性,未来可以看看有没有实现。
二、pod的纵向自动伸缩
横向伸缩很棒,但并不是所有应用都被横向伸缩。
对这些应用而言,唯一选项是纵向伸缩--给他们更多的cpu和(或)内存。因为一个节点所拥有的资源通常都比单个pod请求要多。我们应该几乎总能纵向扩容一个pod。
pod的资源请求是通过pod manifest的字段配置,纵向伸缩pod将会会通过改变这些字段来实现,目前还未看到纵向伸缩实现。
1.自动配置资源请求
如果新创建的pod的容器没有明确设置cpu与内存请求,可以通过一个叫做InitialResources的准入控制插件来提供。当一个没有资源请求的pod被创建时,该插件会根据pod的容器历史资源使用数据(随容器镜像、tag而变)来设置资源请求。
可以不用指定资源请求就部署pod,而靠kubernetes来最终得出每个容器资源需求有多少。实际上,kubernetes是在纵向伸缩这些pod,比如说:一个容器总是内存不足,下次创建一个包含该容器镜像的pod时,他的内存资源就会被自动调高了。
2.修改运行中的pod资源请求
纵向伸缩提案正在定稿
三、集群节点的横向伸缩
HPA在需要的时候会创建更多pod实例,万一所有节点都满了,放不下更多pod了,如果kubernetes集群运行在自建(on premise)基础架构上,你得添加一台物理机,并将其加入集群。如果你的集群运行在云端基础架构之上,添加新的节点通常就是点击几个鼠标,或向云端做api调用。
Cluster Autoscaler介绍
Cluster Autoscaler负责在由于节点资源不足,而无法调度某个pod到已有节点时,自动部署新节点。他也会在节点长时间使用率低下的情况下线节点。
从云端基础架构请求新节点
前提:
如果一个pod被创建之后,scheduler无法将其调度到任何一个已有节点,一个新节点就会被创建。Cluster Autoscaler会注意到此类pod,并请求云服务器提供者启动一个新节点。在做之前,会检查新节点有没有可能容纳这些(个)pod。
云服务提供者通常会把相同规格(或者相同特性)的节点聚合成组,Cluster Autoscaler需要指明节点类型。
Cluster Autoscaler通过检查可用节点分组来确定是否有至少一种节点类型能容纳未被调度的pod。如果只存在唯一一个此种节点分组,Cluster Autoscaler就可以增加节点分组的大小,让云服务提供商给分组增加一个节点。但如果存在多个满足条件的节点分组,Cluster Autoscaler就必须挑一个最合适的。这里“最合适”的精确含义是必须可配置的,最坏情况下,他会随机挑选一个。
新节点启动后,其上运行的kubelet会联系api服务器,创建一个node资源以注册该节点。从这一刻起,该节点即成为kbernetes集群的一部分。可以调度pod于其上。
归还节点
当节点利用率不足时,Cluster Autoscaler也需要能够减少节点的数目。Cluster Autoscaler通过监控所有节点上请求的cpu与内存来实现这一点。如果某个节点上所有pod请求的cpu、内存都不到50%,该节点即被认定为不再需要。
Cluster Autoscaler也会检查该是否有系统pod(仅仅)运行在该节点(并不是每个节点都允许的服务,比如DaemonSet所部署的服务)。如果节点上有系统pod运行,该节点就不会被归还。对非托管pod,以及有本地存储的pod也是如此,否则会造成这些pod提供的服务中断。换句话说,只有当Cluster Autoscaler知道节点上运行的pod能重新调度到其他节点,该节点才会被归还。
当一个节点被选中下线,他首先会标记为不可调度,随后运行其上的pod将被疏散至其他节点。因为所有这些pod都属于ReplicaSet或者其他控制器,他们的替代pod会被创建并调度到其他剩下节点
手动标记为不可调度、排空节点
kubectl cordon <node>标记节点为不可调度(但对其上的pod不做任何事)
kubectl drain <node>标记节点为不可调度,随后疏散其上的所有pod
两种情形下,在你用kubectl uncordon <node> 解除节点的不可调度状态之前,不会有新的pod被调度到该节点
启用Cluster Autoscaler
集群自动伸缩在以下云服务商可用:
Google Kubernetes Engine(GKE)
Google Compute Engine(GCE)
Amazon Web Services(AWS)
Microsoft Azure
在kubia集群运行GKE,启用Cluster Autoscaler
gcloud container cluster update kubia --enble -autoscaling --min-nodes=3 --max-nodes=5
如果在GCE上,需要运行kube-up.sh前设置以下环境变量:
KUBE_ENABLE_CLUSTER_AUTOSCALER=true
KUBE_AUTOSCALER_MIN_NODES=3
KUBE_AUTOSCALER_MAX_NODES=5
注意:Cluster Autoscaler将它的状态发布到kube-system命名空间的cluster-autoscaler-status ConfigMap上
限制集群缩容时的服务干扰
如果一个节点发生非预期故障,不可能阻止其上的pod变为不可用;但如果一个节点被Cluster Autoscaler或者人类操作员主动下线,可以用一个新特性来确保下线操作不会干扰到这个节点上pod所提供的服务。
kubernetes可指定下线等操作时需要保持的最少pod数量,可以通过创建一个podDisruptionBudget(PDB)资源的方式来利用这一特性。
PodDisruptionBudget不会通过yaml文件创建它,可以用kubectl create poddisruptionbudget命令创建它,然后在查看以下yaml文件
如果确保你的kubia pod总有3个实例运行,可以这样创建PodDisruptionBudget资源
kubectl create pdb kubia-pdb --selector=app=kubia --min-available=3
获取PDB的yaml文件如下:
kubectl get pdb kubia-pdb -o yaml
apiVersion:policy/v1betal
kind:PodDisruptionBudget
metadata:
name:kubia-pdb
spec:
minAvailable:3
selector:
mathLabels:
app:kubia
status:
...
也可以用一个百分比而非绝对值来写minAvailable字段。比如说可以指定60%带app=kubia标签的pod应当时刻保持运行。
注意:从kubernetes1.7开始,podDisruptionBudget资源也支持maxUnavailable。如果当很多pod不可用而想要阻止pod被剔除时,就可以用maxUnavailable字段,而不是minAvailable
小结:
本章学了kubernetes能够如何伸缩你的pod以及节点
配置pod的自动横向伸缩很简单,只要创建一个HorizontalpodAutoscaler对象,将他指向一个Deployment、ReplicaSet、ReplicationController,并设置pod的目标cpu使用率即可。
除了让HPA基于pod的CPU利用率进行伸缩操作,还可以配置他就应用自身提供的自定义度量,或在集群中部署的其他对象的度量来自动伸缩
目前还不能进行纵向pod自动伸缩
如果你的kubernetes集群运行在支持的云服务提供者之上,甚至集群节点也可以自动伸缩
可以用带有-it和--rm选项的kubectl run命令在pod中运行一次性的进程,并在按下ctrl+c组合键时自动停止并删除该临时pod