基於 Kubernetes 事件驅動的自動縮放
KEDA 是一種基於事件驅動的自動伸縮工具,能夠解決 Kubernetes 原生 HPA 在靈活性和複雜性上的不足。KEDA 支持多種事件源(如 Prometheus、Kafka、RabbitMQ 等),可根據實際需求動態調整 Pod 副本數量,甚至將副本數縮減至 0,從而優化資源利用率並降低成本。通過 ScaledObject 對象,KEDA 能夠靈活配置伸縮策略,快速響應負載變化,實現高效擴展。本文將深入解析 KEDA 的工作原理、部署流程及優化策略,幫助開發者更好地理解和應用這一強大的彈性伸縮工具。
文 / 王琴
01 什麼是 KEDA
KEDA(Kubernetes-based Event-Driven Autoscaler)是一個在 Kubernetes 中實現事件驅動的彈性伸縮器。它不僅支持基於 CPU 和內存指標進行伸縮,還支持根據消息隊列長度、數據庫數據、QPS、Cron 定時計劃等多種指標進行伸縮,甚至可以將副本數量縮減至 0。KEDA 作爲一個強大的開源工具,爲 Kubernetes 用戶提供了基於事件的自動擴展能力,使得應用可以根據實際需求動態調整資源,提高效率和降低成本。
02 爲什麼需要 KEDA
Kubernetes 原生 HPA(Horizontal Pod Autoscaler)只能根據監控指標對工作負載自動擴縮容,主要是 CPU 和內存利用率。如果需要支持其他自定義指標,通常需要安裝 prometheus-adapter 來提供這些指標給 HPA。
HPA 在經過三個大版本的演進後當前支持了 Resource、Object、External、Pods 等四種類型的指標,演進歷程如下:
-
autoscaling/v1:只支持基於 CPU 指標的縮放;
-
autoscaling/v2beta1:支持 Resource Metrics(資源指標,如 pod 的 CPU)和 Custom Metrics(自定義指標)的縮放;
-
autoscaling/v2beta2:支持 Resource Metrics(資源指標,如 pod 的 CPU)和 Custom Metrics(自定義指標)和 ExternalMetrics(額外指標)的縮放。
如果需要基於其他地方如 Prometheus、Kafka、雲供應商或其他事件上的指標進行伸縮,可以通過 v2beta2 版本提供的 external metrics 來實現,具體如下:
(1)利用 Prometheus-adaptor 將通過 prometheus 取到的指標轉換爲 HPA 能夠識別的格式,以此來實現基於 prometheus 指標的彈性伸縮;
(2)應用系統需要實現 metrics 接口或者對應的 exporter 將指標暴露給 Prometheus。
雖然 HPA v2beta2 版本可以實現基於外部指標彈性伸縮,但實現方式較爲繁瑣。KEDA 的出現解決了 HPA 無法基於靈活的事件源進行伸縮的問題,提供了更靈活的伸縮策略。
03 Kubernetes 原生 H/VPA Autoscaling 存在的弊端
Kubernetes 原生的 HPA(Horizontal Pod Autoscaler)和 VPA(Vertical Pod Autoscaler)提供了 Autoscaling 能力,但也存在以下潛在瓶頸和限制:
延遲和響應時間
HPA 和 VPA 的 Autoscaling 過程需要一定的時間來監測指標並作出調整,從而可能會導致在負載突然增加或減少時出現一定的延遲,無法立即響應變化。這種延遲可能會導致性能下降或資源浪費。
指標選擇和配置
HPA 和 VPA 的 Autoscaling 依賴於指標的選擇和配置。選擇不合適的指標或錯誤地配置指標閾值可能導致擴縮容的不準確性。因此,正確選擇和配置指標是確保 Autoscaler 有效運行的重要因素。
基礎設施強綁
HPA 和 VPA 依賴於底層基礎設施的可擴展性和彈性。如果底層基礎設施無法滿足自動擴縮容的需求,例如,底層節點資源有限或網絡帶寬不足,那麼自動彈性伸縮的效果將受到限制。
應用程序設計限制
在實際的業務場景中,往往存在某些應用程序可能不適合自動擴縮容,特別是具有持久性狀態或特定調度要求的應用程序。這些應用程序可能需要採取額外的措施來處理自動擴縮容引起的狀態管理或數據持久性問題。
實施的複雜性
通常而言,爲 H/VPA 創建自定義指標可能並非易事。這個過程需要對 Kubernetes 內部結構有一定的瞭解,並需要開發人員深入研究相關接口和進行復雜的代碼修改。因此,對於沒有相關經驗的開發人員來說,這可能是一個具有挑戰性的任務。從長遠角度來看,這種額外的複雜性可能會導致維護困難。
04 KEDA 解決了哪些痛點
在目前的 Kubernetes 環境中,水平 Pod 自動縮放器(HPA)僅對基於資源的指標作出反應,例如 CPU 或內存使用情況,或者自定義指標。然而,對於那些可能經歷突發數據流的事件驅動應用程序來說,HPA 的擴展速度可能相當緩慢。此外,一旦數據流減緩,HPA 必須縮小規模並刪除多餘的 Pod,導致不必要的資源繼續產生費用。
KEDA 的出現填補了這一缺失,通過引入事件驅動的自動彈性伸縮機制,使得在 Kubernetes 上運行的事件驅動應用程序可以更加高效地擴展。KEDA 可以根據事件流的速率和規模動態地調整應用程序的副本數量,以滿足負載需求。這意味着在應用程序需要處理大量事件時,KEDA 可以快速擴展並自動添加 Pod 實例,以確保高吞吐量和低延遲。
另一個 KEDA 的優勢是它支持多種事件源,如 Azure 隊列、Kafka、RabbitMQ 等,使得應用程序能夠從不同來源接收事件。這爲開發人員提供了更大的靈活性和選擇性,可以根據應用程序的需求選擇適當的事件源。
如下爲基於 KEDA 使用 Prometheus 指標觸發 Autoscaling 機制的示例:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: prometheus-scaledobject
namespace: devops
spec:
scaleTargetRef:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
name: keda-devops-demo
triggers:
- type: prometheus
metadata:
serverAddress: http://<prometheus-host>:9090
metricName: http_request_total
query: envoy_cluster_upstream_rq{appId="300", cluster_ }
threshold: "50"
idleReplicaCount: 0
minReplicaCount: 1
maxReplicaCount: 10
在上述 ScaledObject 對象和 KEDA 定義中,我們指定了一個 ScaledObject 的示例,使用 Prometheus 指標來配置 KEDA Autoscaling。部署對象 “keda-devops-demo” 將根據 Prometheus 指標 “sum(irate(by_path_counter_total{}[60s]))” 來監控 HTTP 請求的數量。如果該指標的值超過 50,則 KEDA 將根據需要創建新的 Pod 來處理請求。如果該指標的值低於 50,則 KEDA 將根據需要刪除多餘的 Pod,以確保資源利用率的最大化。
該示例展示瞭如何使用 KEDA 來根據 Prometheus 指標來動態縮放應用程序的規模。KEDA 提供了靈活的配置選項,可以滿足不同的業務需求。
通過這種配置,系統能夠根據實際的 HTTP 請求負載情況來動態調整應用程序的規模。當負載增加時,Autoscaling 機制將創建更多的 Pod 來處理請求,從而保持應用程序的性能和可用性。一旦負載減輕,Autoscaling 機制便將適時地縮減 Pod 的數量,以節省資源和成本。
05 KEDA 是如何工作的
KEDA 工作原理
作爲 Kubernetes 的事件驅動 Autoscaling 工具,KEDA 可以根據應用程序的事件源來自動調整 Pod 的數量。KEDA 部署簡單,只需在 Kubernetes 集羣中創建一個 ScaledObject 對象即可。ScaledObject 對象包含 KEDA 的配置信息,包括事件源、縮放規則等。
在部署 KEDA 後,縮放器將會像哨兵一樣,持續監視事件源,並在發生任何觸發事件時將指標傳遞給指標適配器。指標適配器會像翻譯員一樣,將指標調整爲控制器組件可以理解的格式,並將其提供給控制器組件。控制器組件會根據 ScaledObject 中設置的擴展規則來做出擴大或縮小的決策,並將決策執行到 Pod 上。
通常來講,KEDA 與 Kubernetes 水平 Pod 自動縮放器(Horizontal Pod Autoscaler,HPA)、外部事件源以及 Kubernetes 的數據存儲之間的協作關係,可參考如下圖所示:
上述參考流程圖描述了 KEDA 如何與 HPA 配合應用 Pod 進行自動彈性伸縮,這裏,針對此實現架構圖進行簡要解析,具體實現流程如下:
-
Kubernetes API Server 充當 KEDA 和 Kubernetes 之間集成的橋樑,將 KEDA 的自動彈性伸縮功能與 Kubernetes 的資源管理功能相結合。KEDA 通過 ScaledObject 對象將自動彈性伸縮的機制與 Kubernetes 資源對象相結合。KEDA 的核心組件包括指標適配器、控制器、縮放器和准入 Webhooks。
-
Metrics Adapter 和准入 Webhooks 從外部觸發源收集指標,具體取決於 ScaledObject 對象中定義的觸發器類型。Metrics Adapter 將指標轉換爲 Kubernetes 指標,並將其暴露給 Controller。准入 Webhooks 負責驗證和修改 ScaledObject 對象,以確保 KEDA 能夠正確地進行自動彈性伸縮。
-
Controller 和 Scaler 負責根據 Metrics Adapter 收集的指標來進行自動彈性伸縮。Controller 負責將彈性任務發送給 Scaler。Scaler 負責將縮放任務應用到 Kubernetes 資源對象。
-
外部觸發源可以是任何可以提供指標數據的來源,例如 Apache Kafka、Prometheus、AWS CloudWatch 等。外部觸發源負責直接從正在運行的服務收集系統指標。如果工作負載很高,Pod 將會被橫向擴展。如果工作負載較低,則對 Pod 進行縮容。如果完全沒有工作負載,則將刪除 Pod,以最終優化基礎設施資源。
KEDA 核心組件
通常而言,KEDA 核心由以下幾個關鍵組件組成,具體涉及如下:
-
Scaler:連接到外部組件(例如 Prometheus 或者 RabbitMQ) 並獲取指標(例如,待處理消息隊列大小 )獲取指標;
-
Metrics Adapter:將 Scaler 獲取的指標轉化成 HPA 可以使用的格式並傳遞給 HPA;
-
Controller:負責創建和更新一個 HPA 對象,並負責擴縮到零;
-
keda operator:負責創建維護 HPA 對象資源,同時激活和停止 HPA 伸縮。在無事件的時候將副本數降低爲 0 (如果未設置 minReplicaCount 的話);
-
metrics server: 實現了 HPA 中 external metrics,根據事件源配置返回計算結果。
HPA 控制了副本 1→N 和 N→1 的變化。keda 控制了副本 0→1 和 1→0 的變化(起到了激活和停止的作用,對於一些消費型的任務副本比較有用,比如在凌晨啓動任務進行消費)。
06 KEDA 部署實踐
KEDA 兼容 K8s 版本
Kubectl apply -f https://raw.githubusercontent.com/shaowenchen/hubimage/master/keda/v2.8.2-keda.yaml
查看 Pod 是否正常
kubectl -n keda get pod
NAME READY STATUS RESTARTS AGE
keda-metrics-apiserver-7d8df95dd-nqfbg 1/1 Running 0 20d
keda-operator-59878677c4-2rqjm 1/1 Running 0 20d
keda-operator 負責處理 KEDA 內置對象、HPA 對象;keda-metrics-apiserver 提供給 HPA 的 external 類型指標,藉助 HPA 實現彈性。
配置 ScaledObject
創建應用
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: default
labels:
app: nginx
spec:
replicas: 5
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx-vts
image: shaowenchen/demo-nginx-vts:latest
ports:
- containerPort: 80
imagePullPolicy: Always
- name: nginx-vts-exporter
image: shaowenchen/demo-nginx-vts-exporter:latest
ports:
- containerPort: 9913
創建 Service
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx
name: nginx-svc
namespace: default
annotations:
prometheus.io/scrape: "true"
prometheus.io/path: "/metrics"
prometheus.io/port: "9913"
spec:
ports:
- name: nginx
nodePort: 30000
port: 80
protocol: TCP
targetPort: 80
- name: metrics
nodePort: 30001 # 在 30001 端口上暴露 metrics,是爲了方便測試,如果不需要,可以不暴露。
port: 9913
protocol: TCP
targetPort: 9913
selector:
app: nginx
type: NodePort
創建 ScaledObject 對象
ScaledObject 對象是 KEDA 的核心對象,它定義了伸縮的目標對象、觸發器、伸縮策略等。ScaledJob 與 ScaledObject 類似,只是它的目標對象是 Job。
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: nginx-deployment-scaledobject
namespace: default
spec:
scaleTargetRef: # 指定了伸縮的目標對象
name: nginx-deployment
pollingInterval: 15 # 指定了觸發器的輪詢間隔,Prometheus 指標採樣間隔爲 15s,因此這裏設置爲 15s
cooldownPeriod: 30 # 指的是副本從 1 變爲 0 的冷卻時間,KEDA 並不僅僅針對常駐服務,Scale to Zero 也是 KEDA 的特性之一
minReplicaCount: 0 # 最小副本數
maxReplicaCount: 20 # 最大副本數
triggers: # 指定了觸發伸縮的數據來源,這裏使用的是 Prometheus 觸發器
- type: prometheus
metadata:
serverAddress: http://prometheus-server.monitor.svc:80 # Prometheus 服務地址
metricName: nginx_server_requests # 指標名稱
threshold: '5' # 閾值
query: sum (irate(nginx_server_requests{code="total", host="*"}[1m]))/60 # Prometheus 查詢語句
Pod 的副本數 = 當前 Pod 副本數 *(query/threshold)。這裏的意思是,按照每個 Pod 處理 5QPS,設置 Pod 的數量。
測試應用伸縮能力
使用 wrk 工具對應用進行壓測。測試命令如下:
wrk -t1 -c10 -d120s http://0.0.0.0:30000/ # 使用1個線程,10個連接,持續120s。
計算 QPS 和 Pod 數量:
sum (irate(nginx_server_requests{code="total", host="*"}[1m])) / 60
max (sum by(instance)(kube_deployment_status_replicas{deployment=~"nginx-deployment"}))
以下爲測試數據:
VTS QPS / Pod Num 約等於 5,與預期一致。
由於設置了 maxReplicaCount 爲 20,VTS QPS 達到 133 時,Pod 副本數爲 20 時達到上限。
縮容 Pod,但 Pod 副本數沒有降爲 0。
可以發現,縮容 Pod 速度很慢,而且 Pod 副本數沒有降爲 0,需要進一步優化配置。
優化配置
在指標達到閾值時,我們希望能夠快速加副本,而不用長時間等待。在指標低於閾值時,我們希望能夠延緩縮容副本,避免因指標抖動,導致副本同步抖動。
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: nginx-deployment-scaledobject
namespace: default
spec:
scaleTargetRef:
name: nginx-deployment
pollingInterval: 15
cooldownPeriod: 30
minReplicaCount: 0
maxReplicaCount: 20
advanced:
horizontalPodAutoscalerConfig:
behavior:
scaleUp: # 擴容策略
stabilizationWindowSeconds: 15 # 指標穩定時間,也就是指標達到閾值後,需要持續多久纔會觸發伸
policies: # 策略列表,periodSeconds 指的是每隔多久執行一次策略,value 指的是每次執行策略時,增加或減少的 Pod 數量
- type: Pods
value: 5
periodSeconds: 15
scaleDown: # 縮容策略
stabilizationWindowSeconds: 60
policies:
- type: Pods
value: 5
periodSeconds: 15
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus-server.monitor.svc:80
metricName: nginx_server_requests
threshold: '5'
query: sum (irate(nginx_server_requests{code="total", host="*"}[1m]))/60
上述代碼優化後的效果爲:擴容時,持續 15s 就會觸發伸縮,每隔 15s 擴容 5 個 Pod;縮容時,持續 60s 纔會觸發伸縮,每隔 15s 縮容 5 個 Pod。
擴容時,指標與 Pod 數量同步增加;縮容時,指標先下降,然後 Pod 數量才下降。
Scale to Zero
雖然我們設置了 minReplicaCount 爲 0,但從監控數據看到 Pod 副本數並沒有降爲 0,這其實就涉及到指標可能的誤差。監控系統的可用性優先級是高於一致性、準確性的,是容忍一定的誤差的。
如下圖,我們可以看到在沒有請求時,指標也不是 0,才導致 Pod 副本數沒有降爲 0。
解決辦法很簡單,就是在指標中減去誤差值即可。
sum (irate(nginx_server_requests{code="total", host="*"}[1m]))/60 - 0.1
指標查詢的結果爲 0 或者負數時,KEDA 會將 Pod 副本數設置爲 minReplicaCount 值。
07 總結
KEDA 相比 HPA 支持更多的觸發來源,通過 Advanced 參數,可以對伸縮策略進行優化,實現快速擴容、延時縮容。當 Scale to Zero 時,需要考慮指標的誤差。另外,KEDA 會自動管理 Deployment 副本數,人工設置的值會被覆蓋,並且 Deployment 的 resourceVersion 值也會發生變化。如果在伸縮期間,修改 Deployment 鏡像、環境變量等參數,可能會因爲 resourceVersion 不一致,導致變更失敗。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/R-Lxz4bkKgNIiZNAez2MAw