作者:vivo 互聯(lián)網(wǎng)服務(wù)器團隊 - Chen Han
容器平臺針對業(yè)務(wù)資源申請值偏大的運營問題,通過靜態(tài)超賣和動態(tài)超賣兩種技術(shù)方案,使業(yè)務(wù)資源申請值趨于合理化,提高平臺資源裝箱率和資源利用率。
一、背景
在Kubernetes中,容器申請資源有request和limit概念來描述資源請求的最小值和最大值。
requests值在容器調(diào)度時會結(jié)合節(jié)點的資源容量(capacity)進(jìn)行匹配選擇節(jié)點。
limits表示容器在節(jié)點運行時可以使用的資源上限,當(dāng)嘗試超用資源時,CPU會被約束(throttled),內(nèi)存會終止(oom-kill)。
總體而言,在調(diào)度的時候requests比較重要,在運行時limits比較重要。在實際使用時,容器資源規(guī)格 request 和 limit 的設(shè)置規(guī)格也一直都讓Kubernetes的用戶飽受困擾:
對業(yè)務(wù)運維人員:希望預(yù)留相當(dāng)數(shù)量的資源冗余來應(yīng)對上下游鏈路的負(fù)載波動,保障線上應(yīng)用的穩(wěn)定性。
對平臺人員:集群的資源裝箱率高,節(jié)點利用率低,存在大量的空閑資源無法調(diào)度,造成算力浪費。
二、現(xiàn)狀
2.1 vivo容器平臺介紹
vivo容器平臺基于Kubernetes技術(shù)對內(nèi)部業(yè)務(wù)提供容器服務(wù)。內(nèi)部業(yè)務(wù)統(tǒng)一在CICD平臺部署和管理容器資源,容器平臺自研的caas-openapi組件提供restful接口與CICD交互。
平臺通過標(biāo)簽,從資源維度邏輯上可以分為測試池、共享池、專有池、混部池。
測試池:為業(yè)務(wù)部署容器測試,一般非現(xiàn)網(wǎng)業(yè)務(wù),為業(yè)務(wù)測試提供便利。
共享池:為業(yè)務(wù)不感知物理機,類似公有云全托管容器服務(wù)。
專有池:為業(yè)務(wù)獨享物理機,類似公有云半托管容器服務(wù),業(yè)務(wù)方獨占資源,容器平臺維護(hù)。
混部池:為業(yè)務(wù)獨享物理機,在專有池基礎(chǔ)上,混部離線業(yè)務(wù),緩解離線資源缺口,提升整機利用率。

2.2 資源部署現(xiàn)狀和問題
vivo容器平臺的所有在線業(yè)務(wù)部署均要求設(shè)置request和limit,且request <= limit,默認(rèn)情況request等于limit。在共享池中,常見業(yè)務(wù)request設(shè)置會出現(xiàn)如下情況:
(1) 較少情況,業(yè)務(wù)設(shè)置較低的 request 值,而實際使用資源遠(yuǎn)大于它的 request 值,若大量pod調(diào)度一個節(jié)點,加劇節(jié)點熱點問題影響同節(jié)點其他業(yè)務(wù)。
(2)大多情況,業(yè)務(wù)按最大資源需求設(shè)置較高的 request 值,而實際使用資源長期遠(yuǎn)小于它的 request 值。業(yè)務(wù)側(cè)賬單成本高(按request計費),且容器異常退出時,重調(diào)度時可能因為平臺空閑資源碎片,導(dǎo)致大規(guī)格容器無法調(diào)度。這會導(dǎo)致,平臺側(cè)可調(diào)度資源少,但平臺整體節(jié)點資源利用率偏低。
對平臺和用戶方,request值設(shè)置合理很重要,但平臺無法直接判斷用戶設(shè)置request值合理性,所以無法首次部署時硬限制。

2.3 資源規(guī)格合理性思考
2.3.1 request怎么樣才是合理設(shè)置
request值接近業(yè)務(wù)實際使用量,例如用戶申請request為2核,limit為4核,實際真實使用量最多1核,那么合理request值設(shè)置為1核附近。但是業(yè)務(wù)真實使用量只有運行一段時間后才能評估,屬于后驗知識。
2.3.2 保障資源最大使用量
不修改limit值就能保障業(yè)務(wù)最大使用量符合業(yè)務(wù)預(yù)期。

