跳到主要内容
博客容器(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服务。

KEDA对Kubernetes的自动扩展策略。传入的流量在到达KubernetesAPI 服务器之前到达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网络研讨会系列


评论 (2)

  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.

留下回复

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