KEDA:基于事件驱动扩展K8S应用的深度实践

KEDA:基于事件驱动扩展K8S应用的深度实践

作者:祝祥 2023-11-02 08:01:22

云计算

云原生 KEDA 提供了一个类似于 FaaS 的事件感知扩展模型,在这种模型中,Kubernetes 部署可以基于需求和基于智能动态地从零扩展,而不会丢失数据和上下文。

为什么我们要自动扩展应用程序?

作为 SRE,需要保证应用弹性和高可用性。因此,自动缩放是我们需要的必须功能。通过自动缩放,我们能确保工作负载能够高效的地处理业务流量。

在本文中,我们将详细描述如何使用 KEDA 以事件驱动的方式自动扩展 Kubernetes 应用程序。

什么是KEDA?

KEDA 是一个轻量级的开源 Kubernetes 事件驱动的自动缩放器,DevOps、SRE 和 Ops 团队使用它来根据外部事件或触发器水平扩展 Pod。KEDA 有助于扩展本机 Kubernetes 自动缩放解决方案的功能,这些解决方案依赖于标准资源指标,如 CPU 或内存。我们可以将 KEDA 部署到 Kubernetes 集群中,并使用自定义资源定义 (CRD) 管理 Pod 的扩展。

KEDA 基于 Kubernetes HPA 构建,根据来自 AWS SQS、Kafka、RabbitMQ 等事件源的信息扩展 Pod。这些事件源使用缩放程序进行监视,缩放程序根据为其设置的规则激活或停用部署。KEDA 缩放器还可以为特定事件源提供自定义指标,帮助 DevOps 团队观察与其相关的指标

我们唯一要做的就是通过选择要用于自动扩展应用程序的缩放器以及一些参数来配置 ScaledObject (KEDA CRD),KEDA 将完成剩下的工作:

  • 监视事件源
  • 创建和管理 HPA 生命周期

截至目前,有 62 个内置缩放器和 4 个外部缩放器可用。 KEDA 之所以好,是因为使用轻量级组件以及原生 Kubernetes 组件,例如 HorizontalPodAutoscaler ,更重要的是“即插即用”。

部署KEDA

那么,部署 KEDA 的最简单方法是使用官方 Helm,安装如下所示:

helm repo add kedacore 
helm repo update
helm install keda kedacore/keda --namespace keda --create-namespace

️ 如果使用 ArgoCD 部署 KEDA,您可能会遇到有关 CRD 注释长度的问题。我们可以使用选项 ServerSideApply=true 来解决 template.sped.syncPolicy.syncOptions 。此外,还可以在 helm中设置参数,从而 禁用 CRD 部署,当然,这种情况下也必须找到另一种方法来部署 KEDA CRD。

基于本机Cron Scaler自动缩放Web应用

下面让我们深度实践一下KEDA!

部署我们的Web应用

对于demo,将使用一个建的 Golang Web 应用程序。使用以下清单部署:


---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: go-helloworld
  name: go-helloworld
spec:
  selector:
    matchLabels:
      app: go-helloworld
  template:
    metadata:
      labels:
        app: go-helloworld
    spec:
      containers:
        - image: rg.fr-par.scw.cloud/novigrad/go-helloworld:0.1.0
          name: go-helloworld
          resources:
            requests:
              cpu: "50m"
              memory: "64Mi"
            limits:
              memory: "128Mi"
              cpu: "100m"
---
apiVersion: v1
kind: Service
metadata:
  name: go-helloworld
spec:
  selector:
    app: go-helloworld
  ports:
    - protocol: TCP
      port: 8080
      name: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt
  name: go-helloworld
spec:
  rules:
  - host: helloworld.jourdain.io
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: go-helloworld
            port:
              number: 8080
  tls: # < placing a host in the TLS config will indicate a certificate should be created
  - hosts:
    - helloworld.jourdain.io
    secretName: go-helloworld-tls-cert

将KEDA配置为仅在工作时间自动扩展Web应用

如果,我们希望我们的应用程序仅在工作时间可用。至于为何有如此要求,下面是一些场景。例如,在开发环境中,不一定需要保持应用程序24小时启动和运行。在云环境中,它可以为您节省大量成本,具体取决于用户实际环境的应用程序/计算实例的数量。 为了实现这一点,我们将使用KEDA的原生Cron scaler。由于 Cron scaler 支持 Linux 格式的 cron,它甚至允许我们在工作日扩展我们的应用程序 要配置 Cron scaler,我们将按如下方式使用 [ScaledObject] CRD:


apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: go-helloworld
spec:
  scaleTargetRef:
    name: go-helloworld
  triggers:
  - type: cron
    metadata:
      timezone: Europe/Paris
      start: 00 08 * * 1-5
      end: 00 18 * * 1-5
      desiredReplicas: "2"

️ ScaledObject 必须与应用程序位于同一命名空间中!让我们深入了解一下这个配置:

  • spec.scaleTargetRef 是 Kubernetes Deployment/StatefulSet 或其他自定义资源的引用

name (必填): Kubernetes 资源的名称

kind (可选):Kubernetes 资源的种类,默认值为 Deployment

  • spec.triggers 是用于激活目标资源缩放的触发器列表
  • type (必填):缩放器名称
  • metadata (必需):Cron 缩放器所需的配置参数,使用此配置,我的应用程序将在周一到周五的一周中每天的 08:00 到 18:00 之间启动并运行两个副本。

基于HTTP事件自动缩放Web应用

(使用 KEDA HTTP 外部缩放器)

借助所有 KEDA 的缩放器,我们可以通过多种方式自动扩展 Web 应用程序,例如,基于 AMQP 队列中的消息进行缩放应用。

目前,我们了解了 KEDA 的工作原理。我下面们将探讨 KEDA 如何通过基于 HTTP 事件自动扩展应用程序来帮助我们处理流量高峰。为此,我们有两个选择:

  • 使用Prometheus scaler
  • 使用 KEDA HTTP 外部缩放器,它的工作方式类似于附加组件,由于演示集群上没有安装 Prometheus,因此我们将使用 KEDA HTTP 外部缩放器。

KEDA HTTP插件目前处于测试阶段。它主要由KEDA团队维护。

解决方案概述

KEDA HTTP scaler是构建在 KEDA 核心之上的附加组件,它拥有自己的组件:operator, scaler和 interceptor。如果你想进一步了解它们的作用,请阅读官方文档。总之,为了帮助大家更好地理解它的工作原理,下面提供一个小案例:

图片

安装KEDA HTTP附加组件

由于这个缩放器不是内置的,我们必须手工安装。根据官方文档的说明,我们可以使用 Helm 来进行安装:

helm install http-add-on kedacore/keda-add-ons-http --namespace keda

如果安装顺利,我们应该会看到以下pod:

 k get pods -l app=keda-add-ons-http -o name
pod/keda-add-ons-http-controller-manager-5c8d895cff-7jsl8
pod/keda-add-ons-http-external-scaler-57889786cf-r45lj
pod/keda-add-ons-http-interceptor-5bf6756df9-wwff8
pod/keda-add-ons-http-interceptor-5bf6756df9-x8l58
pod/keda-add-ons-http-interceptor-5bf6756df9-zxvw

为我们的Web应用配置’HTTPScaledObject’

正如之前所说,KEDA HTTP 附加组件自带组件,包括操作符,这也意味着它自带 CRD。HTTPScaledObject 是由 KEDA HTTP 附加组件管理的 CRD。这就是我们在这里需要配置的。让我们为 Web 应用程序创建 HTTPScaledObject 资源: ️ HTTPScaleObject 必须在与 Web 应用相同的命名空间中创建资源!

kind: HTTPScaledObject
apiVersion: http.keda.sh/v1alpha1
metadata:
    name: go-helloworld
spec:
    host: "helloworld.jourdain.io"
    targetPendingRequests: 10
    scaledownPeriod: 300
    scaleTargetRef:
        deployment: go-helloworld
        service: go-helloworld
        port: 8080
    replicas:
        min: 0
        max: 10

在这里,我们已经配置了我们的 HTTPScaledObject 应用程序,以便将我们的应用程序 Deployment 从 0 个副本扩展到 10 个副本。因为,如果拦截器上有 10 个请求处于挂起状态(应用程序尚未接收的请求),则 KEDA 将添加一个 pod。

调整我们的Web应用程序的service和ingress

