云原生彈性伸縮控制器實(shí)現(xiàn)細(xì)節(jié)
什么是彈性伸縮
基本原理
彈性伸縮(Horizontal Pod Autoscaler)是kubernetes內(nèi)置的一種副本控制器,主要功能是檢測(cè)分析pod的負(fù)載變化情況來(lái)判斷是否需要調(diào)整服務(wù)pod個(gè)數(shù)并自動(dòng)將服務(wù)pod數(shù)擴(kuò)縮至伸縮算法的預(yù)期值以滿足服務(wù)正常運(yùn)行,目前逐漸成為了各大服務(wù)器廠商的標(biāo)準(zhǔn)配置。其基本原理如下圖:

由圖可看出,彈性伸縮調(diào)度是一個(gè)間歇運(yùn)行的閉環(huán)系統(tǒng),主要有以下三個(gè)組件構(gòu)成:
Metrics Server
主要功能是從RC/deployment處搜刮服務(wù)運(yùn)行的負(fù)載指標(biāo),為hpa調(diào)度提供數(shù)據(jù)源
Rc/Deployment
hpa具體調(diào)度的資源,目前Hpa支持Deployment、StateFulSet和RC等包含了Scale子資源的所有資源
Hpa控制器
彈性伸縮系統(tǒng)的大腦,通過(guò)從Metrics Server中獲取服務(wù)的實(shí)際負(fù)載,按照彈性伸縮算法對(duì)Rc/Deployment資源進(jìn)行調(diào)度,彈性伸縮核心算法如下:

主要調(diào)度過(guò)程如下圖:

應(yīng)用場(chǎng)景
彈性伸縮主要是用于解決容量規(guī)劃和實(shí)際負(fù)載的矛盾,其最典型的應(yīng)用場(chǎng)景就是應(yīng)對(duì)實(shí)際負(fù)載激增而容量規(guī)劃還未來(lái)得及反應(yīng)的情況,如下圖:

舉個(gè)簡(jiǎn)單的栗子——微博熱搜,熱搜時(shí)訪問(wèn)量激增,需要快速擴(kuò)容機(jī)器,在熱點(diǎn)過(guò)后,又需要快速回收機(jī)器降低成本,一方面是可能擴(kuò)容不及時(shí),導(dǎo)致服務(wù)存在部分不可用,另一方面費(fèi)時(shí)費(fèi)力,需要人力操作。對(duì)于這種場(chǎng)景使用彈性伸縮就很合適,控制器持續(xù)檢測(cè)服務(wù)使用率,當(dāng)使用率超過(guò)配置的閾值時(shí)會(huì)動(dòng)態(tài)擴(kuò)容,保證服務(wù)可用和穩(wěn)定;當(dāng)熱點(diǎn)消失,服務(wù)使用率下降,此時(shí)控制器會(huì)動(dòng)態(tài)縮容,降低資源浪費(fèi),全程實(shí)現(xiàn)自動(dòng)化,智能化。
優(yōu)勢(shì)
從彈性伸縮的應(yīng)用場(chǎng)景中可以看到,彈性伸縮至少具備如下三個(gè)優(yōu)勢(shì):
自動(dòng)
接入彈性伸縮的服務(wù)擴(kuò)縮操作全程由彈性伸縮控制器控制,無(wú)需人力干預(yù),全程操作閉環(huán)自動(dòng)。
穩(wěn)定
彈性伸縮會(huì)在服務(wù)負(fù)載高于配置閾值時(shí)觸發(fā)擴(kuò)容,擴(kuò)容后服務(wù)有更多的實(shí)例支撐服務(wù)訪問(wèn),保證服務(wù)的穩(wěn)定。
-
降本
彈性伸縮會(huì)在服務(wù)負(fù)載降低到配置閾值時(shí)觸發(fā)縮容,縮容后服務(wù)會(huì)回收多余的實(shí)例,降低資源浪費(fèi)。
云原生平臺(tái)彈性伸縮實(shí)現(xiàn)細(xì)節(jié)
以上主要介紹kubernetes原生彈性伸縮hpa的設(shè)計(jì)原理和優(yōu)勢(shì)。原生的彈性伸縮部署和使用都很簡(jiǎn)單,但是不能直接應(yīng)用于哈啰的業(yè)務(wù),因?yàn)閔pa無(wú)法感知soa拉出場(chǎng)景。我們的預(yù)期是實(shí)現(xiàn)基于負(fù)載和soa注冊(cè)狀態(tài)的雙重檢測(cè)。所以我們借鑒hpa的設(shè)計(jì)思路自研了一套能夠?qū)崿F(xiàn)哈啰業(yè)務(wù)場(chǎng)景的水位彈性控制器WPA(Water Pod Autoscaler),即實(shí)現(xiàn)soa拉入、拉出和應(yīng)用負(fù)載的組合推薦計(jì)算,將soa注冊(cè)狀態(tài)引入到核心算法中,當(dāng)服務(wù)因自身健康原因從注冊(cè)中心拉出時(shí),核心算法能夠準(zhǔn)確剝離拉出soa實(shí)例,計(jì)算出當(dāng)前服務(wù)的實(shí)際有效負(fù)載平均值,確保服務(wù)能夠準(zhǔn)確擴(kuò)縮。
wpa的設(shè)計(jì)思路一定程度上借鑒了hpa,主要采用crd實(shí)現(xiàn),其設(shè)計(jì)原理如下:

