KEDA:基于事件驱动扩展K8S应用的深度实践
新钛云服已累计为您分享773篇技术干货
作为 SRE,需要保证应用弹性和高可用性。因此,自动缩放是我们需要的必须功能。通过自动缩放,我们能确保工作负载能够高效的地处理业务流量。
在本文中,我们将详细描述如何使用 KEDA 以事件驱动的方式自动扩展 Kubernetes 应用程序。
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 的最简单方法是使用官方 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。
下面让我们深度实践一下KEDA!
---
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
[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 之间启动并运行两个副本。
(使用 KEDA HTTP 外部缩放器)
借助所有 KEDA 的缩放器,我们可以通过多种方式自动扩展 Web 应用程序,例如,基于 AMQP 队列中的消息进行缩放应用。
目前,我们了解了 KEDA 的工作原理。我下面们将探讨 KEDA 如何通过基于 HTTP 事件自动扩展应用程序来帮助我们处理流量高峰。为此,我们有两个选择:
使用Prometheus scaler
使用 KEDA HTTP 外部缩放器,它的工作方式类似于附加组件,由于演示集群上没有安装 Prometheus,因此我们将使用 KEDA HTTP 外部缩放器。
💡 KEDA HTTP插件目前处于测试阶段。它主要由KEDA团队维护。
helm install http-add-on kedacore/keda-add-ons-http --namespace keda
❯ 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
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。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
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)
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();
}
❯ 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}
targetPendingRequests
10
。所以一切都很正常 😁❯ 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 iterations
constant_request_rate ✓ [======================================] 00/50 VUs 30s 1000.00 iters/s
❯ 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 指标的一些对比:
http_req_duration: avg=11.38ms min=7.68ms med=10.68ms max=100.96ms p(90)=12.78ms p(95)=14.33ms
http_req_duration: avg=120.09ms min=8.14ms med=74.77ms max=6.87s p(90)=189.49ms p(95)=250.21ms
targetPendingRequests
HTTPScaledObject
参数 。通过本文介绍的两个案例,我们已经验证过了如何缩放到零的场景。但是,大家真的知道它是如何工作的吗?
由于 KEDA 根据事件自动缩放应用程序,因此从收到事件的那一刻起,KEDA 会将应用程序缩放到其最小副本。例如,如果我们以 HTTP 附加组件为例,KEDA 将在第一次收到请求时扩展到最小副本。
KEDA 提供了一个类似于 FaaS 的事件感知扩展模型,在这种模型中,Kubernetes 部署可以基于需求和基于智能动态地从零扩展,而不会丢失数据和上下文。在业务请求量上来后,应用程序将进行自动化的扩容,当业务低谷的时候,则会自动的缩容。这可以在缓解很多生产环境下的手动扩/缩容操作,以保障用户的服务体验。
KEDA 还为 Kubernetes 带来了更多的事件源。随着未来更多触发器的加入,KEDA 有很大的潜力成为生产级 Kubernetes 部署的必需品,从而使应用程序自动缩放成为应用程序开发中的嵌入式组件。
推荐阅读
推荐视频
微信扫码关注该文公众号作者