仔细观察一下上面的图,可以看到我们的 Web 应用程序 ingress 需要引用 KEDA HTTP 附加组件的拦截器服务,而不是 Web 应用程序的拦截器服务。由于 ingress 无法引用另一个命名空间中的服务,因此我们将在与 Web 应用相同的命名空间 external 中创建类型服务,该服务引用来自 keda 命名空间的拦截器服务:

kind: Service
apiVersion: v1
metadata:
  name: keda-add-ons-http-interceptor-proxy
spec:
  type: ExternalName
  externalName: keda-add-ons-http-interceptor-proxy.keda.svc.cluster.local

现在,我们需要重新配置 Web 应用的入口,使其引用新创建的服务:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt
  name: go-helloworld
spec:
  rules:
  - host: helloworld.jourdain.io
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: keda-add-ons-http-interceptor-proxy
            port:
              number: 8080
  tls: # < placing a host in the TLS config will indicate a certificate should be created
  - hosts:
    - helloworld.jourdain.io
    secretName: go-helloworld-tls-cert

️ 需要输入新服务的名称,但请注意端口,该端口也被拦截器的服务所取代

让我们测试一下

为了保证我们的配置正常,本处将使用 [k6]工具 ,这是一个负载测试工具。如果想了解有关k6的更多信息,以下是Padok博客中的一些介绍:

  • [How to do distributed load testing using K6 & Kubernetes?] (https://www.padok.fr/en/blog/k6-load-testing)
  • [k6 description from the Padok tech radar] (https://www.padok.fr/en/tech-radar-resilient?category=resilient&rank=6)

下面本次使用的 k6 脚本,后续将使用它进行测试:


import { check } from 'k6';
import http from 'k6/http';

export const options = {
  scenarios: {
    constant_request_rate: {
      executor: 'constant-arrival-rate',
      rate: 100,
      timeUnit: '1s', // 100 iterations per second, i.e. 100 RPS
      duration: '30s',
      preAllocatedVUs: 50, // how large the initial pool of VUs would be
      maxVUs: 50, // if the preAllocatedVUs are not enough, we can initialize more
    },
  },
};

export function test(params) {
  const res = http.get('');
  check(res, {
    'is status 200': (r) => r.status === 200,
  });
}

export default function () {
  test();
}

首先,让我们看看 100 个 RPS 会发生什么:


 k6 run k6/script.js

          /\\      |‾‾| /‾‾/   /‾‾/
     /\\  /  \\     |  |/  /   /  /
    /  \\/    \\    |     (   /   ‾‾\\
   /          \\   |  |\\  \\ |  (‾)  |
  / __________ \\  |__| \\__\\ \\_____/ .io

  execution: local
     script: k6/script.js
     output: -

  scenarios: (100.00%) 1 scenario, 50 max VUs, 1m0s max duration (incl. graceful stop):
           * constant_request_rate: 100.00 iterations/s for 30s (maxVUs: 50, gracefulStop: 30s)


      is status 200

     checks.........................: 100.00%  3001       0
     data_received..................: 845 kB  28 kB/s
     data_sent......................: 134 kB  4.5 kB/s
     http_req_blocked...............: avg=792.54µs min=0s     med=1µs     max=137.85ms p(90)=2µs     p(95)=2µs
     http_req_connecting............: avg=136.6µs  min=0s     med=0s      max=17.67ms  p(90)=0s      p(95)=0s
     http_req_duration..............: avg=11.38ms  min=7.68ms med=10.68ms max=100.96ms p(90)=12.78ms p(95)=14.33ms
       { expected_response:true }...: avg=11.38ms  min=7.68ms med=10.68ms max=100.96ms p(90)=12.78ms p(95)=14.33ms
     http_req_failed................: 0.00%    0          3001
     http_req_receiving.............: avg=89.68µs  min=8µs    med=64µs    max=6.35ms   p(90)=112µs   p(95)=134µs
     http_req_sending...............: avg=152.31µs min=14µs   med=137µs   max=2.57ms   p(90)=274µs   p(95)=313µs
     http_req_tls_handshaking.......: avg=587.62µs min=0s     med=0s      max=74.46ms  p(90)=0s      p(95)=0s
     http_req_waiting...............: avg=11.14ms  min=7.62ms med=10.48ms max=100.92ms p(90)=12.47ms p(95)=13.96ms
     http_reqs......................: 3001    99.983105/s
     iteration_duration.............: avg=12.37ms  min=7.73ms med=10.88ms max=194.89ms p(90)=13.07ms p(95)=14.99ms
     iterations.....................: 3001    99.983105/s
     vus............................: 1       min=1       max=1
     vus_max........................: 50      min=50      max=50


running (0m30.0s), 00/50 VUs, 3001 complete and 0 interrupted iterations
constant_request_rate  [======================================] 00/50 VUs  30s  100.00 iters/s

如果您想实时查看拦截器队列中有多少请求,可以在两个终端窗格中启动以下命令:

 kubectl proxy
Starting to serve on 127.0.0.1:8001

以及:

 watch -n '1' curl --silent localhost:8001/api/v1/namespaces/keda/services/keda-add-ons-http-interceptor-admin:9090/proxy/queue
{"default/go-helloworld":0}

在 100 RPS 测试中,应用程序没有纵向扩展,因为拦截器队列中的挂起请求数不超过 1。提醒一下,我们配置为 targetPendingRequests 10 。所以一切都很正常 下面让我们将 RPS x10 ,看看会发生什么:

 k6 run k6/script.js
          /\\      |‾‾| /‾‾/   /‾‾/     /\\  /  \\     |  |/  /   /  /    /  \\/    \\    |     (   /   ‾‾\\   /          \\   |  |\\  \\ |  (‾)  |  / __________ \\  |__| \\__\\ \\_____/ .io
  execution: local     script: k6/script.js     output: -
  scenarios: (100.00%) 1 scenario, 50 max VUs, 1m0s max duration (incl. graceful stop):           * constant_request_rate: 1000.00 iterations/s for 30s (maxVUs: 50, gracefulStop: 30s)
      is status 200      ↳  99% —  11642 /  2
     checks.........................: 99.98%  11642       2     data_received..................: 2.6 MB 86 kB/s     data_sent......................: 446 kB 15 kB/s     dropped_iterations.............: 18356  611.028519/s     http_req_blocked...............: avg=1.07ms   min=0s     med=0s      max=408.06ms p(90)=1µs      p(95)=1µs     http_req_connecting............: avg=43.12µs  min=0s     med=0s      max=11.05ms  p(90)=0s       p(95)=0s     http_req_duration..............: avg=120.09ms min=8.14ms med=74.77ms max=6.87s    p(90)=189.49ms p(95)=250.21ms       { expected_response:true }...: avg=120.01ms min=8.14ms med=74.76ms max=6.87s    p(90)=189.41ms p(95)=249.97ms     http_req_failed................: 0.01%   2           11642     http_req_receiving.............: avg=377.61µs min=5µs    med=32µs    max=27.32ms  p(90)=758.1µs  p(95)=2.49ms     http_req_sending...............: avg=61.57µs  min=9µs    med=45µs    max=9.99ms   p(90)=102µs    p(95)=141µs     http_req_tls_handshaking.......: avg=626.79µs min=0s     med=0s      max=297.82ms p(90)=0s       p(95)=0s     http_req_waiting...............: avg=119.65ms min=7.95ms med=74.32ms max=6.87s    p(90)=188.95ms p(95)=249.76ms     http_reqs......................: 11644  387.60166/s     iteration_duration.............: avg=121.26ms min=8.32ms med=74.87ms max=7.07s    p(90)=189.62ms p(95)=250.28ms     iterations.....................: 11644  387.60166/s     vus............................: 44     min=25       max=50     vus_max........................: 50     min=50       max=50

running (0m30.0s), 00/50 VUs, 11644 complete and 0 interrupted iterationsconstant_request_rate  [======================================] 00/50 VUs  30s  1000.00 iters/s

结果还不错,我们有两个请求 KO ,这是因为应用程序冷启动(从 0 开始),每个请求等待的时间不超过 1/2 秒。以下是部署历史记录:

 k get deployments.apps -w
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
go-helloworld   0/0     0            0           36m
go-helloworld   0/1     0            0           36m
go-helloworld   1/1     1            1           36m
go-helloworld   1/4     1            1           36m
go-helloworld   2/4     4            2           36m
go-helloworld   3/4     4            3           36m
go-helloworld   4/4     4            4           36m
go-helloworld   4/5     4            4           37m
go-helloworld   5/5     5            5           37m

应用程序从 0 个副本扩展到 5 个副本;直到 Web 应用程序的挂起请求数少于 10。

缩放指令非常快,应用程序很快达到了 5 个副本。

以下是 100 RPS 和 1k RPS 测试之间 http_req_duration k6 指标的一些对比:

# 100 RPS
http_req_duration: avg=11.38ms  min=7.68ms med=10.68ms max=100.96ms p(90)=12.78ms p(95)=14.33ms

# 1k RPS
http_req_duration: avg=120.09ms min=8.14ms med=74.77ms max=6.87s    p(90)=189.49ms p(95)=250.21ms

根据我们的需求(SLO,SLA等),我们也许可以稍微调整一下 Web应用程序的 targetPendingRequestsHTTPScaledObject 参数 。

缩放到零!

通过本文介绍的两个案例,我们已经验证过了如何缩放到零的场景。但是,大家真的知道它是如何工作的吗?

由于 KEDA 根据事件自动缩放应用程序,因此从收到事件的那一刻起,KEDA 会将应用程序缩放到其最小副本。例如,如果我们以 HTTP 附加组件为例,KEDA 将在第一次收到请求时扩展到最小副本。

总结

KEDA 提供了一个类似于 FaaS 的事件感知扩展模型,在这种模型中,Kubernetes 部署可以基于需求和基于智能动态地从零扩展,而不会丢失数据和上下文。在业务请求量上来后,应用程序将进行自动化的扩容,当业务低谷的时候,则会自动的缩容。这可以在缓解很多生产环境下的手动扩/缩容操作,以保障用户的服务体验。

KEDA 还为 Kubernetes 带来了更多的事件源。随着未来更多触发器的加入,KEDA 有很大的潜力成为生产级 Kubernetes 部署的必需品,从而使应用程序自动缩放成为应用程序开发中的嵌入式组件。

文章来源网络,作者:运维,如若转载,请注明出处:https://shuyeidc.com/wp/250995.html<

(0)
运维的头像运维
上一篇2025-04-28 18:39
下一篇 2025-04-28 18:41

相关推荐

  • 个人主题怎么制作?

    制作个人主题是一个将个人风格、兴趣或专业领域转化为视觉化或结构化内容的过程,无论是用于个人博客、作品集、社交媒体账号还是品牌形象,核心都是围绕“个人特色”展开,以下从定位、内容规划、视觉设计、技术实现四个维度,详细拆解制作个人主题的完整流程,明确主题定位:找到个人特色的核心主题定位是所有工作的起点,需要先回答……

    2025-11-20
    0
  • 社群营销管理关键是什么?

    社群营销的核心在于通过建立有温度、有价值、有归属感的社群,实现用户留存、转化和品牌传播,其管理需贯穿“目标定位-内容运营-用户互动-数据驱动-风险控制”全流程,以下从五个维度展开详细说明:明确社群定位与目标社群管理的首要任务是精准定位,需明确社群的核心价值(如行业交流、产品使用指导、兴趣分享等)、目标用户画像……

    2025-11-20
    0
  • 香港公司网站备案需要什么材料?

    香港公司进行网站备案是一个涉及多部门协调、流程相对严谨的过程,尤其需兼顾中国内地与香港两地的监管要求,由于香港公司注册地与中国内地不同,其网站若主要服务内地用户或使用内地服务器,需根据服务器位置、网站内容性质等,选择对应的备案路径(如工信部ICP备案或公安备案),以下从备案主体资格、流程步骤、材料准备、注意事项……

    2025-11-20
    0
  • 如何企业上云推广

    企业上云已成为数字化转型的核心战略,但推广过程中需结合行业特性、企业痛点与市场需求,构建系统性、多维度的推广体系,以下从市场定位、策略设计、执行落地及效果优化四个维度,详细拆解企业上云推广的实践路径,精准定位:明确目标企业与核心价值企业上云并非“一刀切”的方案,需先锁定目标客户群体,提炼差异化价值主张,客户分层……

    2025-11-20
    0
  • PS设计搜索框的实用技巧有哪些?

    在PS中设计一个美观且功能性的搜索框需要结合创意构思、视觉设计和用户体验考量,以下从设计思路、制作步骤、细节优化及交互预览等方面详细说明,帮助打造符合需求的搜索框,设计前的规划明确使用场景:根据网站或APP的整体风格确定搜索框的调性,例如极简风适合细线条和纯色,科技感适合渐变和发光效果,电商类则可能需要突出搜索……

    2025-11-20
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注