跳到主要内容
博客容器(Kubernetes, Docker)将Kubernetes扩展到零(并返回)。

将 Kubernetes 缩容至零(再扩展到原样)。

扩缩 Kubernetes 映像

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

降低基础设施成本归根结底是在不使用资源时将其关闭。然而,挑战在于如何在必要时自动打开这些资源。让我们通过必要的步骤,使用Linode Kubernetes Engine(LKE)部署一个Kubernetes集群,并使用Kubernetes Events-Driven Autoscaler(KEDA)来扩展到零,再返回。

为什么要实现 "零规模"?

让我们想象一下,你在Kubernetes上运行一个合理的资源密集型的应用程序,而且只在工作时间需要它。

你可能想在人们离开办公室时把它关掉,在他们开始一天的工作时再打开。

将Kubernetes扩展为零,用于只在工作时间需要的开发工作负载,而不是需要全天候运行的生产工作负载。
如果没有人使用你的开发环境,你可能会想关闭它!

虽然你可以使用CronJob来扩大和缩小实例,但这种解决方案是一种权宜之计,只能按预先设定的时间表运行。

周末期间会发生什么?公共假期又如何?或者当团队生病的时候?

你可以根据流量来扩展你的工作负载,而不是生成一个不断增长的规则列表。当流量增加时,你可以扩大副本的规模。如果没有流量,你可以把应用关掉。如果应用程序被关闭,有新的传入请求,Kubernetes将至少启动一个副本来处理流量。

缩放Kubernetes图--只在有活动流量时才缩放和使用资源。
将应用程序扩展到零,以帮助节省资源。

接下来,让我们来谈谈如何:

  • 拦截到你的应用程序的所有流量;
  • 监测交通;以及
  • 设置自动缩放器来调整复制的数量或关闭应用程序。

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

创建一个群集

让我们从创建一个Kubernetes集群开始。

以下命令可以用来创建集群并保存kubeconfig文件。

bash
$ 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

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

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

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

你可以这样做。

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

现在我们来部署一个应用程序。

部署一个应用程序

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: podinfo
spec:
 selector:
   matchLabels:
     app: podinfo
 template:
   metadata:
     labels:
       app: podinfo
   spec:
     containers:
     - name: podinfo
       image: stefanprodan/podinfo
       ports:
       - containerPort: 9898
---
apiVersion: v1
kind: Service
metadata:
 name: podinfo
spec:
 ports:
   - port: 80
     targetPort: 9898
 selector:
   app: podinfo

你可以用提交YAML文件:

terminal|command=1|title=bash
$ kubectl apply -f 1-deployment.yaml

而你可以用访问该应用程序:

打开你的浏览器到localhost:8080

bash
$ kubectl port-forward svc/podinfo 8080:80

在这一点上,你应该看到该应用程序。

浏览器中的podinfo应用程序的屏幕截图。

接下来,让我们安装KEDA--自动缩放器。

KEDA - Kubernetes事件驱动的自动调节器

Kubernetes提供了Horizontal Pod Autoscaler(HPA)作为控制器来动态地增加和减少复制。

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

  1. 它不是开箱即用的--你需要安装一个指标服务器来汇总和公开指标。
  2. 它不能扩展到零副本
  3. 它根据指标来扩展副本,并且不拦截HTTP流量。

幸运的是,你不必使用官方的自动缩放器,但你可以使用KEDA代替。

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

  1. 缩放器
  2. 一个指标适配器
  3. A 控制器
显示组件的KEDA架构图。
KEDA建筑

缩放器就像适配器,可以从数据库、消息中介、遥测系统等收集指标。

例如,HTTP Scaler是一个适配器,可以拦截和收集HTTP流量。

你可以在这里找到一个使用 RabbitMQ 的扩展器的例子。

Metrics适配器负责将扩展器收集的指标以Kubernetes度量管道可以使用的格式公开。

最后,控制器将所有的组件粘在一起:

  • 它使用适配器收集指标,并将它们公开给指标 API。
  • 它注册和管理KEDA特定的自定义资源定义(CRD)--即ScaledObject、TriggerAuthentication等。
  • 它代表你创建并管理Horizontal Pod Autoscaler。

这就是理论,但让我们看看它在实践中如何运作。

安装控制器的一个更快方法是使用Helm。

你可以在Helm官方网站上找到安装说明。

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

KEDA默认不带有HTTP扩展器,所以你必须单独安装它:

bash
$ helm install http-add-on kedacore/keda-add-ons-http

在这一点上,你已经准备好扩展应用程序。

定义一个自动缩放策略

KEDA HTTP插件暴露了一个CRD,在这里你可以描述你的应用程序应该如何被扩展。

让我们来看看一个例子:

yaml
kind: HTTPScaledObject
apiVersion: http.keda.sh/v1alpha1
metadata:
   name: podinfo
spec:
   host: example.com
   targetPendingRequests: 100
   scaleTargetRef:
       deployment: podinfo
       service: podinfo
       port: 80
   replicas:
       min: 0
       max: 10

这个文件指示拦截器将对example.com的请求转发给podinfo服务。

Kubernetes 的 KEDA 自动缩放策略。传入流量在到达 Kubernetes API 服务器之前到达 KEDA HTTP 拦截器。
KEDA和HTTP拦截器。

