跳到主要内容
博客容器(Kubernetes, Docker)Kubernetes集群的主动式扩展

Kubernetes 集群的主动扩缩

为Kubernetes集群主动扩容

这篇文章是我们扩展Kubernetes系列的一部分。 注册来观看直播或访问录音,并查看本系列的其他文章。

当你的集群资源不足时,Cluster Autoscaler会提供一个新的节点并将其添加到集群中。如果你已经是一个Kubernetes用户,你可能已经注意到,创建和添加一个节点到集群需要几分钟时间。

在这段时间里,你的应用程序很容易被连接所淹没,因为它无法进一步扩展。

截图显示了基于每秒请求数(RPS)的预期扩展与仅依靠集群自动缩放器时发生的实际扩展高原。
供应一个虚拟机可能需要几分钟时间。在这段时间内,你可能无法扩展你的应用程序。

如何解决等待时间过长的问题?

主动扩大规模,或。 

  • 了解集群自动缩放器如何工作,并最大限度地发挥其作用。
  • 使用Kubernetes调度器将pod分配给一个节点;以及
  • 主动提供工作节点,以避免糟糕的扩展。

如果你喜欢阅读本教程的代码,你可以在LearnK8s GitHub上找到。

Cluster Autoscaler如何在Kubernetes中工作

Cluster Autoscaler在触发自动缩放时并不看内存或CPU的可用性。相反,Cluster Autoscaler对事件作出反应,并检查任何不可调度的pod。当调度器不能找到一个可以容纳它的节点时,一个pod就是不可调度的。

让我们通过创建一个集群来测试一下。

bash
$ linode-cli lke cluster-create \
 --label learnk8s \
 --region eu-west \
 --k8s_version 1.23 \
 --node_pools.count 1 \
 --node_pools.type g6-standard-2 \
 --node_pools.autoscaler.enabled enabled \
 --node_pools.autoscaler.max 10 \
 --node_pools.autoscaler.min 1 \
 
$ linode-cli lke kubeconfig-view "insert cluster id here" --text | tail +2 | base64 -d > kubeconfig

你应该注意以下细节。

  • 每个节点有4GB内存和2个vCPU(即`g6-standard-2`)。
  • 集群中只有一个节点;以及
  • 集群自动缩放器被配置为从1个节点增长到10个节点。

你可以用以下方法验证安装是否成功。

bash
$ kubectl get pods -A --kubeconfig=kubeconfig

用环境变量导出kubeconfig文件通常更方便。

你可以这样做。

bash
$ export KUBECONFIG=${PWD}/kubeconfig
$ kubectl get pods

优秀的!

部署一个应用程序
让我们部署一个需要1GB内存和250m*CPU的应用程序。
Note: m = thousandth of a core, so 250m = 25% of the CPU

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: podinfo
spec:
 replicas: 1
 selector:
   matchLabels:
     app: podinfo
 template:
   metadata:
     labels:
       app: podinfo
   spec:
     containers:
       - name: podinfo
         image: stefanprodan/podinfo
         ports:
           - containerPort: 9898
         resources:
           requests:
             memory: 1G
             cpu: 250m

你可以通过以下方式将资源提交给集群。

bash
$ kubectl apply -f podinfo.yaml

一旦你这样做,你可能会注意到一些事情。首先,有三个豆荚几乎立即运行,还有一个正在等待。

图中显示一个节点上有三个活跃的pod,以及该节点外的一个待定pod。

然后。

  • 几分钟后,自动分配器创建一个额外的节点;以及
  • 第四个pod被部署在新节点上。
图中显示一个节点上有三个pod,第四个pod部署到一个新的节点上。
最终,第四个pod被部署到一个新的节点。

为什么第四个pod没有部署在第一个节点上? 让我们来挖掘一下可分配的资源。

Kubernetes节点中可分配的资源

部署在Kubernetes集群中的Pod会消耗内存、CPU和存储资源。

然而,在同一个节点上,操作系统和Kubelet需要内存和CPU

在Kubernetes工作节点中,内存和CPU被划分为。

  1. 运行操作系统和系统守护程序(如SSH、systemd等)所需的资源。
  2. 运行Kubernetes代理的必要资源,如Kubelet、容器运行时间、节点问题检测器等。
  3. 平台可用的资源。
  4. 为驱逐门槛保留的资源。