從圖中可以看出,wpa控制器除了從metrics server上獲取服務(wù)負(fù)載指標(biāo)外,還通過(guò)hahas平臺(tái)獲取soa注冊(cè)指標(biāo)。控制器通過(guò)對(duì)兩種指標(biāo)的聚合處理得出擴(kuò)縮預(yù)期副本數(shù),實(shí)現(xiàn)準(zhǔn)確擴(kuò)縮。
由于彈性伸縮控制器細(xì)節(jié)較多,下面主要介紹wpa幾個(gè)比較核心的概念及其實(shí)現(xiàn)。
核心算法
為避免服務(wù)頻繁擴(kuò)縮,將hpa的單線擴(kuò)縮判斷指標(biāo)改為雙線判斷擴(kuò)縮指標(biāo),可以單獨(dú)設(shè)置向上的擴(kuò)容觸發(fā)閾值和向下縮容的觸發(fā)閾值,如下圖:

擴(kuò)容核心算法:

例如:服務(wù)當(dāng)前有效副本數(shù)5個(gè),平均負(fù)載為1500m,最大閾值為:1200m,則
upScaleProposal=Ceil(float(5) * 1500 / float(1200))=7(6.25向上取整),
wpa控制器最終會(huì)擴(kuò)容2個(gè)實(shí)例,擴(kuò)容后總實(shí)例為7個(gè)。
縮容核心算法:

當(dāng)彈性伸縮算法選擇均值模式(average)的時(shí)候,averaged=n, 當(dāng)彈性伸縮算法選擇絕對(duì)值模式(absolute)時(shí),averaged=1,目前wpa均默認(rèn)為均值模式。
例如:服務(wù)當(dāng)前有效副本數(shù)7個(gè),平均負(fù)載為300m,最小閾值為:400m,則
downScaleProposal=floor(float64(7) * 300 / float64(400))=5(5.25)
wpa控制器最終會(huì)縮容2個(gè)實(shí)例,縮容后總實(shí)例為5個(gè)。
兩種算法有一點(diǎn)區(qū)別,擴(kuò)容使用ceil方法向上取整,縮容使用floor方法向下取整,兩者操作采取最大可能原則,這樣可以提高彈性調(diào)度的靈敏度和準(zhǔn)確性。
噪聲處理
噪聲主要來(lái)自于兩部分,一個(gè)是kubernetes本身采用recreate方式創(chuàng)建新的實(shí)例時(shí),會(huì)出現(xiàn)Starting和Stopping兩種狀態(tài)的實(shí)例,此時(shí)統(tǒng)計(jì)的總的實(shí)例數(shù)會(huì)變多,會(huì)給計(jì)算預(yù)期值帶來(lái)噪音;第二個(gè)是新啟動(dòng)的pod還未被metrics server采集到有效指標(biāo),此時(shí)實(shí)例的指標(biāo)是空值,會(huì)對(duì)計(jì)算預(yù)期值帶來(lái)噪音。
wpa控制器針對(duì)第一個(gè)問(wèn)題主要通過(guò)在計(jì)算指標(biāo)時(shí)對(duì)pod狀態(tài)去噪來(lái)解決,將非running狀態(tài)的指標(biāo)都進(jìn)行過(guò)濾,降低Starting和Stopping狀態(tài)指標(biāo)帶來(lái)的干擾,核心代碼如下:
if pod.DeletionTimestamp != nil || pod.Status.Phase == corev1.PodFailed {ignoredPods.Insert(pod.Name)continue}if pod.Status.Phase == corev1.PodPending {unReadyPods.Insert(pod.Name)continue}if condition == nil || pod.Status.StartTime == nil {unReady = true} else {if pod.Status.StartTime.Add(cpuInitializationPeriod).After(time.Now()) {unReady = condition.Status == corev1.ConditionFalse || metric.Timestamp.Before(condition.LastTransitionTime.Time.Add(metric.Window))} else {unReady = condition.Status == corev1.ConditionFalse && pod.Status.StartTime.Add(delayOfInitialReadinessStatus).After(condition.LastTransitionTime.Time)}}if unReady {unReadyPods.Insert(pod.Name)continue}if ignoredPods != nil && ignoredPods.Len() > 0 {removeMetricsForPods(metrics, ignoredPods)}if unReadyPods != nil && unReadyPods.Len() > 0 {removeMetricsForPods(metrics, unReadyPods)}
針對(duì)第二個(gè)問(wèn)題則通過(guò)過(guò)濾metric空值與重新賦值來(lái)實(shí)現(xiàn)去噪,核心代碼如下:
// Pods missing metricsmetric, found := metrics[pod.Name]if !found {missingPods.Insert(pod.Name)continue}if len(missPods) > 0 {if action == v1alpha1.CronScaleDown {for podName := range missPods {metrics[podName] = metricsclient.PodMetric{Value: metric.Resource.HighWatermark.MilliValue() + metric.Resource.HighWatermark.MilliValue()*wpa.Spec.Tolerance.MilliValue()/1000}}} else {for podName := range missPods {metrics[podName] = metricsclient.PodMetric{Value: 0}}}}
冷卻周期
冷卻周期是指控制器執(zhí)行擴(kuò)縮動(dòng)作的等待時(shí)間,即配即生效,主要是防止過(guò)快的擴(kuò)縮對(duì)服務(wù)穩(wěn)定性造成影響,核心邏輯如下:

