在KEDA中尝试事件驱动的自动缩放

这是什么?

Kubernetes提供了一种称为HorizontalPodAutoscaling(以下简称HPA)的机制,根据资源使用量等指标自动增减Pod的启动数量。虽然HPA在广泛使用的用例中是一种优秀的机制,但不能将Pod完全降为零。KEDA通过扩展HPA的机制,可以将Pod完全降为零。

为了确认KEDA自身的安装方法等,本文将尝试使用演示性的配置测试KEDA进行Pod的自动扩展。

我们试试看

构建集群

我认为集群可以在几乎任何环境下运行。

我正在kind集群上进行操作验证。

Prometheus 安装

为了收集指标并回答KEDA的查询,安装 Prometheus。

稍微夸张一点,但由于构建容易,我们将使用名为kube-prometheus-stack的helm图表。

helm upgrade -i \
    -n prometheus \
    --create-namespace \
    prom-op \
    kube-prometheus-stack \
    --repo https://prometheus-community.github.io/helm-charts \
    --version 48.4.0

请注意,设置为上述的发布名称”prom-op”将在后续的ingress-nginx配置以及ScaledObject(KEDA的自定义资源)配置中出现,因此如果要进行更改,请谨慎操作。

安装Ingress-Nginx。

我们将作为Ingress Controller引入ingress-nginx。

在当前的配置中,将使用该Ingress Controller公开的指标作为自动缩放的输入。

因为要使用Helm图表,所以我会准备以下values文件:

controller:
  service:
    type: ClusterIP
  extraArgs:
    metrics-per-host: 'false'
  metrics:
    enabled: true
    serviceMonitor:
      enabled: true
      namespace: prometheus
      additionalLabels:
        release: prom-op

controller.extraArgs.metrics-per-host 的设置是因为在后续创建的 Ingress 资源中没有主机名。我们想要使用的指标是每个 Ingress 资源公开的指标,名为 nginx_ingress_controller_requests。但是,如果原始的 Ingress 资源没有设置主机名,那么默认情况下,与该 Ingress 资源相关联的指标将不会由 ingress-nginx 控制器公开。如官方文档所述,通过提供这个参数可以避免这个问题。

请使用以下命令进行安装:

helm upgrade -i \
  -n ingress-nginx \
  --create-namespace \
  ingress-nginx \
  ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --version 4.7.2 \
  --values ingress-nginx-values.yaml

KEDA的安装

默认安装KEDA。

helm upgrade -i \
  -n keda \
  --create-namespace \
  keda \
  keda \
  --repo https://kedacore.github.io/charts \
  --version 2.11.2

部署示例应用程序

为了使再现变得更加简单,我们使用nginx镜像。

为了使操作简单,我们将创建一个app/目录,并将以下清单分别作为文件创建。

请参考以下翻译:app/nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
– image: nginx:mainline-alpine
name: nginx
ports:
– containerPort: 80
name: http
protocol: TCP
resources: {}
status: {}

app/nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
namespace: default
spec:
ports:
– name: http
port: 80
protocol: TCP
targetPort: http
selector:
app: nginx
type: ClusterIP
status:
loadBalancer: {}

app/nginx-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
creationTimestamp: null
name: nginx
namespace: default
spec:
ingressClassName: nginx
rules:
– http:
paths:
– backend:
service:
name: nginx
port:
name: http
path: /
pathType: Prefix
status:
loadBalancer: {}

点击打开完整文件的清单(Manifest):

应用/nginx-部署.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
namespace: 默认
spec:
replicas: 1
selector:
matchLabels:
app: nginx
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
– image: nginx:mainline-alpine
name: nginx
ports:
– containerPort: 80
name: http
protocol: TCP
resources: {}
status: {}

应用/nginx-服务.yaml
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
namespace: 默认
spec:
ports:
– name: http
port: 80
protocol: TCP
targetPort: http
selector:
app: nginx
type: ClusterIP
status:
loadBalancer: {}

应用/nginx-Ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
creationTimestamp: null
name: nginx
namespace: 默认
spec:
ingressClassName: nginx
rules:
– http:
paths:
– backend:
service:
name: nginx
port:
name: http
path: /
pathType: Prefix
status:
loadBalancer: {}

最后,让我们在同样的app/目录中创建所需的ScaledObject清单。

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: nginx
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx
  idleReplicaCount: 0
  minReplicaCount: 1
  maxReplicaCount: 10
  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prom-op-kube-prometheus-st-prometheus.prometheus.svc:9090
      query: 'sum by (exported_namespace,exported_service) (irate(nginx_ingress_controller_requests[1m]))'
      activationThreshold: '0'
      threshold: '2'
    metricType: AverageValue

在上述的ScaledObject中,我们通过以下查询从Prometheus获取指标数据。

sum by (exported_namespace,exported_service) (irate(nginx_ingress_controller_requests[1m]))

这个指标是由ingress-nginx公开的,并且包含在通过ServiceMonitor资源(Prometheus operator的自定义资源)自动设置到Prometheus中的指标中。因此,在进行操作验证时,需要发送请求通过ingress-nginx。

请将通过以下命令创建的清单全部应用:

kubectl apply -f app/

你可以通过以下命令确认是否成功进行了apply:

kubectl get so,hpa,deploy,po,svc,ep

确认动作

我会向Ingress/Nginx发送请求。

将请求传送至Ingress的方法因环境而异,但可以通过端口转发的方式在任何环境中使用。可以通过打开另一个终端并使用以下命令进行端口转发:

kubectl -n ingress-nginx port-forward service/ingress-nginx-controller 3080:80

当ScaledObject处于非激活状态时,由于没有nginx Pod,会返回503错误。然而,503错误也会计入ScaledObject的请求数中,因此我们会继续发送请求而不放弃。您可以使用以下简单命令,或者使用脚本语言。

watch -p -n 1 curl -s localhost:3080/

使用上述的命令每秒发送一个请求,应该会启动一个Pod。

首先,我们同样尝试使用普通的自动扩展进行水平扩展。

我将尝试使用以下第4个命令将请求频率增加到每秒3次左右。

watch -p -n 0.3 curl -s localhost:3080/

当监控HPA对象时,可以确认指标将达到3.3左右,并且Pod的数量应该增加到2个。

接下来,让我们尝试进行缩容操作,尤其是尝试缩减到零的情况。

只需使用Ctrl + C中断正在进行的watch命令。由于scale-in默认设置了5分钟的冷却时间,因此需要等待大约5分钟。

那么,应该会删除所有的nginx Pod。

那么,所有的nginx Pod将会被删除。

这样一来,所有的nginx Pod都应该会被删除。

通过重新发起请求,您可以确认从零开始的scale-out。

总结

我尝试了在KEDA上进行零级别的扩容/缩容。

KEDA通常使用Amazon SQS或Apache Kafka等消息代理的指标,例如队列长度和消息延迟时间等,来检测消息代理中是否积压了消息,并对Pod进行自动扩展。但本次我们选择了更容易进行操作确认的Web服务器作为应用程序配置。

为了避免多个Ingress指标的合并导致爆炸(不确定是指溢出还是突然增加),据说采取这个措施。↩可以将其整合到一个文件中,也可以使用kustomize,但请注意这可能会影响后续的命令。↩

ScaledObject资源的缩写是so。↩

如果watch命令无法处理小数,也可以使用watch -p -n 1 ‘for i in $(seq 3); do curl -s localhost:3080 & done’。↩

在这种配置下,当Pod为零时,请求会出现错误,需要在使用端进行重试。↩

广告
将在 10 秒后关闭
bannerAds