三、解決方案探索
3.1 靜態(tài)超賣方案
思路:
靜態(tài)超賣方案是將CICD用戶申請規(guī)格的request按一定比例降低,根據(jù)平臺運營經(jīng)驗設(shè)置不同集群不同機房不同環(huán)境的靜態(tài)系數(shù),由caas-openapi組件自動修改。如下圖:

優(yōu)點:
首次部署時可以應(yīng)用,實現(xiàn)簡單。
缺點:
生產(chǎn)環(huán)境系數(shù)設(shè)置保守,導(dǎo)致request依然偏大,且由于內(nèi)存是不可壓縮資源,實際實施時為避免業(yè)務(wù)實例內(nèi)存oom-kill,靜態(tài)超賣只開啟了cpu維度,未開啟內(nèi)存靜態(tài)超賣。
3.2 動態(tài)超賣方案
3.2.1 方案思路
開發(fā)caas-recommender組件,基于業(yè)務(wù)監(jiān)控數(shù)據(jù)的真實資源用量來修正業(yè)務(wù)request值。
從監(jiān)控組件拉取各個容器資源的真實使用量。
通過算法模型得到業(yè)務(wù)申請量的推薦值。
業(yè)務(wù)重新部署時,使用推薦值修改業(yè)務(wù)request值。
3.2.2 半衰期滑動窗口模型
結(jié)合容器業(yè)務(wù)的特點,對推薦算法有如下要求:
當(dāng)workload負(fù)載上升時,結(jié)果需要快速響應(yīng)變化,即越新的數(shù)據(jù)對算法模型的影響越大;
當(dāng)workload負(fù)載下降時,結(jié)果需要推遲體現(xiàn),即越舊的數(shù)據(jù)對算法結(jié)果的影響越小。
半衰期滑動窗口模型可以根據(jù)數(shù)據(jù)的時效性對其權(quán)重進(jìn)行衰減,可以滿足上述要求。
詳細(xì)描述參考:google Borg Autopilot的moving window模型,參看原論文>>
公式如下:

其中 τ 為數(shù)據(jù)樣本的時間點,t1/2 為半衰期,表示每經(jīng)過 t1/2 時間間隔,前一個 t1/2 時間窗口內(nèi)數(shù)據(jù)樣本的權(quán)重就降低一半。
核心理念:在參考時間點之前的數(shù)據(jù)點,離的越遠(yuǎn)權(quán)重越低。在參考時間點之后的數(shù)據(jù)點權(quán)重越高。
半衰期halfLife:經(jīng)過時間halfLife后,權(quán)重值降低到一半。默認(rèn)的halfLife為24小時。
數(shù)據(jù)點的時間timestamp:監(jiān)控數(shù)據(jù)的時間戳。
參考時間referenceTimestamp:監(jiān)控數(shù)據(jù)上的某個時間(一般是監(jiān)控時間最近的零點00:00)。
衰減系數(shù)decayFactor:2^((timestamp-referenceTimestamp)/halfLife)
cpu資源的固定權(quán)重:CPU 使用量數(shù)據(jù)對應(yīng)的固定權(quán)重是基于容器 CPU request 值確定的。當(dāng) CPU request 增加時,對應(yīng)的固定權(quán)重也隨之增加,舊的樣本數(shù)據(jù)固定權(quán)重將相對減少。
memory資源的固定權(quán)重:由于內(nèi)存為不可壓縮資源,而內(nèi)存使用量樣本對應(yīng)的固定權(quán)重系數(shù)為1.0。
數(shù)據(jù)點權(quán)重 = 固定權(quán)重衰減系數(shù):*例如現(xiàn)在的數(shù)據(jù)點的權(quán)重為1,那么24小時之前的監(jiān)控數(shù)據(jù)點的權(quán)重為0.5,48小時前的數(shù)據(jù)點的權(quán)重為0.25,48小時后的數(shù)據(jù)權(quán)重為4。
3.2.3 指數(shù)直方圖計算推薦值
caas-recommender每個掃描周期(默認(rèn)1min)從 metrics server 或 prometheus 中獲取帶時間戳的樣本數(shù)據(jù),如 container 維度的 CPU、Memory 資源使用等。樣本數(shù)據(jù)結(jié)合權(quán)重值,為每個workload構(gòu)建指數(shù)直方圖,指數(shù)直方圖中每個桶的大小以指數(shù)速率逐步提升。指數(shù)直方圖的樣本存儲方式也便于定期checkpoint保存,可以顯著提升程序recover性能。如下圖:

