Kubernetes彈性擴縮容之HPA和KEDA
前言
傳統意義上説,彈性伸縮主要解決的問題是容量規劃與實際負載的矛盾。而云計算為雲原生中提供的優勢之一就是彈性能力,從Kubernetes實戰出發,不管是在業務穩定性保障還是成本治理角度,彈性擴縮容能力都是必要研究方向。當實際的負載隨着業務量訪問增大,而逐漸的向集羣的資源瓶頸靠攏的時候,接近的時候,能不能快速的響應,來擴容集羣的流量,從而來應對這種突發。在Kubernetes平台中,資源彈性分為兩個維度:Node級別、Pod級別。針對Node負載,當集羣資源池不足時,可以使用CA(Cluster Autoscaler)自動增加Node;針對Pod負載,當Pod資源不足時,可以使用HPA(Horizontal Pod Autoscaler)自動增加Pod副本數量。本文就從Pod級別出發,初探一下水平擴縮容方案中的HPA和KEDA。
1.HPA實現原理
1.1 HPA 介紹
HPA( HorizontalPodAutoscaler )水平自動擴縮容 適用對象:Deployment、StatefulSet等 不適用對象:無法擴縮的對象,例如DaemonSet HPA的演進歷程 HPA 已經支持 autoscaling/v1、autoscaling/v2beta1和autoscaling/v2beta2 三個大版本 。 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(額外指標)的縮放。
通過kubectl api-versions |grep autoscal 查看集羣內支持的版本
HPA中metrics類型介紹 metrics中的type字段有四種類型的值:Object、Pods、Resource、External。
- Resource:指的是當前伸縮對象下的pod的cpu和memory指標,只支持Utilization(使用率)和AverageValue類型的目標值。
# Resource類型的指標
- type: Resource
resource:
name: cpu
# Utilization類型的目標值,Resource類型的指標只支持Utilization和AverageValue類型的目標值
target:
type: Utilization
averageUtilization: 50
- Object:指的是指定k8s內部對象的指標,數據需要第三方adapter提供,只支持Value和AverageValue類型的目標值。
# Object類型的指標
- type: Object
object:
metric:
# 指標名稱
name: requests-per-second
# 監控指標的對象描述,指標數據來源於該對象
describedObject:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
name: main-route
# Value類型的目標值,Object類型的指標只支持Value和AverageValue類型的目標值
target:
type: Value
value: 10k
- Pods:指的是伸縮對象Pods的指標,數據需要第三方的adapter提供,只允許AverageValue類型的目標值。
# Pods類型的指標
- type: Pods
pods:
metric:
name: packets-per-second
# AverageValue類型的目標值,Pods指標類型下只支持AverageValue類型的目標值
target:
type: AverageValue
averageValue: 1k
- External:指的是k8s外部的指標,數據同樣需要第三方的adapter提供,只支持Value和AverageValue類型的目標值。
# External類型的指標
- type: External
external:
metric:
name: queue_messages_ready
# 該字段與第三方的指標標籤相關聯,(此處官方文檔有問題,正確的寫法如下)
selector:
matchLabels:
env: "stage"
app: "myapp"
# External指標類型下只支持Value和AverageValue類型的目標值
target:
type: AverageValue
averageValue: 30
1.2 HPA實現原理
使用HPA生效前提: 必須定義 requests參數,必須安裝metrics-server 工作流程: 1、創建HPA資源,設定目標CPU使用率限額,以及最大、最小實例數 2、收集一組中(PodSelector)每個Pod最近一分鐘內的CPU使用率,並計算平均值 3、讀取HPA中設定的CPU使用限額 4、計算:平均值之和/限額,求出目標調整的實例個數 5、目標調整的實例數不能超過1中設定的最大、最小實例數,如果沒有超過,則擴容;超過,則擴容至最大的實例個數 6、回到2,不斷循環 HPA通過kube-controller-manager定期(定期輪詢的時間通過–horizontal-pod-autoscaler-sync-period選項來設置,默認的時間為30秒);如果指標變化太頻繁,也可以使用--horizontal-pod-autoscaler-downscale-stabilization指令設置擴縮容延遲時間,表示是自從上次縮容執行結束後,多久可以再次執行縮容,默認是5m。
算法説明:desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )] currentMetricValue表示當前度量值,desiredMetricValue表示期望度量值,desiredReplicas表示期望副本數。例如,當前度量值為 200m,目標設定值為 100m,那麼由於 200.0/100.0 == 2.0, 副本數量將會翻倍。 如果當前指標為 50m,副本數量將會減半,因為50.0/100.0 == 0.5
算法源代碼説明: 必須要定義requests參數源碼
// 獲取Pod resource request
func calculatePodRequests(pods []*v1.Pod, resource v1.ResourceName) (map[string]int64, error) {
requests := make(map[string]int64, len(pods))
for _, pod := range pods {
podSum := int64(0)
for _, container := range pod.Spec.Containers {
if containerRequest, ok := container.Resources.Requests[resource]; ok {
podSum += containerRequest.MilliValue()
} else {
return nil, fmt.Errorf("missing request for %s", resource)
}
}
requests[pod.Name] = podSum
}
return requests, nil
}
使用率算法源碼
// 計算使用率
func GetResourceUtilizationRatio(metrics PodMetricsInfo, requests map[string]int64, targetUtilization int32) (utilizationRatio float64, currentUtilization int32, rawAverageValue int64, err error) {
metricsTotal := int64(0)
requestsTotal := int64(0)
numEntries := 0
for podName, metric := range metrics {
request, hasRequest := requests[podName]
if !hasRequest {
// we check for missing requests elsewhere, so assuming missing requests == extraneous metrics
continue
}
metricsTotal += metric.Value
requestsTotal += request
numEntries++
}
currentUtilization = int32((metricsTotal * 100) / requestsTotal)
return float64(currentUtilization) / float64(targetUtilization), currentUtilization, metricsTotal / int64(numEntries), nil
}
1.3 彈性伸縮小例子
實驗小例子:創建nginx服務,為nginx服務創建一個HPA資源,當時nginx服務CPU使用率超過30%時則觸發水平擴容機制。(依賴metrics數據,集羣中需要提前部署好metrics-server)
``` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-hpa labels: app: nginx-hpa spec: replicas: 2 selector: matchLabels: app: nginx-hpa template: metadata: labels: app: nginx-hpa spec: containers: - name: nginx-hpa image: nginx:1.7.9 ports: - containerPort: 80 resources: requests: ##必須設置,不然HPA無法運行。 cpu: 200m
apiVersion: v1 kind: Service metadata: labels: app: nginx-hpa name: nginx-hpa spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: nginx-hpa
kind: HorizontalPodAutoscaler metadata: name: nginx-hpa-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: nginx-hpa minReplicas: 1 maxReplicas: 3 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 30
也可以通過kubectl autoscale來創建 HPA 對象。
將會為名為nginx-hpa的ReplicationSet 創建一個HPA對象,目標CPU 使用率為%,副本數量配置為1到3之間
kubectl autoscale rs nginx-hpa --min=1 --max=3 --cpu-percent=30 ```
使用ab命令創建一個簡易http服務壓測邏輯
yum install httpd -y
for i in {1..600}
do
ab -c 1000 -n 100000000 http://ServiceIP/
sleep
done
2.HPA實現侷限性
1、 HPA 無法將pod實例縮到0,即不能從n->0,或者從0-1,根據算法實現説明可以看出desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )] 。desiredMetricValue作為分母,期望值不能是'0';currentMetricValue當前值是'0'的時候,任務數乘'0'都為零。 2、使用率計算方式 計算方式存在爭議,在Resource類型中,使用率計算是通過request而不是limit,如果按照request來計算使用率(會超過100%)是不符合預期的。但也是可以修改源碼,或者使用自定義指標來代替。 3、多容器Pod使用率問題 1.20版本中已經支持了ContainerResource可以配置基於某個容器的資源使用率來進行擴縮,如果是之前的版本建議使用自定義指標替換。 4、性能問題 單線程架構:默認的hpa-controller是單個Goroutine執行的,隨着集羣規模的增多,勢必會成為性能瓶頸,目前默認hpa資源同步週期會15s,假設每個metric請求延時為100ms,當前架構只能支持150個HPA資源(保證在15s內同步一次)。
3.KEDA引入
3.1 KEDA是什麼、KEDA和HPA是什麼關係
KEDA是什麼: KEDA 是一個基於 Kubernetes 的事件驅動自動擴縮器。 它為 Kubernetes 資源提供了30 多個內置縮放器,因此我們不必擔心為我們需要的各種指標源編寫自定義適配器。 KEDA 提供了將資源擴展到零的強大功能。KEDA 可以將資源從0擴展到 1或從 1 擴展到 0,從1 到 n 以及向後擴展由 HPA 負責。 KEDA安裝使用要求Kubernetes集羣 1.16 或以上版本 KEDA和HPA是什麼關係: “既生瑜何生亮”? 主要是 HPA 這哥們天生有缺陷,無法基於靈活的事件源進行伸縮,KEDA去幫助實現,當然沒有一個事物誕生是完美的,只有在特定的場景下才能相較誰更完美。
3.2 KEDA實現原理
KEDA 哪些核心組件: Metrics Adapter: 將 Scaler 獲取的指標轉化成 HPA 可以使用的格式並傳遞給 HPA Controller:負責創建和更新一個 HPA 對象,並負責擴縮到零 Scaler:連接到外部組件(例如 Prometheus或者例如,RabbitMQ並獲取指標(例如,待處理消息隊列大小) )獲取指標 KEDA實現原理圖: KEDA源碼怎樣實現n-0的:
//當前副本數為0,並是所有scaler屬於active狀態,則修改副本數為MinReplicaCount 或 1
if currentScale.Spec.Replicas == 0 && isActive {
e.scaleFromZero(ctx, logger, scaledObject, currentScale)
} else if !isActive &&
currentScale.Spec.Replicas > 0 &&
(scaledObject.Spec.MinReplicaCount == nil || *scaledObject.Spec.MinReplicaCount == 0) {
// 所有scaler都處理not active狀態,並且當前副本數大於0,且MinReplicaCount設定為0
// 則縮容副本數為0
e.scaleToZero(ctx, logger, scaledObject, currentScale)
} else if !isActive &&
scaledObject.Spec.MinReplicaCount != nil &&
currentScale.Spec.Replicas < *scaledObject.Spec.MinReplicaCount {
// 所有scaler都處理not active狀態,並且當前副本數小於MinReplicaCount,則修改為MinReplicaCount
currentScale.Spec.Replicas = *scaledObject.Spec.MinReplicaCount
err := e.updateScaleOnScaleTarget(ctx, scaledObject, currentScale)
....
} else if isActive {
// 處理active狀態,並且副本數大於0,則更新LastActiveTime
e.updateLastActiveTime(ctx, logger, scaledObject)
} else {
// 不處理
logger.V(1).Info("ScaleTarget no change")
3.3 KEDA配置介紹
環境介紹 Kubernetes :v1.17.2 Keda:2.6.1 部署
kubectl apply -f https://github.com/kedacore/keda/releases/download/v2.4.0/keda-2.6.1.yaml
組件介紹 keda-operator 負責創建維護hpa對象資源,同時激活和停止hpa伸縮。在無事件的時候將副本數降低為0(如果未設置minReplicaCount的話) keda-metrics-apiserver 實現了hpa中external metrics,根據事件源配置返回計算結果。 創建ScaledObject資源
``` apiVersion: keda.sh/v1alpha1
由 Keda 運營商提供的自定義 CRD
kind: ScaledObject metadata: name: nginx-scaledobject namespace: hpa-tmp spec: advanced: # HPA config # Read about it here: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/ horizontalPodAutoscalerConfig: behavior: scaleDown: policies: - periodSeconds: 30 type: Pods value: 1 stabilizationWindowSeconds: 30 scaleUp: policies: - periodSeconds: 10 type: Pods value: 1 stabilizationWindowSeconds: 0 # 在將部署縮放回 1 之前,最後一個觸發報告活動後等待的時間 cooldownPeriod: 30 # keda 將擴展到的最大副本數 maxReplicaCount: 3 # keda 將擴展到的最小副本數 minReplicaCount: 1 # 查詢 Prometheus 的時間間隔 pollingInterval: 15 scaleTargetRef: # 針對哪個deployment name: nginx-hpa triggers: - type: cpu metadata: type: Utilization value: "30" ```
deployment 複用上文 nginx-hpa,創建好 ScaledObject 後會自動創建出hpa資源 更多type 類型配置介紹通過多個指標控制更精準的控制擴縮容動作,支持 Prometheus 指標、Metrics-server 指標、計劃任務指標等。
triggers:
- metadata:
# Prometheus指標支持
metricName: istio_request_qps_1m
query: istio_request_qps_1m{app_name="test-hpa"}
serverAddress: http://prometheus-service.monitoring.svc.cluster.local:9090
threshold: "100"
name: istio-qps-trigger
type: prometheus
- metadata:
# metrics-server 指標支持
type: AverageValue
value: "10"
name: cpu-trigger
type: cpu
- metadata:
# 計劃任務指標支持
desiredReplicas: "3"
end: 45 * * * *
start: 40 * * * *
timezone: Asia/Shanghai
type: cron
3.4 社區介紹
KEDA社區瞭解和活躍性 2019 年 5 月 Microsoft 和 Red Hat 共同發起了 KEDA 項目,該項目旨在簡化應用程序的自動伸縮。用户只需創建 ScaledObject或 ScaledJob定義想要伸縮的對象和要使用的觸發器,支持縮減到零 (scale-to-zero)。 KEDA 在 2020 年 3 月成為 CNCF 沙盒項目。自從成為沙盒項目以來,KEDA 用户數量大幅增長,這些用户主要來自 Alibaba、CastAI、KPMG、Meltwater、Microsoft 等公司。目前 KEDA 已經從 CNCF 的沙箱項目升級為孵化項目。 KEDA 的開源代碼倉庫地址為 https://github.com/kedacore/keda,目前已獲得 5.6k 多顆 Star,超過 100 位貢獻者。KEDA 主要由 Microsoft、Red Hat 和 Codit這三個組織人員進行維護
4.使用場景和後續深究切入點後續後續
1、大數據應用定時抽取任務應用,夜間高峯時可以擴容,白天縮減,可以結合CronHPA進行定時擴縮容; 2、事件驅動架構應用可以嘗試在平時未有使用量的時候將應用實例數置為0; 3、一些轉換類服務擴縮容時並行度控制。使用 Kubernetes 生命週期管理鈎子SIGTERM,可以利用它來延遲終止或者可以使用 KEDA 的 ScaledJob對象來創建和擴展它,而不是針對事件創建部署。這樣,它可以控制並行度,並且這些轉換作業可以運行直到完成; 4、不同場景下的擴容速度控制; 5、根據流量削峯填谷,自動化的成本管控。
參考資料
HPA相關:
https://kubernetes.io/zh-cn/docs/tasks/run-application/horizontal-pod-autoscale/ https://kubernetes.io/zh-cn/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/ https://kubernetes.io/zh-cn/docs/tasks/run-application/horizontal-pod-autoscale/#algorithm-details https://help.aliyun.com/document_detail/86554.html https://help.aliyun.com/document_detail/151557.html?spm=a2c4g.11186623.0.0.5e86135f1tiy60 https://cloud.tencent.com/developer/article/1839418 https://github.com/kubernetes/kubernetes/blob/2dba4034f8424574c42cebfce91664c4b7e08c61/pkg/controller/podautoscaler/replica_calculator.go
KEDA相關: https://github.com/kedacore/keda https://keda.sh/ 官網
推薦閲讀
招賢納士
政採雲技術團隊(Zero),一個富有激情、創造力和執行力的團隊,Base 在風景如畫的杭州。團隊現有 500 多名研發小夥伴,既有來自阿里、華為、網易的“老”兵,也有來自浙大、中科大、杭電等校的新人。團隊在日常業務開發之外,還分別在雲原生、區塊鏈、人工智能、低代碼平台、中間件、大數據、物料體系、工程平台、性能體驗、可視化等領域進行技術探索和實踐,推動並落地了一系列的內部技術產品,持續探索技術的新邊界。此外,團隊還紛紛投身社區建設,目前已經是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等眾多優秀開源社區的貢獻者。如果你想改變一直被事折騰,希望開始折騰事;如果你想改變一直被告誡需要多些想法,卻無從破局;如果你想改變你有能力去做成那個結果,卻不需要你;如果你想改變你想做成的事需要一個團隊去支撐,但沒你帶人的位置;如果你想改變本來悟性不錯,但總是有那一層窗户紙的模糊……如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望參與到隨着業務騰飛的過程,親手推動一個有着深入的業務理解、完善的技術體系、技術創造價值、影響力外溢的技術團隊的成長過程,我覺得我們該聊聊。任何時間,等着你寫點什麼,發給 [email protected]
微信公眾號
文章同步發佈,政採雲技術團隊公眾號,歡迎關注
- Kubernetes彈性擴縮容之HPA和KEDA
- 重構-把代碼寫的更漂亮
- 別再用 Netty 寫 helloWorld 了, 進來了解一下實時聊天系統
- Linux 是如何啟動的
- DataX 全系列之五 —— DataX-web 介紹和使用
- 神祕莫測的 SQL 執行計劃
- 人工智能 NLP 簡述
- 雪花算法詳解
- 淺析 ElasticJob-Lite 3.x 定時任務
- 一次接口響應時間過長的性能分析及排查過程
- 基於父子關係的高效去重算法
- 分佈式一致性
- Lucene 查詢原理解析
- MySQL 意向鎖
- Unsafe 魔法類應用體現 (LockSupport)
- 一次線上報錯引起對MySQL間隙鎖的研究
- RocketMQ 延時方案分析與總結
- dapr實戰(三)
- 數據中台建設實踐(二)- 數據治理之數據質量
- 政採雲 Flutter 動態 iconfont 實踐探索