Cerebral是一个开源的工具,用于响应用户定义的策略生成的警报来增加或减小Kubernetes集群中节点池的大小。这些策略引用可插拔和可配置的度量标准后端,以收集度量标准以进行自动缩放决策。
本文,在易于搭建的 Kops
环境中将Cerebral与Kubernetes Cluster Autoscaler进行了公平地比较和对比。
Kubernetes 伸缩基础
要了解生产中的自动缩放,我们首先必须退后一步,从一般意义上理解Kubernetes的自动缩放。与Kubernetes相关的自动缩放器有几类:
- 垂直Pod自动缩放器(VPA),可根据观察到的利用率自动设置资源请求
- 水平Pod自动缩放器(HPA),可根据观察到的利用率(或自定义指标)自动缩放Pod的数量
- Cluster Autoscaler,它可以自动在群集节点池中添加或删除节点,以满足需求并节省资金
在本文中,我们将重点介绍水平Pod自动缩放和群集自动缩放。它们通常相互串联使用,因此,我们将研究HPA与集群自动缩放器之间的交互以及不同集群自动缩放器实现之间的权衡。特别是,我们将介绍Cerebral和Kubernetes Cluster Autoscaler(CA)项目之间的区别。
Kubernetes Cluster Autoscaler 总览
Kubernetes Cluster Autoscaler采用一种简单的方法(至少在较高级别上)来决定何时将节点添加到集群中:当发现无法调度Pod并确定添加新节点后将能够调度成功该Pod时,它将进行扩展。这种方法有很多好处,例如,它仅依赖于Kubernetes核心概念。它还保证仅在绝对需要时才添加节点,因为它知道由于资源不足而导致Pod处于pending状态。
这种方法的主要缺点是,它太慢了,无法满足实际的业务需求。在许多情况下,等到无法调度Pod的时间为时已晚。
使用CA进行缩小也很简单:如果某个节点在一定时间内未得到充分利用,并且当前可以在该节点上运行的Pod可以重新安排到另一个节点上,则它将删除该节点。
对于CA如何工作的细节,FAQ是一个很好的资源。
Cerebral 总览
Cerebral的构建考虑了灵活性。它使operators可以轻松地对从各种来源收集的指标进行自动缩放配置,并可以通过使用自定义资源定义(CRD)在云环境之间轻松移植的方式进行配置。
Cerebral采用的抢占式自动缩放方法与标准的Kubernetes Cluster Autoscaler根本不同。 Cerebral无需等待直到无法安排Pod,而是决定根据来自可配置指标后端的用户指定指标进行扩展。在撰写本文时,主要的Cerebral存储库中支持以下后端:Prometheus,InfluxDB和Kubernetes本身。
通过实现简单的接口并在新的MetricsBackend CRD中定义后端的配置,也可以轻松构建自定义后端。自定义后端的可能性是无限的。例如,可以使用Cerebral基于特定于应用程序的队列的深度来自动缩放群集。
在本文中,我们将坚持使用Kubernetes后端与Kubernetes CA进行公平比较。
有关Cerebral的工作方式的更多详细信息,以及其他指标后端和云提供商的入门信息,请参阅GitHub上的项目。
自动缩放实验
让我们看一个简单的示例,将HPA与Kubernetes Cluster Autoscaler和Cerebral结合使用。这将使我们能够在模拟的真实场景中对比Kubernetes CA和Cerebral。
如果您对实验本身不感兴趣,请随时跳至“关键”部分。
在AWS准备一个测试集群
虽然Cerebral已完全集成到支持AWS的Containership Kubernetes Engine(CKE)中,但让我们改用kops以便从基础上进行构建。
我们创建了一个具有非常基本的kops集群配置的公共要点,任何人都可以复制它以进行后续操作。集群具有一个主节点和一个节点池(等于一个AWS Auto Scaling组),其最小大小为1个节点,最大大小为5个节点。只需在S3中创建一个新的存储桶以用作集群状态存储,然后按以下步骤创建集群即可。首先以kops创建集群配置:
kops --state=s3://bucket-name create -f https://gist.githubusercontent.com/mattkelly/692af294868ff50a6c5664ea63b7e9c4/raw/48b00e3dc3d7de18c040522a6392a92beb9dc7e1/kops\-autoscaling-demo-cluster-config.yaml
你应该能看到如下的输出:
现在apply更改以实际创建集群:
kops --state=s3://bucket-name update cluster autoscaling-experiment.k8s.local --yes
它可能会提示您首先指定一个公用SSH密钥。如果是这样,只需按照说明创建密钥,然后重新运行更新命令。
您应该看到一堆输出,然后最终是:
几分钟后,您应该能够与群集进行交互。尝试kubectl get nodes
,直到它起作用为止(请注意,如果一切顺利,kops
将适当地设置您的kubectl上下文)。
有关kops的更多信息,请参考存储库。
部署 Kubernetes Metrics Server
水平Pod自动缩放器需要知道Pod正在消耗多少资源。
为此,我们需要部署metrics-server
。请注意,仅HPA才需要,而不是Cerebral或Kubernetes CA都需要。
启动并运行metrics-server的最简单方法是简单地克隆存储库并部署所有Kubernetes v1.8 +清单。不幸的是,我们发现需要一些变通办法来使清单在此kops群集上正常工作。一旦不再需要此解决方法,我们将更新本文。
同时,您可以在我的fork上使用分支:
git clone [email protected]:mattkelly/metrics-server.git
cd metrics-server
git checkout bugfix/deploy-1.8+
应用以下相关清单:
kubectl apply -f deploy/1.8+/
检查metrics-server pod日志,以确保一切正常:
kubectl -n kube-system logs -lk8s-app=metrics-server
创建用于自动扩展的资源使用者部署
现在,我们已经建立了一个群集,并且可以使用运行状况良好的metrics server,让我们继续创建一个部署,可以将其包装在HPA中以运行实验。如果我们能够通过强制它消耗某些请求的结果而消耗指定数量的CPU或内存来使它按需扩展,那就太好了。幸运的是,生活在Kubernetes存储库深处的resource-consumer确实做到了这一点。
让我们根据CPU利用率自动调整resource-consumer
部署。我们的工作节点池正在使用t2.small实例,这些实例具有1个CPU(1000 millicores)和2 GB的内存。由于工作池的最小节点数为1,并且尚未扩展,因此该池中只有一个节点。使用kubectl describe节点,我们看到该节点已经在使用640 millicores的CPU:
让我们在默认命名空间中运行resource-consumer
,请求为100 millicores,并使用负载均衡器公开它,以便我们向其发送请求:
kubectl run resource-consumer --image=gcr.io/kubernetes-e2e-test-images/resource-consumer:1.4 --expose --service-overrides='{ "spec": { "type": "LoadBalancer" } }' --port 8080 --requests='cpu=100m'
立刻,我们看到初始的单个副本正按预期从节点请求另一个100m:
由于使用负载均衡器公开了resource-consumer
的部署,因此我们还应该能够使用kubectl get服务来查看它是否已正确分配了外部地址:
让我们导出完整的地址以供以后发送请求:
export RESOURCE_CONSUMER_ADDRESS="http://$(kubectl get service resource-consumer -ojsonpath='{.status.loadBalancer.ingress[0].hostname}'):8080"
创建水平Pod自动缩放器(HPA)
现在,我们将HPA添加到resource-consumer
部署中。如果观察到的Pod CPU利用率超过Pod CPU请求的50%,则此HPA将添加一个新Pod(最多10个Pod)。如果部署中的Pod没有显示出很高的CPU利用率,它还将缩小到最少1个Pod。
kubectl autoscale deployment resource-consumer --cpu-percent=50 --min=1 --max=10
您应该能够使用kubectl get hpa看到新创建的HPA,因为它只是另一个Kubernetes资源:
注意:HPA就像其他任何Kubernetes控制器一样都是循环检查。默认情况下,它们每15秒收集一次指标。因此,利用率百分比可能会短暂显示为<未知>,直到收集度量为止。
正如预期的那样,资源消耗者实际上并没有消耗任何CPU,因为我们尚未要求这样做。
现在我们已经设置了HPA,让我们通过消耗CPU并查看Kubernetes Cluster Autoscaler和Cerebral的反应来强制其扩展!
设置Kubernetes Cluster Autoscaler
我们创建了一个公共gist,可以很容易地在此kops集群上启动和运行Kubernetes集群自动缩放器(CA)。它对AWS清单所示的CA示例进行了如下调整:
- 添加已将主部署更新为参考的secret
- 设置部署命令以正确地引用带有预期最小/最大界限的kops辅助节点池。
- 添加容忍和节点选择器以使其仅在主节点上运行
只需下载文件,填写AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY的有效值,然后kubectl即可应用它。
您可以通过检查日志来检查集群自动伸缩器部署是否正常:
kubectl -n kube-system logs -lapp=cluster-autoscaler
此处应用的“cerebral-aws-engine”secret也可以与Cerebral自动缩放器一起使用。)
注意:这只是出于演示目的运行CA的一种简单方法。如果您对在生产环境中运行CA感兴趣,请阅读文档以确定最适合您的环境(包括身份验证方法等)的文档。
使用Kubernetes集群自动缩放器进行集群自动缩放演示
仅当无法调度Pod时,Kubernetes Cluster Autoscaler才会缩放。这种缩放方法学是CA与Cerebral之间的主要区别之一。让我们确切地了解这在实践中意味着什么。
为了模拟需求激增的开始,让我们通过将请求发布到其ConsumeCPU
端点来告诉resource-consumer
消耗CPU:
curl --data "millicores=60&durationSec=600" $RESOURCE_CONSUMER_ADDRESS/ConsumeCPU
如果愿意,您可以通过在第二个终端上运行watch
来实时观看动作:
watch --differences kubectl get deployments,replicasets,pods,hpa
片刻之后,您应该看到此峰值导致HPA向部署中添加了另一个Pod,以满足需求并保持部署中Pod的CPU利用率低于50%的目标:
再过一会儿,您应该会看到情况稳定下来,并且HPA报告了新的CPU利用率:
并且kubectl describe nodes
显示新副本向该节点添加了另外100m的CPU请求:
我们有一个简单,可重复的环境,Horizontal Pod Autoscaler可以按预期工作。
现在,我们假设在很短的时间内,需求会增加更多:
curl --data "millicores=120&durationSec=600" $RESOURCE_CONSUMER_ADDRESS/ConsumeCPU
片刻之后,我们看到HPA可以扩展以满足新的需求:
看起来我们有一个处于待定状态的Pod,因此我们还没有成功地进行扩展以满足新的需求。这里发生了什么?使用该Pod上的kubectl describe,我们可以看到该Pod无法调度,因为工作节点没有足够的CPU来满足所有CPU请求。由于我们现在有4个副本,这比640m / 1000m的基本状态多了400m的CPU请求,因此只能在该节点上安排3/4个副本。
kubectl describe pod/resource-consumer-55c57bc84c-986lk
我们还可以看到由于挂起的pod触发了放大。在等待新节点出现之后,另一个kubectl describe
显示pod最终被调度在新节点上。
kubectl get events --field-selector=involvedObject.name=resource-consumer-55c57bc84c-986lk -o=custom-columns=FIRST_TIMESTAMP:.firstTimestamp,TYPE:.type,REASON:.reason,MESSAGE:.message
从以上内容可以看出,CA迅速做出反应,要求AWS扩展实例组-但仅在pod未能首先调度好之后。
Cooling Down
如果我们等待对resource-consumer
的请求中的durationSec指定的持续时间,我们将看到resource-consumer
回落到其原始资源使用情况。 CA会注意到这一点,并在默认的冷却时间10分钟后请求缩减规模。这对于节省成本至关重要。可以通过CA配置来调整冷却时间。
使用Cerebral抢占式自动缩放
清理并重新开始
确保我们从头开始的最简单方法是简单地删除kops集群并重新创建它。要删除它,请运行以下命令:
kops --state=s3://bucket-name delete cluster autoscaling-experiment.k8s.local --yes
现在,只需重新创建测试设置,从创建kops集群开始,到配置HPA后结束。
部署Cerebral
在脑存储库中可以找到许多示例清单。首先,我们需要应用一些必备清单,这些清单定义一些自定义资源定义,一个Cerebral的服务帐户以及一些RBAC规则:
kubectl apply -f https://raw.githubusercontent.com/containership/cerebral/v0.4.0-alpha/examples/common/00-prereqs.yaml
我们还必须像以前一样使用AWS凭证应用相同的secret。我们可以下载示例secret清单并进行适当的编辑:
curl -O https://raw.githubusercontent.com/containership/cerebral/master/examples/engines/aws/00-secret-cerebral-aws.yaml
# Edit OO-secret-cerebral-aws.yaml to fill in valid values
kubectl apply -f OO-secret-cerebral-aws.yaml
接下来,让我们部署Cerebral operator,它作为Kubernetes部署运行:
kubectl apply -f https://raw.githubusercontent.com/containership/cerebral/v0.4.0-alpha/examples/engines/aws/10-deployment-cerebral-aws.yaml
要向Cerebral注册AWS自动缩放引擎,需要一个AutoscalingEngine资源:
kubectl apply -f https://raw.githubusercontent.com/containership/cerebral/master/examples/engines/aws/20-autoscaling-engine-aws.yaml
最后,让我们向Cerebral注册Kubernetes MetricsBackend:
kubectl apply -f https://raw.githubusercontent.com/containership/cerebral/v0.4.0-alpha/examples/metrics_backends/kubernetes/00-metrics-backend-kubernetes.yaml
如果一切顺利,日志应该看起来很健康,我们应该看到有关引擎和后端成功注册的消息:
kubectl -n kube-system logs -lapp.kubernetes.io/name=cerebral
配置 Autoscaling Groups and Policies
既然Cerebral已启动并在Kubernetes指标后端已注册并运行AWS引擎的情况下运行,我们可以将重点转移到定义一个自动扩展组和扩展该组的策略。
Cerebral存储库中提供了示例AutoscalingGroups以及示例AutoscalingPolicies。但是,让我们在此处创建更特定于我们的测试设置的组和策略。
首先,让我们定义一个AutoscalingPolicy,该策略使用在上一步中定义的kubernetes指标后端收集CPU分配指标以进行自动缩放决策。我们将每15秒轮询一次指标后端,并在30秒内进行抽样。如果CPU分配大于或等于80%的时间超过该采样周期,则Cerebral将按一个节点扩展:
现在应用它:
kubectl apply -f cpu-example-policy.yaml
接下来,让我们定义AutoscalingGroup,以使minNodes和maxNodes与kops定义的AWS AutoscalingGroup相匹配。这对于避免意外行为很重要。选择用于选择属于该组的节点的标签选择器,以匹配我们在kops配置中分配给工作组的标签,而我们刚刚定义的策略将附加到该组:
现在应用它:
kubectl apply -f kops-group.yaml
有了这些简单的清单,一旦CPU分配超出阈值,Cerebral现在就可以自动缩放Kubernetes集群。
使用Cerebral 进行集群扩缩演示
完全按照我们在Kubernetes Cluster Autoscaler演示中所做的一样,让我们向resource-consumer
发出请求以使其消耗更多的CPU以模拟需求高峰的开始:
curl --data "millicores=60&durationSec=600" $RESOURCE_CONSUMER_ADDRESS/ConsumeCPU
如预期的那样,第二个副本将出现并在几分钟后开始运行:
和以前一样,这会将工作节点池上的CPU请求总数提高到80%以上。Cerebral注意到了这一点,在我们在AutoscalingPolicy中配置的短暂采样周期之后,触发了放大事件。我们可以在Cerebral日志中看到这一点:
您还会在日志中注意到,引擎会忽略所有后续缩放请求,因为自动缩放组已进入了我们定义的冷却期。这为新节点的建立提供了时间,并有助于避免对云提供商的伸缩请求进行大幅调整。
在短时间内,AWS会启动新节点,并可以用于调度:
kubectl get events --field-selector=involvedObject.name=ip-172-20-37-156.ec2.internal -o=custom-columns=FIRST_TIMESTAMP:.firstTimestamp,REASON:.reason,MESSAGE:.message![image-19.png](/img/bVbCXem)
如您所见,在触发放大之前,我们不必等待任何pod处于“待处理”状态。违反定义的CPU资源请求阈值后,就会触发放大事件。换句话说,Cerebral抢先调度另一个节点,以满足Pod调度需求。
Cooling Down
在增加的需求减少之后缩减规模以节省成本同样容易。
只需编辑AutoscalingPolicy即可添加一个缩减策略,一旦资源请求低于定义的阈值,该策略就会触发。
重点
当在我们的测试场景中增加负载时,Kubernetes Cluster Autoscaler(CA)和Cerebral都能够成功地扩展集群(向该集群添加节点)。当发现负载正在增加时,Cerebral能够使用用户定义的CPU阈值抢先扩大规模。但是,Kubernetes CA必须等待,直到无法调度Pod。
这只是使用Cerebral优于Kubernetes CA的一个非常具体的例子。Cerebral的真正力量在于其灵活性和可扩展性。例如,在先前的实验中,如果负载瞬间增加到最高点,则Cerebral将无法抢先缩放,并且两个自动缩放器之间的结果将非常相似。但是,与Kubernetes CA不同,Cerebral可以配置为使用自定义指标/事件后端来触发不同指标的缩放(例如,push ,应用程序队列深度达到某个阈值等)。
借助Cerebral,操作员可以以对其系统有意义的方式配置自动缩放。由于基于CRD的方法,他们还可以轻松地将此功能转移到其他云提供商。