降本提效是翻新开发的永久话题。过去多年来,开发者纷繁拥抱容器技术以提高部署效率,降落运维累赘。随着像Docker这类容器引擎经常使用量始终增长,作为Docker治理系统的Kubernetes(简称K8s)趁势而出,协助开发者构建并简化复杂的容器编排任务。
本文Akamai将带大家一同看看,如何准确确定Kubernetes集群的规模,并依据需求更灵敏、灵活地对集群规模启动缩放,从而在满足负载需求的同时最大限制降落老本。
一、高效确定Kubernetes集群的最优规模
每当咱们须要创立Kubernetes集群时,必需首先都会问自己:该用什么类型的任务节点?详细须要多少个?
例如,当咱们正在经常使用Linode Kubernetes引擎(LKE)等托管式Kubernetes服务,究竟该经常使用8个2GB的Linode实例,还是2个8GB的Linode实例来成功所需计算才干?
在回答这个疑问之前须要留意:无论自建K8s集群或任何云平台上托管的K8s,并非一切任务节点中的资源都可以用于运转任务负载。
在Kubernetes节点中,CPU和内存会被划分给:
假定有个只要一个Linode 2GB计算实例的集群(蕴含1个vCPU和2GB内存),以下资源会被保管给kubelet和操作系统:
此外,还有100MB内存为驱逐阈值保管。
总的来说,此时咱们有30%的内存和6%的CPU是不能被任务负载经常使用的。
每个云提供商都有各自定义限制的形式,但在CPU方面他们仿佛不约而同启动了以下限制:
至于内存方面的限制,不同提供商之间有很大差异。但普通来说,内存的预留往往遵照以下限制:
既然知道了任务节点内资源的调配形式,那么咱们该选用哪种实例?答案因详细状况而异,咱们须要依据任务负载的实践状况来选用最佳任务节点。
Kubernetes中有两种方法来指定容器可以经常使用多少内存和CPU:
Kubernetes调度程序经常使用恳求来确定在集群中调配Pod的位置。由于调度程序不知道消耗状况(Pod尚未启动),因此它须要一个揭示。这些“揭示”就是恳求;咱们可认为内存和CPU区分设置恳求。
kubelet经常使用限制在内存经常使用超出准许范畴时中止进程。假设经常使用的CPU时期超越准许的范畴,kubelet也会限制该进程。但是,该如何选用适当的恳求和限制值呢?
咱们可以测量任务负载性能(例如平均值、95和99百分位数等)并将其用作恳求和限制。为了简化该环节,可以经过两个便利的工具来减速剖析:
VPA会搜集内存和CPU应用率数据,并运转一个回归算法,为咱们的部署倡导恳求和限制。这是一个官方的Kubernetes名目,也可以用于智能调整值:咱们可以让控制器间接在YAML中更新恳求和限制。
KRR的任务原理相似,但它应用了咱们经过Prometheus导出的数据。作为第一步,任务负载应该被性能为将度量数据导出到Prometheus。一旦存储了一切度量数据,就可以经常使用KRR来剖析数据并倡导恳求和限制。
在具有了(粗略的)资源需求概念后,终于可以继续选用一个实例类型了。
假定预算自己的任务负载须要2GB的内存恳求,并且预计至少须要约10个正本。咱们可以扫除大少数小于2GB的小型实例。此时髦容许以间接经常使用某些大型实例,例如Linode 32GB。
接上去,可以将内存和CPU除以可部署在该实例上的最大Pod数量(例如在LKE中的110个),以取得内存和CPU的单元数量。
在最后一步中,咱们可以经常使用这些单元来预算有多少任务负载可以顺应节点。
假想象要部署一个Spring Boot,恳求为6GB和1 vCPU,这相当于:
这些数字标明,内存耗尽之前受限会将CPU耗尽,并且最多可以在集群中部署(110/24)4个运行程序。
当咱们在此实例上运转四个任务负载时,将经常使用:
还不错,但能做得更好吗?让咱们尝试经常使用Linode 64GB实例(64GB / 16 vCPU)。
假定要部署相反的运行程序,数字会出现一些变动:
可以在这个实例中放多少任务负载?由于将耗尽内存,并且每个任务负载须要12个单元,所以最大运行程序数是9(即110/12)。
计算效率/糜费比例将会发现:
虽然糜费的CPU数量简直与前一个实例相反,但内存应用率失掉了显着改善。
最后,咱们还可以比拟一下老本:
换句话说,选用较大的实例可认为咱们每月每个任务负载节俭多达6美元。
4.经常使用计算器对比不同节点
假构想测试更多实例该怎样办?启动这些计算须要很多任务。咱们可以经常使用learnsk8s计算器放慢该环节。
经常使用该计算器的第一步是输入内存和CPU恳求。系统会智能计算保管的资源并提供应用率和老本倡导。此外还有一些额外的适用性能:依照运行程序用量调配最凑近的CPU和内存恳求。假设运行程序偶然会突发高CPU或内存经常使用率,也可以灵敏应答。
但是当一切Pod都将一切资源经常使用到极限会出现什么?这或许造成超额承诺。咱们可以经过门户中的小组件了解CPU和内存超额承诺的百分比。那么当超额承诺时详细又会出现什么?
最后,咱们还可以经常使用DaemonSets和Agent小组件,这是一个繁难的机制,可以模拟在一切节点上运转的Pod。例如,LKE将Cilium和CSI插件部署为DaemonSets。这些Pod经常使用的资源对任务负载无法用,应从计算中减去。该小组件可以帮咱们做到这一点!
为了尽或许降落基础设备老本,咱们可以在不经常使用某些资源时将其封锁。但是此时的应战之处在于,必要时该如何将资源智能关上。接上去咱们一同看看如何经常使用Linode Kubernetes Engine(LKE)部署一个Kubernetes集群,并经常使用Kubernetes Events-Driven Autoscaler(KEDA)将其收缩到“零”,而后恢还原状。
假定咱们在Kubernetes上运转了一个经常出现的资源密集型运行,但咱们只要要在任务时期里运转。此时或许会宿愿在大家都任务后将其封锁,并在任务时期智能从新关上。
虽然可以经常使用CronJob来缩放实例,但这只是权宜之计,只能依照预先设定的时期表照方案运转。
周末怎样办?公共假期又如何处置?假设整个团队都生病无法到岗呢?
与其编制一个始终增长的规定列表,不如依据流量来裁减咱们的任务负载。当流量参与时,可以裁减正本数量;当没有流量时,可以将整个运行封锁。当运行封锁后又收到新的传入恳求后,Kubernetes会启动至少一个正原本处置这些流量。
接上去一同看看该如何阻拦去往运行程序的一切流量,监控流量,并设置Autoscaler调整正本数量或封锁运行。
首先须要创立一个Kubernetes集群。可经常使用下列命令创立一个集群并保管kubeconfig文件。
$ linode-cli lke cluster-create \ --label cluster-manager \ --region eu-west \ --k8s_version 1.23$ linode-cli lke kubeconfig-view "insert cluster id here" --text | tail +2 | base64 -d > kubeconfig
经过下列命令验证装置环节已成功成功:
$ kubectl get pods -A --kubecnotallow=kubeconfig
用环境变量导出kubeconfig文件通常是一种比拟繁难的做法。为此可以运转:
$ export KUBECONFIG=${PWD}/kubeconfig$ kubectl get pods
接着须要部署运行程序。
apiVersion: apps/v1kind: Deploymentmetadata: name: podinfospec: selector: matchLabels: app: podinfo template: metadata: labels: app: podinfo spec: containers: - name: podinfo image: stefanprodan/podinfo ports: - containerPort: 9898---apiVersion: v1kind: Servicemetadata: name: podinfospec: ports: - port: 80 targetPort: 9898 selector: app: podinfo经常使用下列命令提交YAML文件:terminal|command=1|title=bash$ kubectl apply -f 1-deployment.yaml
随后即可访问该运行,为此请关上阅读器并访问localhost:8080。
$ kubectl port-forward svc/podinfo 8080:80
接着应该就能看到这个运行了。
接上去须要装置KEDA,也就是本例中将会用到的Autoscaler。
Kubernetes提供的Horizontal Pod Autoscaler(HPA)可以作为控制器灵活增减正本数量。但是HPA有一些无余之处:
好在并非只能经常使用官方提供的Autoscaler,咱们还可以经常使用KEDA。KEDA是一种为下列三个组件打造的Autoscaler:
Scaler相似于适配器,可以从数据库、信息代理、遥测系统等处搜集目的。例如,HTTP Scaler这个适配器就可以阻拦并搜集HTTP流量。咱们可以在这里看到一个 经常使用RabbitMQ的Scaler范例。
Metrics Adapter担任以Kubernetes目的管道可以经常使用的格局导出Scaler所搜集的目的。
最后,Controller可以将一切这些组件严密联合在一同:
通常上的引见就是这些了,一同看看它们实践上是如何起效的。
咱们可以经常使用Helm加快装置Controller,详细的说明和引见请参阅Helm官方。
$ helm repo add kedacore$ helm install keda kedacore/keda
KEDA自动并不蕴含HTTP Scaler,因此须要独自装置:
$ helm install http-add-on kedacore/keda-add-ons-http
随后就可以裁减咱们的运行了。
KEDA的HTTP加载项会暴显露一个CRD,借此咱们可以形容运行程序的裁减形式。一同看一个例子:
kind: HTTPScaledObjectapiVersion: http.keda.sh/v1alpha1metadata: name: podinfospec: host: example.com targetPendingRequests: 100 scaleTargetRef: deployment: podinfo service: podinfo port: 80 replicas: min: 0 max: 10
该文件会批示阻拦器将无关的恳求转发给podinfo服务。
其中还蕴含了须要裁减的部署的称号,本例中为podinfo。
经常使用下列命令将YAML提交至集群:
$ kubectl apply -f scaled-object.yaml
提交了上述定义后,Pod被删除了!为何会这样?
在创立了HTTPScaledObject后,KEDA会立即将该部署收缩到零,由于目前没有流量。
为了启动裁减,咱们必需向运行收回HTTP恳求。试试看衔接到该服务并收回一个恳求。
$ kubectl port-forward svc/podinfo 8080:80
这种现象是正当的,由于目前没有可认为恳求提供服务的Pod。但Kubernetes为何没有将该部署裁减为1?
在经常使用Helm装置加载项时,会创立一个名为keda-add-ons-http-interceptor-proxy的Kubernetes服务。为了让智能裁减能够反常起效,HTTP流量必需首先经过该服务启动路由。咱们可以用kubectl port-forward启动测试:
$ kubectl port-forward svc/keda-add-ons-http-interceptor-proxy 8080:8080
这一次性咱们无法在阅读器中访问该URL。
一个KEDA HTTP阻拦器可以处置多个部署,那么它如何知道要将流量路由到哪里?
kind: HTTPScaledObjectapiVersion: http.keda.sh/v1alpha1metadata: name: podinfospec: host: example.com targetPendingRequests: 100 scaleTargetRef: deployment: podinfo service: podinfo port: 80 replicas: min: 0 max: 10
针对这种状况,HTTPScaledObject经常使用了一个host 段。在本例中,咱们须要伪装恳求来自。为此须要设置Host头:
$ curl localhost:8080 -H 'Host: example.com'
咱们将收到一个回应,虽然稍微有些提早。
审核Pod会发现,部署曾经被裁减至一个正本:
$ kubectl get pods
那么刚才究竟出现了什么?
在将流量路由至KEDA的服务时,阻拦器会追踪尚未收到回复的未决HTTP恳求数量。KEDA Scaler会活期审核阻拦器的队列大小,并存储相关目的信息。
KEDA Controller会监控目的,并依据须要增大或减小正本数量。本例中有一个未决恳求,此时KEDA Controller将部署裁减为一个正本就已足够。
咱们可以经过下列形式失掉每个阻拦器的未决HTTP恳求队列形态:
$ kubectl proxy &$ curl -L localhost:8001/api/v1/namespaces/default/services/keda-add-ons-http-interceptor-admin:9090/proxy/queue{"example.com":0,"localhost:8080":0}
由于这种设计的存在,咱们必需谨慎选择该用何种形式将流量路由给运行。KEDA只能在流量可被阻拦的状况下才会对部署启动裁减。
假设有一个现有的入口Controller,并且宿愿经常使用该Controller将流量转发给运行,那么还须要修正入口清单,将流量转发给HTTP加载项服务。一同看一个例子。
7.将KEDA HTTP加载项与入口配合经常使用
$ helm upgrade --install ingress-nginx ingress-nginx \ --repo\ --namespace ingress-nginx --create-namespace
随后写一个入口清单,将流量路由给podinfo:
apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: podinfospec: ingressClassName: nginx rules: - host: example.com http: paths: - path: / pathType: Prefix backend: service: name: keda-add-ons-http-interceptor-proxy # <- this port: number: 8080
经过下列命令可以失掉负载平衡器的IP地址:
LB_IP=$(kubectl get services -l "app.kubernetes.io/compnotallow=controller" -o jsnotallow="{.items[0].status.loadBalancer.ingress[0].ip}" -n ingress-nginx)
最后经常使用下列命令向运行收回一个恳求:
curl $LB_IP -H "Host: example.com"
起作用了!假设期待足够长的时期,咱们还将留意到,该部署最终被收缩到零。
在设计Kubernetes集群时,咱们或许经常须要回答以下疑问:
有四个关键要素会影响集群的伸缩:
下文将依次探讨这些要素。
自动状况下,kubelet每10秒从Pod中提取一次性CPU经常使用状况数据,而Metrics Server每1分钟从kubelet失掉一次性这些数据。Horizontal Pod Autoscaler每30秒审核一次性CPU和内存度量。
假设度量超越阈值,Autoscaler会参与Pod的正本数,并在采取进一步执行之前暂停3分钟。在最蹩脚的状况下,或许要期待长达3分钟才干参与或删除Pod,但平均而言,用户应该希冀期待1分钟后Horizontal Pod Autoscaler即可触发伸缩。
Cluster Autoscaler会审核能否有待处置的Pod,并参与集群的大小。检测到须要裁减集群或许须要:
Linode上的节点预配,也就是从Cluster Autoscaler触发API到新创立节点上可以调度Pod,这一环节须要大概3-4分钟时期。
简而言之,关于小规模集群,咱们会面临:
关于具有100个以上节点的集群,总提早或许为6分30秒…… 这是一个相当长的时期,那么该如何处置这个疑问?可以被动调整任务负载,或许假设十分了解流量形式,也可以提早伸缩。
假设流量的变动形式可预测,那么在高峰之前裁减当务负载(和节点)就是可行的。
$ helm repo add kedacore$ helm install keda kedacore/keda
装置好Prometheus和KEDA后,创立一个部署。
apiVersion: apps/v1kind: Deploymentmetadata: name: podinfospec: replicas: 1 selector: matchLabels: app: podinfo template: metadata: labels: app: podinfo spec: containers: - name: podinfo image: stefanprodan/podinfo
用下列命令将资源提交到集群:
$ kubectl apply -f deployment.yaml
KEDA在现有的Horizontal Pod Autoscaler之上任务,并经常使用名为ScaleObject的自定义资源定义(CRD)启动包装。下列ScaledObject经常使用Cron Scaler定义了更改过本数的时期窗口:
apiVersion: keda.sh/v1alpha1kind: ScaledObjectmetadata: name: cron-scaledobject namespace: defaultspec: maxReplicaCount: 10 minReplicaCount: 1 scaleTargetRef: name: podinfo triggers: - type: cron metadata: timezone: Europe/London start: 23 * * * * end: 28 * * * * desiredReplicas: "5"
$ kubectl apply -f scaled-object.yaml
接上去会出现什么?什么也不会出现。智能伸缩只会在23 * * * *到28 * * * *之间触发。在Cron Guru的协助下,咱们可以将这两个Cron表白式翻译成:
假设等到开局时期,咱们将留意到正本数参与到5。
经常使用KEDA经过Cron表白式启动伸缩
在第28分钟后,正本数能否恢复到1?是的,智能伸缩器会恢复为minReplicaCount中指定的正本数。
假设在其中一个时时期隔内参与正本数会出现什么?假设在23和28分钟之间,咱们将部署的正本数裁减到10,KEDA将笼罩咱们的更改并设置计数。假设在第28分钟后重复相反试验,正本数将设置为10。
在了解了通常后,让咱们看一些实践用例。
假定咱们在开发环境中部署了一个应该在任务时期段内处于生动形态,并且在夜间应该封锁的任务负载。
apiVersion: keda.sh/v1alpha1kind: ScaledObjectmetadata: name: cron-scaledobject namespace: defaultspec: maxReplicaCount: 10 minReplicaCount: 0 scaleTargetRef: name: podinfo triggers: - type: cron metadata: timezone: Europe/London start: 0 9 * * * end: 0 17 * * * desiredReplicas: "10"
自动正本数为零,但在任务时期(上午9点到下午5点)时期,正本会裁减到10个。
仅在任务时期内裁减当务负载
咱们还可以裁减Scaled Object以扫除周末:
apiVersion: keda.sh/v1alpha1kind: ScaledObjectmetadata: name: cron-scaledobject namespace: defaultspec: maxReplicaCount: 10 minReplicaCount: 0 scaleTargetRef: name: podinfo triggers: - type: cron metadata: timezone: Europe/London start: 0 9 * * 1-5 end: 0 17 * * 1-5 desiredReplicas: "10"
这样,任务负载将仅在周一至周五的9点到17点生动。由于可以组合多个触发器,因此还可以包括一些例外状况。
咱们或许方案在星期三让任务负载坚持更长时期,为此可经常使用以下定义:
apiVersion: keda.sh/v1alpha1kind: ScaledObjectmetadata: name: cron-scaledobject namespace: defaultspec: maxReplicaCount: 10 minReplicaCount: 0 scaleTargetRef: name: podinfo triggers: - type: cron metadata: timezone: Europe/London start: 0 9 * * 1-5 end: 0 17 * * 1-5 desiredReplicas: "10" - type: cron metadata: timezone: Europe/London start: 0 17 * * 3 end: 0 21 * * 3 desiredReplicas: "10"
在此定义中,任务负载会在周一至周五的9点到17点之间处于优惠形态,但星期三会从9点继续到21点。
按需缩放是一种有效降落老本的方法。Kubernetes作为一种容器编排平台,提供了智能化治理和部署容器化运行程序的性能,使得按需缩放变得愈加容易成功。
依据本文提供的思绪,咱们可以依据运行程序的需求变动状况,灵活调整资源,并在须要时智能裁减或缩减规模,从而降落老本并提高资源应用率。
本文所触及的内容,不只适用于Linode平台上提供的托管式Kubernetes集群,也雷同适用于大家在本地环境或其余云平台上部署的集群。宿愿这些内容对大家有所协助,也欢迎关注Akamai机构号,了解更多经过云平台降本增效的技巧。
如您所在的企业也在思考洽购云服务或启动云迁徙,