指數(shù)直方圖的橫軸定義為資源量,縱軸定義為對應(yīng)權(quán)重,資源量統(tǒng)計間隔以5%左右的幅度增加。
桶的下標(biāo)為N,桶的大小是指數(shù)增加的bucketSize=0.01(1.05^N),下標(biāo)為0的桶的大小為0.01,容納范圍為[0,0.01),下標(biāo)為1的桶的大小為0.011.05^1=0.0105,容納范圍[0.01-0.0205)。[0.01,173]只需要兩百個桶即可完整保存。
將每個數(shù)據(jù)點,按照數(shù)值大小丟到對應(yīng)的桶中。
當(dāng)某個桶里增加了一個數(shù)據(jù)點,則這個桶的權(quán)重增加固定權(quán)重衰減系數(shù),所有桶的權(quán)重也增加固定權(quán)重衰減系數(shù)**。
計算出W(95)=95%*所有桶的總權(quán)重,如上圖僅考慮前4個桶,總權(quán)重為20,w(95)權(quán)重為19。
從最小的桶到最大桶開始累加桶的權(quán)重,這個權(quán)重記為S,當(dāng)S>=W(95)時候,這個時候桶的下標(biāo)為N,那么下標(biāo)為N+1桶的最小邊界值就是95百分位值,如上圖N=3時,S>=W(95),95百分位值即為0.01*1.05^2。
比如CPU波動較大且可壓縮,采用95%分位值(P95),內(nèi)存采用99%分位值(P99)。最終得到workload的資源推薦值。
3.2.4 caas-recommender組件流程圖