Kubernetes节点中分配和保留的资源,包括:1.驱逐阈值;2.留给pod的内存和CPU;3.留给kubelet的内存和CPU;4.留给OS的内存和CPU
在一个Kubernetes节点中分配和保留的资源。

如果你的集群运行一个DaemonSet,如kube-proxy,你应该进一步减少可用内存和CPU。

因此,让我们降低要求,确保所有的pod都能装进一个节点。

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: podinfo
spec:
 replicas: 4
 selector:
   matchLabels:
     app: podinfo
 template:
   metadata:
     labels:
       app: podinfo
   spec:
     containers:
       - name: podinfo
         image: stefanprodan/podinfo
         ports:
           - containerPort: 9898
         resources:
           requests:
             memory: 0.8G # <- lower memory
             cpu: 200m    # <- lower CPU

你可以通过以下方式修改部署。

bash
$ kubectl apply -f podinfo.yaml

选择正确的CPU和内存数量来优化你的实例可能是很棘手的。Learnk8s工具计算器可能会帮助你更快地完成这项工作。

你解决了一个问题,但创建一个新节点的时间呢?

迟早有一天,你会有四个以上的副本。你真的要在新豆荚创建之前等待几分钟吗?

简短的回答是肯定的。

Linode必须从头开始创建一个虚拟机,配置它,并将其连接到集群。这个过程可能很容易就会超过两分钟。

但是有一个替代方案。

你可以在需要时主动创建已经配置好的节点

例如:你可以将autoscaler配置为总是有一个备用节点。当pods被部署在备用节点上时,autoscaler可以主动地创建更多。不幸的是,autoscaler没有这个内置功能,但你可以很容易地重新创建它。

你可以创建一个pod,它的请求等于节点的资源。

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: overprovisioning
spec:
 replicas: 1
 selector:
   matchLabels:
     run: overprovisioning
 template:
   metadata:
     labels:
       run: overprovisioning
   spec:
     containers:
       - name: pause
         image: k8s.gcr.io/pause
         resources:
           requests:
             cpu: 900m
             memory: 3.8G

你可以通过以下方式将资源提交给集群。

bash
kubectl apply -f placeholder.yaml

这个吊舱完全没有作用。

图中显示了一个占位器pod是如何被用来保护节点上的所有资源的。
一个占位的pod被用来保护节点上的所有资源。

它只是让节点完全被占用。

下一步是确保一旦有需要扩展的工作负载时,占位者pod就被驱逐出去。

为此,你可以使用一个优先级类

yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
 name: overprovisioning
value: -1
globalDefault: false
description: "Priority class used by overprovisioning."
---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: overprovisioning
spec:
 replicas: 1
 selector:
   matchLabels:
     run: overprovisioning
 template:
   metadata:
     labels:
       run: overprovisioning
   spec:
     priorityClassName: overprovisioning # <--
     containers:
       - name: pause
         image: k8s.gcr.io/pause
         resources:
           requests:
             cpu: 900m
             memory: 3.8G

并将其重新提交给集群,并附上。

bash
kubectl apply -f placeholder.yaml

现在设置已经完成。

你可能需要等待一下,让自动缩放器创建节点,但在这一点上,你应该有两个节点。

  1. 一个有四个豆荚的节点。
  2. 另一个有占位器的吊舱。

当你将部署规模扩大到5个副本时会发生什么?你是否需要等待自动缩放器创建一个新的节点?

让我们来测试一下。

bash
kubectl scale deployment/podinfo --replicas=5

你应该观察。

  1. 第五个吊舱立即被创建,不到10秒就进入了运行状态。
  2. 占位的吊舱被驱逐,以便为该吊舱腾出空间。
显示占位器吊舱如何被驱逐以腾出空间给常规吊舱的图示。
占位器豆荚被驱逐,以便为常规豆荚腾出空间。

然后。

  1. 集群的自动调节器注意到了这个待定的占位荚,并配置了一个新的节点。
  2. 占位者pod被部署在新创建的节点中。
图中显示了挂起的吊舱如何触发创建新节点的集群自动调节器。
挂起的pod触发了集群的autoscaler,创建了一个新的节点。

当你可以拥有更多的节点时,为什么要主动地创建一个单一的节点?

你可以将占位者的pod扩展到几个副本。每个副本将预先提供一个Kubernetes节点,准备接受标准工作负载。然而,这些节点仍然计入你的云账单,但却闲置着,什么也不做。因此,你应该小心,不要创建太多的节点。

将集群自动调节器与水平吊舱自动调节器相结合

