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

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

科技

新钛云服已累计为您分享773篇技术干货





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



作为 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 updatehelm 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/v1kind: Deploymentmetadata:  labels:    app: go-helloworld  name: go-helloworldspec:  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: v1kind: Servicemetadata:  name: go-helloworldspec:  selector:    app: go-helloworld  ports:    - protocol: TCP      port: 8080      name: http---apiVersion: networking.k8s.io/v1kind: Ingressmetadata:  annotations:    kubernetes.io/ingress.class: nginx    cert-manager.io/cluster-issuer: letsencrypt  name: go-helloworldspec:  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/v1alpha1kind: ScaledObjectmetadata:  name: go-helloworldspec:  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 namepod/keda-add-ons-http-controller-manager-5c8d895cff-7jsl8pod/keda-add-ons-http-external-scaler-57889786cf-r45ljpod/keda-add-ons-http-interceptor-5bf6756df9-wwff8pod/keda-add-ons-http-interceptor-5bf6756df9-x8l58pod/keda-add-ons-http-interceptor-5bf6756df9-zxvw


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


正如之前所说,KEDA HTTP 附加组件自带组件,包括操作符,这也意味着它自带 CRD。HTTPScaledObject 是由 KEDA HTTP 附加组件管理的 CRD。这就是我们在这里需要配置的。让我们为 Web 应用程序创建 HTTPScaledObject 资源: 
⚠️ HTTPScaleObject 必须在与 Web 应用相同的命名空间中创建资源!
kind: HTTPScaledObjectapiVersion: http.keda.sh/v1alpha1metadata:    name: go-helloworldspec:    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: ServiceapiVersion: v1metadata:  name: keda-add-ons-http-interceptor-proxyspec:  type: ExternalName  externalName: keda-add-ons-http-interceptor-proxy.keda.svc.cluster.local
现在,我们需要重新配置 Web 应用的入口,使其引用新创建的服务:
apiVersion: networking.k8s.io/v1kind: Ingressmetadata:  annotations:    kubernetes.io/ingress.class: nginx    cert-manager.io/cluster-issuer: letsencrypt  name: go-helloworldspec:  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 iterationsconstant_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 -wNAME            READY   UP-TO-DATE   AVAILABLE   AGEgo-helloworld   0/0     0            0           36mgo-helloworld   0/1     0            0           36mgo-helloworld   1/1     1            1           36mgo-helloworld   1/4     1            1           36mgo-helloworld   2/4     4            2           36mgo-helloworld   3/4     4            3           36mgo-helloworld   4/4     4            4           36mgo-helloworld   4/5     4            4           37mgo-helloworld   5/5     5            5           37m

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

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

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

# 100 RPShttp_req_duration: avg=11.38ms  min=7.68ms med=10.68ms max=100.96ms p(90)=12.78ms p(95)=14.33ms
# 1k RPShttp_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 部署的必需品,从而使应用程序自动缩放成为应用程序开发中的嵌入式组件。


    推荐阅读   




    推荐视频    


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
K8s 实践:优雅限制 K8s 集群中文件描述符与线程数量Kubernetes 实战:使用 k8s+jenkins 实现 CICD公共数据和企业数据估值与定价模式研究:基于数据产品交易价格计算器的贵州实践探索通俗易懂k8s——核心组件通俗易懂k8s——服务的注册与发现建信量化事件驱动基金合同终止,公募量化优胜劣汰加剧K8s部署Jumpserver并使用Istio对外暴露服务Knowledge-Based Systems 2023:基于注意力机制的图相似度学习探索云原生面经分享:精心整理的 K8s / Docker / DevOps 面试真题!| 极客时间弃亚马逊转戴尔,彻底下云、去 K8s 后,我们已经节省了 100 万美元从单体到微服务的系统改造:采用事件驱动架构优化会员系统国内疯狂的网购我眼中的人類起源和去向是怎麼回事第一章第三节 人类的思想语言K8s 日常运维故障处理,80% 你可能都遇见过!?当K8s出现问题时,我们可以从哪些方面排查出龙行龘(dá)龘(dá)!这些三叠字怎么念?K8s留给我们一地鸡毛!微服务 vs. 事件驱动架构:重新开始理解差异微软推出全新Windows应用商店 可直接搜索下载k8s 多网卡方案之multus用法深入解析k8s 网络插件—AntreaK8s多集群实践思考和探索海外名校1v1科研:基于深度学习的信贷违约预测算法|收获一作论文与导师推荐信!快乐每一天(加州的老墨 - 8)KafkaFlow 入门指南:构建可扩展的 Kafka 事件驱动应用龙行龘(dá)龘(dá)!酿丸子|附华夏厨师祖师爷简介|味之精微,口不能言汇总了近 50 场面试, 总结出了这份 70 多页的 K8s/Docker/DevOps 文档,超实用!| 极客时间龙行龘(dá)龘(dá)!2024央视春晚宣布→k8s集群中namespace状态一直显示Terminating如何解决?放弃亚马逊、谷歌,彻底告别 k8s!小米发布 Vela 快应用开发工具 Aiot-IDE:基于微软 VS Code,仅支持 Ubuntu弃亚马逊转戴尔,彻底下云、去K8s后,我们已经节省了100万美元画你所想!北航港大提出DiffSketcher:基于扩散模型的文本驱动矢量化手绘草图合成
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。