wpa的冷卻周期可以在平臺(tái)上直接配置,如下圖:

頻率控制
頻率控制是指控制器單次執(zhí)行擴(kuò)縮的數(shù)量限制,計(jì)算公式如下:

例如:當(dāng)前實(shí)例數(shù)為3,預(yù)期副本數(shù)6,擴(kuò)容實(shí)例百分比為20%,最大實(shí)例數(shù)為7,則
本次擴(kuò)容后總實(shí)例數(shù) = min((3 + max( 1, floor(3 * 0.2))), 7) = min(4, 7)=4
加入擴(kuò)容頻率控制后,本次最終擴(kuò)容實(shí)例數(shù)為1,擴(kuò)容后總實(shí)例數(shù)為4個(gè)。
縮容頻率控制:

例如:當(dāng)前副本數(shù)為7,預(yù)期副本數(shù)3,縮容實(shí)例百分比為1%,最小實(shí)例數(shù)為2,則
本次縮容后總實(shí)例數(shù) = max((7-max(1,floor(7 * 0.01 ))),2)=max(6,2)=6
加入縮容頻率控制后,本次實(shí)際縮容實(shí)例數(shù)為1,縮容后總實(shí)例數(shù)為6個(gè)。
目前彈性擴(kuò)縮容的頻率控制是對(duì)用戶放開的,用戶可以在hke平臺(tái)上修改如下參數(shù)即可。

容忍度
容忍度是對(duì)最大閾值和最小閾值設(shè)置的緩沖區(qū)間,相當(dāng)于將擴(kuò)縮容的閾值向外延展,這個(gè)參數(shù)可以避免閾值臨界值的噪音,保證服務(wù)穩(wěn)定性。目前wpa對(duì)所有應(yīng)用都設(shè)置的1%,加上容忍度后的閾值計(jì)算公式如下:
最大閾值公式:

目前Tolerance默認(rèn)為1%。
例如:highWaterMark為100m,Tolerance=1%,則添加容忍度后的擴(kuò)容觸發(fā)閾值=100*(1+0.01)=101m
最小閾值公式:

目前Tolerance默認(rèn)為1%。
例如:lowWaterMark為20m,Tolerance=1%,則添加容忍度后的縮容觸發(fā)閾值=20*(1-0.01)=19.8m
目前彈性伸縮的容忍緩沖是隱式配置的,默認(rèn)都為1%,也就是容忍實(shí)際負(fù)載在閾值上下限產(chǎn)生1%的波動(dòng)噪音。
告警和消息通知
哈啰的彈性伸縮控制器已經(jīng)接入了監(jiān)控和告警,并且控制器具備自愈和秒級(jí)擴(kuò)容的能力。對(duì)于業(yè)務(wù)方面,我們定制了兩種消息通知和一種告警,消息通知分別是彈性擴(kuò)縮情況通知和非pro環(huán)境的版本不一致通知,告警主要是對(duì)pro環(huán)境版本不一致告警。
擴(kuò)縮消息通知是為了讓用戶感知到控制器對(duì)服務(wù)的每一次操作,這樣可以讓用戶對(duì)服務(wù)當(dāng)前實(shí)例情況有一個(gè)準(zhǔn)確的了解,做到心中有“數(shù)”。服務(wù)版本不一致則會(huì)干擾彈性調(diào)度的目標(biāo)服務(wù)版本,導(dǎo)致擴(kuò)非所需,縮非所想,所以需要及時(shí)的告知用戶并調(diào)整預(yù)期服務(wù)版本。