为了理解这项技术的含义,让我们把集群自动调节器和水平荚自动调节器(HPA)结合起来。HPA是为了增加你部署中的复制。

随着你的应用程序收到更多的流量,你可以让自动缩放器调整复制的数量,以处理更多的请求。

当pod用尽所有可用的资源时,集群的autoscaler将触发创建一个新的节点,以便HPA可以继续创建更多的副本。

让我们通过创建一个新的集群来测试一下。

bash
$ linode-cli lke cluster-create \
 --label learnk8s-hpa \
 --region eu-west \
 --k8s_version 1.23 \
 --node_pools.count 1 \
 --node_pools.type g6-standard-2 \
 --node_pools.autoscaler.enabled enabled \
 --node_pools.autoscaler.max 10 \
 --node_pools.autoscaler.min 3 \
 
$ linode-cli lke kubeconfig-view "insert cluster id here" --text | tail +2 | base64 -d > kubeconfig-hpa

你可以用以下方法验证安装是否成功。

bash
$ kubectl get pods -A --kubeconfig=kubeconfig-hpa

用环境变量导出kubeconfig文件更方便。

你可以这样做。

bash
$ export KUBECONFIG=${PWD}/kubeconfig-hpa
$ kubectl get pods

优秀的!

让我们用Helm来安装Prometheus ,并从部署中刮取指标。
你可以在他们的官方网站上找到关于如何安装Helm的说明。

bash
$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
$ helm install prometheus prometheus-community/prometheus

Kubernetes为HPA提供了一个控制器,可以动态地增加和减少副本。

不幸的是,HPA有一些缺点。

  1. 它并不是开箱即用的。你需要安装一个指标服务器来汇总和公开这些指标。
  2. 你不能使用开箱即用的PromQL查询。

幸运的是,你可以使用KEDA,它扩展了HPA控制器的一些额外功能(包括从Prometheus 读取指标)。

KEDA是一个由三部分组成的自动恒温器

  • 缩放器
  • 一个指标适配器
  • A 控制器
显示KEDA架构的图示
KEDA建筑。

你可以用Helm安装KEDA。

bash
$ helm repo add kedacore https://kedacore.github.io/charts
$ helm install keda kedacore/keda

现在,Prometheus 和KEDA已经安装完毕,让我们来创建一个部署。

在这个实验中,你将使用一个设计为每秒处理固定数量请求的应用程序。 

每个pod每秒钟最多可以处理10个请求。如果pod收到第11个请求,它将把该请求留待以后再处理。

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: podinfo
spec:
 replicas: 4
 selector:
   matchLabels:
     app: podinfo
 template:
   metadata:
     labels:
       app: podinfo
     annotations:
       prometheus.io/scrape: "true"
   spec:
     containers:
       - name: podinfo
         image: learnk8s/rate-limiter:1.0.0
         imagePullPolicy: Always
         args: ["/app/index.js", "10"]
         ports:
           - containerPort: 8080
         resources:
           requests:
             memory: 0.9G
---
apiVersion: v1
kind: Service
metadata:
 name: podinfo
spec:
 ports:
   - port: 80
     targetPort: 8080
 selector:
   app: podinfo

你可以通过以下方式将资源提交给集群。

bash
$ kubectl apply -f rate-limiter.yaml

为了产生一些流量,你将使用Locust

下面的YAML定义创建了一个分布式负载测试集群。

yaml
apiVersion: v1
kind: ConfigMap
metadata:
 name: locust-script
data:
 locustfile.py: |-
   from locust import HttpUser, task, between
 
   class QuickstartUser(HttpUser):
       @task
       def hello_world(self):
           self.client.get("/", headers={"Host": "example.com"})
---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: locust
spec:
 selector:
   matchLabels:
     app: locust-primary
 template:
   metadata:
     labels:
       app: locust-primary
   spec:
     containers:
       - name: locust
         image: locustio/locust
         args: ["--master"]
         ports:
           - containerPort: 5557
             name: comm
           - containerPort: 5558
             name: comm-plus-1
           - containerPort: 8089
             name: web-ui
         volumeMounts:
           - mountPath: /home/locust
             name: locust-script
     volumes:
       - name: locust-script
         configMap:
           name: locust-script
---
apiVersion: v1
kind: Service
metadata:
 name: locust
spec:
 ports:
   - port: 5557
     name: communication
   - port: 5558
     name: communication-plus-1
   - port: 80
     targetPort: 8089
     name: web-ui
 selector:
   app: locust-primary
 type: LoadBalancer
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
 name: locust
