天天看點

如何使用 KEDA 自動縮放 Grafana Loki Queries

作者:新钛雲服

介紹

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 會在測試完成幾分鐘後回退。

如何使用 KEDA 自動縮放 Grafana Loki Queries

一個 Grafana k6 Cloud 測試,它疊代地将不同類型的查詢發送到 Grafana Loki 開發叢集。

如何使用 KEDA 自動縮放 Grafana Loki Queries

随着 inflight request 數量的增加(頂部),querier 的數量增加(底部)。在工作負載減少後的某個時間,querier 的數量也會減少。

如何使用 KEDA 自動縮放 Grafana Loki Queries

一旦我們确認我們的方法按預期工作,下一步就是在一些實際工作負載上進行嘗試。

部署和微調

為了驗證我們的實作是否滿足我們在真實場景中的需求,我們在内部環境中部署了自動縮放器。到目前為止,我們已經在 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 随着工作負載的增加而擴大,在工作負載減少後縮小。

如何使用 KEDA 自動縮放 Grafana Loki Queries

随着我們部署中 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 在縮小後不久就會擴大規模,但總會有邊緣情況發生這種情況。我們仍然需要為這個參數找到一個适合我們工作負載形狀的好值,但總的來說,我們可以看到峰值更少。

如何使用 KEDA 自動縮放 Grafana Loki Queries

配置穩定視窗後,與上圖相似的工作負載相比,副本數量波動較小。

即使我們配置的最大副本數比手動擴充 Loki 時要高得多,但添加自動擴充程式後的平均副本數會更低。較低的平均副本數轉化為較低的查詢器部署的擁有成本。

如何使用 KEDA 自動縮放 Grafana Loki Queries

啟用查詢器自動縮放器後,叢集中運作的平均副本數減少了。

此外,我們可以看到自動縮放器沒有出現查詢延遲下降的問題。下圖顯示了 7 月 21 日 12:00 UTC 在啟用自動縮放之前和之後的查詢和範圍查詢的 p90 延遲(以秒為機關)。

如何使用 KEDA 自動縮放 Grafana Loki Queries

啟用查詢器自動縮放器後,查詢延遲并沒有變差。

最後,我們需要一種方法來知道何時增加我們的最大副本數。為此,我們建立了一個警報,當自動縮放器長時間以配置的最大副本數運作時觸發。以下代碼片段包含此名額,如果它在至少三個小時内輸出為 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/

繼續閱讀