它还包括应该被扩展的部署的名称 - 在这种情况下,podinfo

让我们把YAML提交给集群:

bash
$ kubectl apply -f scaled-object.yaml

一旦你提交了定义,这个豆荚就会被删除!

但为什么呢?

在HTTPScaledObject被创建后,KEDA立即将部署扩展到零,因为没有任何流量。

你必须向应用程序发送HTTP请求来扩展它。

让我们通过连接到服务并发出一个请求来测试一下。

bash
$ kubectl port-forward svc/podinfo 8080:80

命令挂掉了!

这是有道理的;没有豆荚来服务于这个请求。

但为什么Kubernetes不能将部署规模扩大到1?

测试KEDA拦截器

一个名为 "Kubernetes "的服务 keda-add-ons-http-interceptor-proxy 是在你使用Helm安装附加组件时创建的。

为了使自动缩放正常工作,HTTP流量必须首先通过该服务。
你可以使用 kubectl port-forward 来测试它:

shell
$ kubectl port-forward svc/keda-add-ons-http-interceptor-proxy 8080:8080

这一次,你无法在浏览器中访问该网址。

一个KEDA HTTP拦截器可以处理多个部署。

那么,它是如何知道将流量导向何处的呢?

yaml
kind: HTTPScaledObject
apiVersion: http.keda.sh/v1alpha1
metadata:
   name: podinfo
spec:
   host: example.com
   targetPendingRequests: 100
   scaleTargetRef:
       deployment: podinfo
       service: podinfo
       port: 80
   replicas:
       min: 0
       max: 10

HTTPScaledObject有一个host字段,正是用于此。

在这个例子中,假装请求来自example.com。

你可以通过设置主机头来做到这一点:

bash
$ curl localhost:8080 -H 'Host: example.com'

你会收到回复,尽管会有一点延迟。

如果你检查豆荚,你会注意到,部署被扩展到一个副本:

bash
$ kubectl get pods

那么刚才发生了什么?

当你把流量路由到KEDA的服务时,拦截器会跟踪还没有回复的HTTP请求的数量。

KEDA扩展器定期检查拦截器的队列大小,并存储指标。

KEDA控制器监控指标,并根据需要增加或减少副本的数量。在这种情况下,一个请求是待定的--足以让KEDA控制器将部署扩展到一个副本。

你可以通过以下方式获取单个拦截器的待处理HTTP请求队列的状态:

bash
$ 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只有在能被拦截的情况下才能扩大流量。

如果你有一个现有的入口控制器,并希望使用它来转发流量到你的应用程序,你将需要修改入口清单,将流量转发到HTTP附加服务。

让我们来看看一个例子。

将KEDA HTTP插件与Ingress相结合

你可以用Helm安装nginx-ingress控制器:

bash
$ helm upgrade --install ingress-nginx ingress-nginx \
 --repo https://kubernetes.github.io/ingress-nginx \
 --namespace ingress-nginx --create-namespace

我们来写一个入口清单,将流量路由到podinfo:

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
 name: podinfo
spec:
 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:

bash
LB_IP=$(kubectl get services -l "app.kubernetes.io/component=controller" -o jsonpath="{.items[0].status.loadBalancer.ingress
[0].ip}" -n ingress-nginx)

你终于可以向应用程序提出请求了:

bash
curl $LB_IP -H "Host: example.com"

它成功了!

如果你等待的时间足够长,你会注意到,部署的规模最终会变成零。

这与Kubernetes上的Serverless相比有什么不同?

这种设置与Kubernetes上的无服务器框架(如OpenFaaS)之间有几个显著的区别:

  1. 有了KEDA,就不需要重新架构或使用SDK来部署应用程序。
  2. 无服务器框架负责处理路由和服务请求。你只需编写逻辑。
  3. 在KEDA中,部署是常规容器。对于无服务器框架来说,这并不总是真的。

想看看这种扩展的实际情况吗?注册参加我们的扩展Kubernetes网络研讨会系列


评论 (5)

  1. Author Photo

    Very nice tutorial. In the case without the nginx ingress, can you explain how to access from the outside, instead of the localhost? I tried to use a NodePort service, but the port gets closed when the Interceptor is installed. The Interceptor proxy is a ClusterIP service. How can we access it from the outside? Is there any sort of kubectl port forwarding instruction?

    • Maddie Presland

      Hi Rui! I forwarded your question to Daniele and here is his response:

      It should work with NodePort, but you have to set the right header (i.e. Host: example.com ) when you make the request. There is no way for the interceptor to decide where the traffic should go without that.

  2. Author Photo

    Muito bom o conteudo!!!

  3. Author Photo

    Where does NodeBalancer show up in this configuration? Does LKE take over that job?

    • tlambert

      Hi Lee – The NodeBalancer is created during the installation of the nginx-ingress controller. For a more detailed explanation of this process you can check out our guide titled Deploying NGINX Ingress on Linode Kubernetes Engine.

      Once the NodeBalancer is provisioned it is controlled via LKE. We don’t recommend configuring the settings of your LKE NodeBalancers through the Cloud Manager.

留下回复

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