spec:
 selector:
   matchLabels:
     app: locust-worker
 template:
   metadata:
     labels:
       app: locust-worker
   spec:
     containers:
       - name: locust
         image: locustio/locust
         args: ["--worker", "--master-host=locust"]
         volumeMounts:
           - mountPath: /home/locust
             name: locust-script
     volumes:
       - name: locust-script
         configMap:
           name: locust-script

你可以用提交给集群。

bash
$ kubectl locust.yaml

蝗虫》的内容如下 locustfile.py,它被存储在一个ConfigMap中。

py
from locust import HttpUser, task, between
 
class QuickstartUser(HttpUser):
 
   @task
   def hello_world(self):
       self.client.get("/")

该文件除了向一个URL发出请求外,并没有做任何特别的事情。要连接到Locust仪表板,你需要它的负载平衡器的IP地址。

你可以用下面的命令检索它。

bash
$ kubectl get service locust -o jsonpath='{.status.loadBalancer.ingress[0].ip}'

打开你的浏览器并输入该IP地址。

优秀的!

缺少了一块:Horizontal Pod Autoscaler。
KEDA的Autoscaler用一个叫ScaledObject的特定对象来包装Horizontal Autoscaler。

yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: podinfo
spec:
scaleTargetRef:
  kind: Deployment
  name: podinfo
minReplicaCount: 1
maxReplicaCount: 30
cooldownPeriod: 30
pollingInterval: 1
triggers:
- type: prometheus
  metadata:
    serverAddress: http://prometheus-server
    metricName: connections_active_keda
    query: |
      sum(increase(http_requests_total{app="podinfo"}[60s]))
    threshold: "480" # 8rps * 60s

KEDA连接了由Prometheus 收集的指标,并将其反馈给Kubernetes。

最后,它用这些指标创建一个Horizontal Pod Autoscaler(HPA)。

你可以通过以下方式手动检查HPA。

bash
$ kubectl get hpa
$ kubectl describe hpa keda-hpa-podinfo

你可以用提交对象。

bash
$ kubectl apply -f scaled-object.yaml

现在是测试缩放是否有效的时候了。

在Locust仪表板上,用以下设置启动一个实验。

  • 用户的数量。 300
  • 产卵率。 0.4
  • 主持人。 http://podinfo
屏幕记录的Gif,展示了使用自动缩放器的待定荚的缩放。
结合集群和水平吊舱的自动调节器。

复制品的数量正在增加!

优秀的!但你注意到了吗?

在部署扩展到8个pod后,必须等待几分钟才能在新节点上创建更多pod。

在这个时期,每秒的请求量停滞不前,因为目前的8个副本只能处理每个10个请求。

让我们缩小规模,重复这个实验。

bash
kubectl scale deployment/podinfo --replicas=4 # or wait for the autoscaler to remove pods

这一次,让我们用占位者的pod来过度配置节点。

yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
 name: overprovisioning
value: -1
globalDefault: false
description: "Priority class used by overprovisioning."
---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: overprovisioning
spec:
 replicas: 1
 selector:
   matchLabels:
     run: overprovisioning
 template:
   metadata:
     labels:
       run: overprovisioning
   spec:
     priorityClassName: overprovisioning
     containers:
       - name: pause
         image: k8s.gcr.io/pause
         resources:
           requests:
             cpu: 900m
             memory: 3.9G

你可以用提交给集群。

bash
kubectl apply -f placeholder.yaml

打开Locust仪表板,用以下设置重复实验。

Gif
将集群和水平吊舱自动调节器与超额配置相结合。

这一次,新的节点是在后台创建的,每秒的请求数增加了,但没有变平。干得好!

让我们回顾一下你在这篇文章中学到的东西。

  • 集群自动调节器不跟踪CPU或内存消耗。相反,它监测待处理的pod。
  • 你可以创建一个pod,使用可用的总内存和CPU来主动配置一个Kubernetes节点。
  • Kubernetes节点为kubelet、操作系统和驱逐门槛保留了资源;以及
  • 你可以把Prometheus 和KEDA结合起来,用PromQL查询来扩展你的pod。

想跟随我们的Scaling Kubernetes系列网络研讨会吗?注册开始,并了解更多关于使用KEDA将Kubernetes集群扩展到零的信息。


注释

留下回复

您的电子邮件地址将不会被公布。 必须填写的字段被标记为*