1. 啟動controller:profile Controller監(jiān)聽profile template crd,根據(jù)profile crd創(chuàng)建相應(yīng)維度的recommendation crd,可支持namepace\workload\pod維度。
2. 初始化:判斷是否有checkpoint,若無,可以選擇從prometheus拉取數(shù)據(jù)構(gòu)建直方圖。若有,由checkpoint直接recover。
3. loop循環(huán):
從recommendation crd中判斷哪些pod需要納管(pod labels)
根據(jù)pod label從Kubernetes獲取pod信息
根據(jù)pod的namespace從metrics server拉取監(jiān)控數(shù)據(jù),由container數(shù)據(jù)匯聚成pod用量數(shù)據(jù)。
構(gòu)建指數(shù)直方圖,填充pod用量數(shù)據(jù)和權(quán)重值。
根據(jù)直方圖的分位值計算推薦值
存儲推薦值和直方圖chekpoint
gc需要刪除的recommendation crd或者直方圖內(nèi)存等無用數(shù)據(jù)。
4.支持原生workload常用類型,拓展支持了OpenKruise相關(guān)workload類型。
3.2.5 推薦值校正規(guī)則
推薦值 = 模型推薦值 * 擴大倍數(shù)(可配置)
推薦值 < 原生request值:按照推薦值修改
推薦值 > 原始request值: 按照原始request修改
內(nèi)存是否修改可以通過配置
不修改workload的limit值
3.3 HPA利用率計算邏輯改造
Pod 水平自動擴縮(Horizontal Pod Autoscaler, 簡稱 HPA)可以基于 CPU/MEM 利用率自動擴縮workload的Pod數(shù)量,也可以基于其他應(yīng)程序提供的自定義度量指標(biāo)來執(zhí)行自動擴縮。
原生Kubernetes的HPA擴縮容利用率計算方式是基于request值。若資源超賣,request值被修改后,那么業(yè)務(wù)設(shè)置的HPA失靈,導(dǎo)致容器不符合預(yù)期擴縮容。
關(guān)于HPA是基于request還是基于limit,目前Kubernetes社區(qū)還存在爭論,相關(guān) issue 見72811。若需要使用limit計算利用率,可以修改kube-controller-manager源碼,或者使用自定義指標(biāo)來代替。
vivo容器平臺兼容業(yè)務(wù)物理機利用率邏輯,規(guī)定內(nèi)部統(tǒng)一監(jiān)控系統(tǒng)的Pod利用率均基于limit計算。
HPA改造思路:通過修改kube-controller-manager源碼方式實現(xiàn)基于limit維度計算。
在pod annotation中記錄設(shè)置值信息(request值和limit值),以及維度信息(request或limit維度)。
controller計算pod資源時,判斷是否有指定annotation,若有,解析annotation記錄值和維度信息計算利用率,若無,使用原生邏輯。
通過上述方式解耦HPA與pod request值,這樣平臺的資源超賣功能修改request不影響HPA自動擴縮預(yù)期。
3.4 專有池支持超賣能力
專有池物理機由業(yè)務(wù)自行運維管理,從平臺角度,不應(yīng)該隨意修改業(yè)務(wù)的容器request規(guī)格。但是專有池業(yè)務(wù)也有降低容器規(guī)格,部署更多業(yè)務(wù),復(fù)用資源,提高整機利用率的需求。平臺默認(rèn)所有共享池自動開啟超賣能力,專有池可配置選擇開啟超賣能力。
可自定義開啟超賣類型:靜態(tài)、動態(tài)、靜態(tài)+動態(tài)。
可自定義靜態(tài)系數(shù)、動態(tài)超賣擴大系數(shù)。
可配置是否自動修改超賣值,當(dāng)不自動生效可通過接口查詢推薦值,由業(yè)務(wù)自行修改。
3.5 整體方案
首次部署:
根據(jù)先驗知識評估,通過固定靜態(tài)系數(shù)修改request值,再根據(jù)部署后各個pod監(jiān)控用量數(shù)據(jù),生成workload的request推薦值。
再次部署:
若有推薦值,使用推薦值部署。無推薦值或者推薦值未生效時,使用靜態(tài)系數(shù)。

四、效果和收益
4.1 測試集群收益

原測試機器的靜態(tài)超賣系數(shù)很低,且只縮減cpu維度資源,導(dǎo)致集群內(nèi)存成為資源瓶頸。
開啟動態(tài)超賣能力4個月后,納管90%的workload,節(jié)點pod平均內(nèi)存request由4.07Gi下降到3.1Gi,內(nèi)存平臺裝箱率降低10%,有效緩解集群內(nèi)存不足問題。
4.2 共享池生產(chǎn)集群收益

原生產(chǎn)集群靜態(tài)超賣系數(shù)較高,CPU資源裝箱率高,導(dǎo)致集群的CPU成為瓶頸。
開啟動態(tài)超賣能力3個月后,納管60%的workload,節(jié)點pod平均cpu request由2.86降低為2.35,整體cpu利用率相比未開啟前提升8%左右。
五、總結(jié)與展望
vivo容器平臺通過資源超賣方案,將業(yè)務(wù)容器的request降低到合理值,降低業(yè)務(wù)使用成本,緩解了集群資源不足問題,達(dá)到了提升節(jié)點利用率目的。但是當(dāng)前僅在生產(chǎn)集群開啟了CPU資源超賣,規(guī)劃近期開啟內(nèi)存資源超賣。
未來基于上述方法,可以納管更多維度,比如GPU卡利用率再結(jié)合GPU虛擬化能力,從而提高GPU資源共享效率。根據(jù)動態(tài)超賣推薦值可以用于構(gòu)建用戶畫像,區(qū)分業(yè)務(wù)是計算型或內(nèi)存型,方便平臺更好理解用戶特性,輔助資源調(diào)度等。
參考資料: