如何使用 KEDA 自动缩放 Grafana Loki Queries

云计算 云原生
为了验证我们的实现是否满足我们在真实场景中的需求,我们在内部环境中部署了自动缩放器。到目前为止,我们已经在 k6 创建所有工作负载的隔离环境中进行了实验。接下来,我们必须估计最大和最小副本数的适当值。

介绍

Grafana Loki (https://grafana.com/oss/loki/?pg=blog&plcmt=body-txt) 是 Grafana Labs 的开源日志聚合系统,灵感来自 Prometheus(https://prometheus.io/) 。Loki 具有水平可扩展性、高可用性和多租户特性。

Grafana Cloud 大规模运营 Grafana Cloud Logs,集群分布在不同的区域和云平台,如 AWS、Microsoft Azure 和 Google Cloud。Grafana Cloud 每天还摄取数百 TB 的数据,并在数千个租户中查询数 PB 的数据。最重要的是,每天数据查询与处理过程中,资源消耗都会有很大的波动,这使得以对用户响应且对 Grafana Cloud 来说具有成本效益的方式手动扩展集群变得非常困难。 

在这篇博客中,我们将描述 Grafana Labs 的工程师如何使用基于 Kubernetes 的事件驱动自动缩放器 ( KEDA(https://keda.sh/) ) 来更好地处理后端的 Loki 查询。

为什么我们需要 autoscaling

负责处理 Grafana Cloud Logs 查询的 Loki 读取路径组件之一是 querier,它是将 LogQL(https://grafana.com/docs/loki/latest/logql/?pg=blog&plcmt=body-txt) 查询与日志匹配的组件。可以想象,我们需要很多 querier 来实现如此高的吞吐量。但是,由于我们的集群全天工作负载发生重大变化,这种需求会发生波动。直到最近,我们还是根据工作负载手动扩展 querier,但这种方法存在三个主要问题。

1、它会适当地扩展 querier 以响应工作量的增加。

2、我们可能会过度配置集群并使 querier 闲置一段时间。

3、这会导致操作繁琐 (https://sre.google/sre-book/eliminating-toil/) ,因为我们必须手动上下扩展 querier。

为了克服这些问题,我们决定在 Kubernetes 中的 querier 部署中添加自动缩放功能。

为什么选择 KEDA

Kubernetes 附带了一个用于水平自动缩放 Pod 的内置解决方案:HorizontalPodAutoscaler ( HPA)。您可以使用 HPA 根据来自 Kubernetes metrics-server(https://kubernetes.io/docs/tasks/debug-application-cluster/resource-metrics-pipeline/#metrics-server) 的指标为 StatefulSets 和 Deployments 等组件配置自动缩放。metrics-server 公开 pod 的 CPU 和内存使用情况,但如果需要,您可以为其提供更多指标。

CPU 和内存使用指标通常足以决定何时扩大或缩小规模,但可能还有其他指标或事件需要考虑。在我们的案例中,我们对基于一些 Prometheus 指标的扩展感兴趣。从 Kubernetes 1.23 开始,HPA 已经支持外部指标(https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#scaling-on-custom-metrics) ,所以我们可以选择一个 Prometheus adapter 来根据 Prometheus 指标进行扩展。然而,KEDA 使这变得更加容易。

KEDA 是最初由 Microsoft 和 Red Hat 开发的开源项目,我们已经在内部将它用于与Grafana Mimir(https://grafana.com/oss/mimir/?pg=blog&plcmt=body-txt) 类似的用例。除了熟悉之外,我们之所以选择它,是因为它更成熟,而且它允许根据来自不同来源(例如 Prometheus)的事件和指标扩展任何 Pod。KEDA 构建在 HPA 之上,并公开了一个新的 Kubernetes 指标服务器,该服务器为 KEDA 创建的 HPA 提供新指标。

我们如何在 queriers 上使用 KEDA

Queriers 从查询调度程序队列中提取查询并在所有 querier 上处理它们。因此,根据以下条件进行扩展是有意义的:

  • 调度程序队列大小
  • 在 querier 中运行的查询

最重要的是,我们希望避免因短期工作负载高峰而扩大规模。在这些情况下,查询可能会比扩展所需的时间更快地处理工作负载。

考虑到这一点,我们根据排队查询的数量加上正在运行的查询进行扩展。我们称这些为 inflight requests。查询调度程序组件将公开一个新指标 ,cortex_query_scheduler_inflight_requests 指标结果使用百分比(https://grafana.com/blog/2022/03/01/how-summary-metrics-work-in-prometheus/?pg=blog&plcmt=body-txt) 跟踪正在进行的请求。通过使用百分比,我们可以避免在指标出现短期峰值时进行扩容。

使用结果度量,我们可以在查询中使用分位数 (Q) 和范围 (R) 参数来微调我们的缩放。Q 越高,指标对短期尖峰越敏感。随着 R 的增加,我们会随着时间的推移减少度量变化。较高的 R 值有助于防止自动缩放器过于频繁地修改副本数量。

sum(
max_over_time(
cortex_query_scheduler_inflight_requests{namespace="%s", quantile="<Q>"}[<R>]
)
)

然后我们需要设置一个阈值,以便我们可以根据度量值计算所需的副本数。Querier 程序处理来自队列的查询,每个 querier 配置为运行六个 worker。我们希望为高峰留出一些富余处理空间,因此我们的目标是使用这些 worker 中的 75%。因此,我们的门槛将是每个 querier 6 个 worker 的 75%,即 4 个 worker。

这个等式定义了当前副本中所需的副本数、度量值以及我们配置的阈值:

desiredReplicas = ceil[currentReplicas * ( currentMetricValue / threshold )]

例如,如果我们有一个副本和 20 个正在进行的请求,并且我们的目标是使用每个 worker 可用的六个 worker 中的 75%(四个),那么新的所需副本数量将是五个。

desiredReplicas = ceil[1 * (20 / 4)] = 5

考虑到这一点,我们现在可以创建 KEDA ScaledObject 来控制查询器的自动缩放。以下资源定义将 KEDA 配置为从 http://prometheus.default:9090/prometheus 提取指标。它还可以扩展到最大 50 个 querier,缩小到最小 10 个 querier,将 75% 用于 inflight requests 指标,并在两分钟内聚合其最大值。扩展阈值仍然是每个 querier 四个 worker。

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: querier
namespace: <REDACTED INTERNAL DEV ENV>
spec:
maxReplicaCount: 50
minReplicaCount: 10
pollingInterval: 30
scaleTargetRef:
kind: Deployment
name: querier
triggers:
- metadata:
metricName: querier_autoscaling_metric
query: sum(max_over_time(cortex_query_scheduler_inflight_requests{namespace=~"REDACTED", quantile="0.75"}[2m]))
serverAddress: http://prometheus.default:9090/prometheus
threshold: "4"
type: prometheus

使用 Grafana k6 Cloud 进行测试

在部署到我们的内部和生产环境之前,我们进行了多次实验和基准测试来验证。

Grafana k6 Cloud 是 Grafana k6 的完全托管版本,它是一个免费、开源、以开发人员为中心且可扩展的负载测试工具,可让工程团队轻松进行性能测试。

使用 k6 的 Grafana Loki 扩展(https://grafana.com/blog/2022/06/08/a-quick-guide-to-load-testing-grafana-loki-with-grafana-k6/?pg=blog&plcmt=body-txt) ,我们创建了一个 k6 测试 (https://gist.github.com/salvacorts/7f6fe8e53dcbdfc38606f3892918cfcc) ,该测试迭代地从多个虚拟用户(VU;有效的运行线程)向 Loki 开发集群发送不同类型的查询。我们尝试使用以下序列模拟真实的可变流量:

1、在两分钟内,将 VU 从 5 个增加到 50 个。

2、用 50 个 VU 保持一分钟。

3、在 30 秒内从 50 个 VU 增加到 100 个 VU,并在另外 30 秒内增加到 50 个,以创建工作负载峰值。

4、重复上一个尖峰。

5、最后,在两分钟内从 50 个 VU 变为零。

在下图中,我们可以看到测试在 k6 Cloud 中的表现,以及在测试期间进行中的请求和查询器副本的数量如何变化。首先,querier 会随着工作负载的增加而扩大,最后,querier 会在测试完成几分钟后回退。

图片

一个 Grafana k6 Cloud 测试,它迭代地将不同类型的查询发送到 Grafana Loki 开发集群。

图片

随着 inflight request 数量的增加(顶部),querier 的数量增加(底部)。在工作负载减少后的某个时间,querier 的数量也会减少。

图片

一旦我们确认我们的方法按预期工作,下一步就是在一些实际工作负载上进行尝试。

部署和微调

为了验证我们的实现是否满足我们在真实场景中的需求,我们在内部环境中部署了自动缩放器。到目前为止,我们已经在 k6 创建所有工作负载的隔离环境中进行了实验。接下来,我们必须估计最大和最小副本数的适当值。

对于最小副本数,我们在前 7 天 75% 的时间内检查了平均 inflight request,目标是 querier 的利用率为 75%。

clamp_min(ceil(
avg(
avg_over_time(cortex_query_scheduler_inflight_requests{namespace=~"REDACTED", quantile="0.75"}[7d])
) / scalar(floor(vector(6 * 0.75)))
), 2)

对于最大副本数,我们将当前副本数与在前 7 天内处理 50% 的 inflight request 所需的查询器数相结合。由于每个 querier 运行六个 worker,我们将 inflight 中的请求除以六。

clamp_min(ceil(
(
max(
max_over_time(cortex_query_scheduler_inflight_requests{namespace=~"REDACTED", quantile="0.5"}[7d])
) / 6
>
max (kube_deployment_spec_replicas{namespace=~"REDACTED", deployment="querier"})
)
or
max(
kube_deployment_spec_replicas{namespace=~"REDACTED", deployment="querier"}
)
), 20)

在估计了最小和最大副本的一些数字后,我们在内部环境中部署了自动缩放器。如下图所示,我们实现了预期的结果:querier 随着工作负载的增加而扩大,在工作负载减少后缩小。

图片

随着我们部署中 inflight request 数量的增加(顶部),querier 的数量增加(底部)。当 worker 减少时,querier 的数量也会减少。

您可能已经注意到由于缩放指标值的不断变化,副本数量的频繁波动(也称为抖动)(https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#flapping)  。如果您在扩展大量 pod 后过早缩减它们,最终会消耗 Kubernetes 中的大量资源来调度 pod。此外,它可能会影响查询延迟,因为我们需要经常启动新的 querier 来处理这些查询。幸运的是,HPA 提供了一种机制来配置稳定窗口(https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#stabilization-window) 以减轻这些频繁的波动,正如您可能已经猜到的那样,KEDA 也是如此。spec.advanced.horizontalPodAutoscalerConfig.behavior.scaleDown.stabilizationWindowSeconds参数允许您在 0 秒(立即缩放)和 3,600 秒(一小时)之间配置冷却时间,默认值为 300 秒(五分钟)。该算法很简单:我们使用一个跨越配置时间的滑动窗口,并将副本数设置为该时间范围内报告的最高数量。在我们的例子中,我们配置了一个 30 分钟的稳定窗口:

spec:
advanced:
horizontalPodAutoscalerConfig:
behavior:
scaleDown:
stabilizationWindowSeconds: 1800

下图显示了图表的形状现在在副本数量方面如何更加统一。在某些情况下,querier 在缩小后不久就会扩大规模,但总会有边缘情况发生这种情况。我们仍然需要为这个参数找到一个适合我们工作负载形状的好值,但总的来说,我们可以看到峰值更少。

图片

配置稳定窗口后,与上图相似的工作负载相比,副本数量波动较小。

即使我们配置的最大副本数比手动扩展 Loki 时要高得多,但添加自动扩展程序后的平均副本数会更低。较低的平均副本数转化为较低的查询器部署的拥有成本。 

图片

启用查询器自动缩放器后,集群中运行的平均副本数减少了。

此外,我们可以看到自动缩放器没有出现查询延迟下降的问题。下图显示了 7 月 21 日 12:00 UTC 在启用自动缩放之前和之后的查询和范围查询的 p90 延迟(以秒为单位)。

图片

启用查询器自动缩放器后,查询延迟并没有变差。

最后,我们需要一种方法来知道何时增加我们的最大副本数。为此,我们创建了一个警报,当自动缩放器长时间以配置的最大副本数运行时触发。以下代码片段包含此指标,如果它在至少三个小时内输出为 true,则会触发。

name: LokiAutoscalerMaxedOut
expr: kube_horizontalpodautoscaler_status_current_replicas{namespace=~"REDACTED"} == kube_horizontalpodautoscaler_spec_max_replicas{namespace=~"REDACTED"}
for: 3h
labels:
severity: warning
annotations:
description: HPA {{ $labels.namespace }}/{{ $labels.horizontalpodautoscaler }} has been running at max replicas for longer than 3h; this can indicate underprovisioning.
summary: HPA has been running at max replicas for an extended time

原文:https://grafana.com/blog/2022/10/20/how-to-autoscale-grafana-loki-queries-using-keda/

责任编辑:武晓燕 来源: 新钛云服
相关推荐

2023-02-27 08:00:00

KEDA云计算Kubernetes

2022-06-29 07:45:53

LogQLLoki日志流选择器

2022-06-20 14:59:14

读写分离模Loki

2023-08-31 08:21:42

KubernetesKADA驱动

2021-05-18 07:30:36

开发Spring Boot日志

2022-06-28 08:40:16

LokiPromtail日志报警

2023-12-21 11:53:34

KubernetesKEDA云原生

2022-04-07 09:30:00

自动化LinodeKubernetes

2016-11-03 20:06:53

UbuntuGrafanaDocker

2024-02-04 00:00:00

Loki性能查询

2022-12-29 08:00:26

Loki网络设备

2019-06-17 09:55:05

GPartedLinux根分区

2022-06-27 07:33:19

微服务Loki

2011-08-11 14:53:21

Ad Hoc DistSQL Server数

2024-03-11 00:01:00

PromtailLoki服务器

2013-10-17 11:02:28

2016-10-26 20:21:34

自动缩放可扩展性

2016-10-26 13:35:43

云自动资源负载

2023-12-06 11:10:08

2021-03-23 22:43:09

Grafana Tem分布式跟踪开源
点赞
收藏

51CTO技术栈公众号