0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

vivo AI計算平臺的K8s分級配額管理實踐

jf_WZTOguxH ? 來源:AI前線 ? 作者:AI前線 ? 2023-03-07 09:56 ? 次閱讀

2018 年底,vivo AI 研究院為了解決統(tǒng)一高性能訓(xùn)練環(huán)境、大規(guī)模分布式訓(xùn)練、計算資源的高效利用調(diào)度等痛點,著手建設(shè) AI 計算平臺。經(jīng)過四年多的持續(xù)迭代,平臺建設(shè)和落地取得了很大進(jìn)展,成為 vivo AI 領(lǐng)域的核心基礎(chǔ)平臺。平臺從當(dāng)初服務(wù)深度學(xué)習(xí)訓(xùn)練為主,到現(xiàn)在演進(jìn)成包含 VTraining、VServing、VContainer 三大模塊,對外提供模型訓(xùn)練、模型推理和容器化能力的基礎(chǔ)設(shè)施。平臺的容器集群有數(shù)千個節(jié)點,擁有超過數(shù)百 PFLOPS 的 GPU 算力。集群里同時運行著數(shù)千個訓(xùn)練任務(wù)和數(shù)百個在線服務(wù)。本文是 vivo AI 計算平臺實戰(zhàn) 系列文章之一,主要分享了平臺在資源配額管理方面的實踐。

背 景

K8s 提供了原生的 ResourceQuota 資源配額管理功能,基于命名空間進(jìn)行配額管理,簡單易用。但是隨著平臺資源使用場景變得越來越復(fù)雜,例如多層級業(yè)務(wù)組織配額、針對具體 CPU 核和 GPU 卡的型號配額、資源使用時長配額等,ResourceQuota 變得難以應(yīng)對,平臺面臨業(yè)務(wù)資源爭搶、配額管理成本增加、定位問題效率變低等問題。

本文主要介紹平臺在 K8s 集群資源配額管理過程中遇到的問題,以及如何實現(xiàn)符合需求的配額管理組件:BizGroupResourceQuota —— 業(yè)務(wù)組資源配額(簡稱 bizrq),用于支撐平臺對復(fù)雜資源使用場景的配額管控。

ResourceQuota 資源配額管理遇到的問題

在使用 ResourceQuota 做資源配額管理時,有以下 4 個問題比較突出:

1、無法滿足有層級的業(yè)務(wù)組織架構(gòu)的資源配額管理

ResourceQuota 不能很好地應(yīng)用于樹狀的業(yè)務(wù)組織架構(gòu)場景,因為命名空間是扁平的,在實際場景中,我們希望將資源配額由父業(yè)務(wù)組到子業(yè)務(wù)組進(jìn)行逐級下發(fā)分配。

2、以 pod 對象的粒度限額可能導(dǎo)致只有部分 pod 創(chuàng)建成功

ResourceQuota 是以 pod 對象的粒度來進(jìn)行資源限額的,正常情況下在線服務(wù)或離線任務(wù)的部署,例如 deployment、argo rollout、tfjob 等,都需要批量創(chuàng)建 pod,可能會造成一部分 pod 由于額度不足而創(chuàng)建失敗的情形,導(dǎo)致部署無法完成甚至失敗,我們希望要么全部 pod 都創(chuàng)建成功,要么直接拒絕部署并提示資源額度不足,提升部署體驗。

3、無法針對具體 CPU 核和 GPU 卡的型號進(jìn)行配額管理

ResourceQuota 管理配額的資源粒度太粗,無法針對具體 CPU 核和 GPU 卡的型號進(jìn)行配額管理,在實際場景中,不同的 CPU、GPU 型號的性能、成本差異很大,需要分開進(jìn)行限額。例如我們會將 CPU 機(jī)器劃分為 A1、A2、A3、A4 等機(jī)型,GPU 機(jī)器也有 T4、V100、A30、A100 等機(jī)型,他們的性能和成本都是有差異的。

4、無法限制資源使用時長

ResourceQuota 僅能限制當(dāng)前時刻資源的已使用額度不能超過配額,但是并不能限制對資源的使用時長。在某些離線的深度模型訓(xùn)練場景,業(yè)務(wù)對 CPU、GPU 資源爭搶比較激烈,某些業(yè)務(wù)組希望能按 CPU 核時或 GPU 卡時的方式,給團(tuán)隊成員發(fā)放資源配額,比如每人每周發(fā)放 1000 GPU 卡時,表示可以用 10 張卡跑 100 小時,也可用 20 張卡跑 50 小時,以此類推。

BizGroupResourceQuota 分級配額管理方案

針對 ResourceQuota 配額管理所面臨的 4 個問題,我們設(shè)計了 BizGroupResourceQuota 配額管理 —— 業(yè)務(wù)組資源配額管理方案,后文簡稱為 bizrq。接下來,我們介紹一下 bizrq 方案。

我們通過 K8s crd(Custom Resource Define)來自定義 bizrq 資源對象(如下圖 bizrq 配額示例),從而定義 bizrq 的實現(xiàn)方案:

557efac6-bc7d-11ed-bfe3-dac502259ad0.png

如上圖配額示例所示,下面分別解釋一下 bizrq 配額方案的特點:

① bizrq name

bizrq name 在 cluster scope 全局唯一,bizrq 配額對象是集群范圍的,不跟命名空間相關(guān)聯(lián)。在實際業(yè)務(wù)場景中,bizrq name 可以跟業(yè)務(wù)組 ID 對應(yīng)起來,便于實現(xiàn)基于樹狀的業(yè)務(wù)組織架構(gòu)的配額管理。

②父 bizrq name

父 bizrq name 表示當(dāng)前 bizrq 的父級業(yè)務(wù)組的 bizrq 配額對象名稱,假如父 bizrq name 值設(shè)置了空字符串"",則表示當(dāng)前 bizrq 是 root 節(jié)點。當(dāng)創(chuàng)建非 root 的 bizrq 配額對象時,子 bizrq 的資源配額要從父 bizrq 的剩余額度中申請,并需要滿足相關(guān)約束條件才能創(chuàng)建成功,后面也會介紹實現(xiàn)原理。這樣就可以按常見的業(yè)務(wù)組織架構(gòu)來管理配額,構(gòu)成一顆“bizrq 樹”:

559785be-bc7d-11ed-bfe3-dac502259ad0.png

③默認(rèn)配額示例

默認(rèn)配額示例跟 ResourceQuota 的資源額度配置和限額效果是一致的,bizrq 借鑒了 ResourceQuota 的實現(xiàn),保持了一致的配置風(fēng)格和使用體驗。

④ CPU核型號配額示例

bizrq 支持將 CPU 核配額限制到具體型號,具體型號資源的已使用額度,也會累加到前綴相同的通用資源配額的已使用額度里,它們是可以結(jié)合使用的,如果都配置了則限額會同時生效。這樣即保留了原生 ResourceQuota 的限額功能,又新增了不同型號資源的限額。

舉例說明,比如將 limits.cpu 配額設(shè)置為 10 核,limits.cpu.A4 配額設(shè)置為 4 核,它們一開始已使用額度都是 0 核,當(dāng)我們的部署對象申請了 4 核的 A4 后,那么 limits.cpu 和 limits.cpu.A4 的已使用額度都會累加上這 4 核,因為 bizrq 會判斷 limits.cpu 是 limits.cpu.A4 的前綴資源,屬于通用資源類型,所以要一并計算。另外,此時業(yè)務(wù)組不能再申請 A4 型號的 cpu 資源了,因為 limits.cpu.A4 的剩余額度為 0,不過 limits.cpu 剩余額度還有 6 核,所以還可以申請非 A4 型號的 CPU 資源。

那么我們能否將 limits.cpu 配額設(shè)置為 10 核,將 limits.cpu.A4 配額設(shè)置為 100 核呢(limits.cpu < limits.cpu.A4)?是可以這樣配置的,但是將 limits.cpu.A4 配置為 100 核沒有意義,因為申請 A4 型號的 CPU 資源時,bizrq 也會分析前綴相同的 limits.cpu 的剩余額度是否足夠,如果額度不足那么任何型號的 CPU 資源都不能申請成功。

⑤GPU 卡型號配額示例

bizrq 支持將 GPU 卡配額限制到具體型號,具體型號資源的已使用額度,也會累加到前綴相同的通用資源的已使用額度里,它們是可以結(jié)合使用的,跟上面介紹的 CPU 核型號的限額行為也是一致的。

⑥GPU 卡時配額示例,⑦CPU 核時配額示例

bizrq 支持虛擬 cash 機(jī)制,可以給業(yè)務(wù)組分配 CPU 核時、GPU 卡時等“虛擬的貨幣”,比如給某個業(yè)務(wù)組分配 100CPU 核時:“cash/limits.cpu: 100”,(注意前綴 “cash/” 的表示)表示這個業(yè)務(wù)組的業(yè)務(wù)可以用 1 個 CPU 核跑 100 小時,也可以用 2 個 CPU 核跑 50 小時,還可以用 100 個 CPU 核跑 1 小時,GPU 卡時的定義可以類比。

⑧指定 Deployment 關(guān)聯(lián)的配額對象(bizrq name)

bizrq 限額不以 pod 對象為粒度進(jìn)行限額,而是以 deployment、argo rollout、tfjob 等上層部署對象的粒度來限額,需要通過部署對象 label “biz.group.resource.quota”來關(guān)聯(lián) bizrq 配額對象(注意不是通過命名空間進(jìn)行關(guān)聯(lián),因為 bizrq 是 cluster scope 的)。

通過攔截上層部署對象的創(chuàng)建、更新操作進(jìn)行資源額度的校驗(通過 validating webhook 攔截,后文會介紹實現(xiàn)原理),當(dāng)關(guān)聯(lián) bizrq 的剩余資源額度充足時,允許上層對象的創(chuàng)建;當(dāng)剩余資源額度不足時,拒絕上層對象的創(chuàng)建,從而防止只有部分 pod 創(chuàng)建成功的情形。

⑨⑩指定部署對象關(guān)聯(lián)的配額對象(bizrq name)

為了達(dá)到具體的 CPU 核型號或 GPU 卡型號的限額目的,也要給部署對象打上聲明具體的資源型號的 label,例如:biz.group.resource.quota/cpu-type: "A4",表示部署對象申請的是 A4 型號的 CPU 核;或 GPU 卡型號的 label,例如:biz.group.resource.quota/gpu-type: "A30",表示部署對象申請的是 A30 型號的 GPU 卡。

備注:bizrq 同樣支持具體的內(nèi)存類型的限額,使用方式與 CPU 核、GPU 卡類型的限額類似,比如可以設(shè)置 limits.memory.A4 的額度,然后給部署對象打上聲明具體的資源型號的 label:biz.group.resource.quota/memory-type: "A4" 即可。

配額機(jī)制實現(xiàn)原理

bizrq 方案在 ResourceQuota 所支持的基礎(chǔ)資源配額的基礎(chǔ)上,增加了(1)父子關(guān)系的表示,(2)CPU 核和 GPU 卡型號的限額,(3)核時、卡時的限額,(4)針對上層的部署資源對象進(jìn)行額度校驗,而不是針對單個 pod;同時 bizrq 對象的配置風(fēng)格和 ResourceQuota 對象的配置風(fēng)格是一樣的,易于配置和理解。所以 bizrq 分級配額的實現(xiàn)思路應(yīng)該也是可以借鑒 ResourceQuota 的,在分析 bizrq 實現(xiàn)前,我們先分析下 ResourceQuota 的實現(xiàn)原理,以便借鑒官方優(yōu)秀的實現(xiàn)思路。

原生 ResourceQuota 實現(xiàn) 整體架構(gòu)

55a72c6c-bc7d-11ed-bfe3-dac502259ad0.png

如上圖所示,APIServer 接收到資源對象請求后,由訪問控制鏈路中的處理器按順序進(jìn)行處理,請求順利通過訪問控制鏈路的處理后,資源對象的變更才允許被持久化到 etcd,它們依次是認(rèn)證(authentication)→鑒權(quán)(authorization)→變更準(zhǔn)入控制(mutating admission)→對象 Schema 校驗(object schema validation)→驗證準(zhǔn)入控制(validating admission)→etcd 持久化,我們需要重點關(guān)注“準(zhǔn)入控制”環(huán)節(jié):

? 變更準(zhǔn)入控制(mutating admission):對請求的資源對象進(jìn)行變更。例如內(nèi)置的 ServiceAccount admission controller,會將 pod 的默認(rèn) ServiceAccount 設(shè)為 default,并為每個容器添加 volumeMounts,掛載至 /var/run/secrets/kubernetes.io/serviceaccount,以便在 pod 內(nèi)部可以讀取到身份信息來訪問當(dāng)前集群的 apiserver。變更準(zhǔn)入控制是可擴(kuò)展的,我們可以通過配置 mutating admission webhooks 將自定義的邏輯加入到變更準(zhǔn)入控制鏈路中;

? 驗證準(zhǔn)入控制(validating admission):對請求的資源對象進(jìn)行各種驗證。此環(huán)節(jié)不會去變更資源對象,而是做一些邏輯校驗,例如接下來要分析的 QuotaAdmission Controller,會攔截 pod 的創(chuàng)建請求,計算 pod 里容器申請的資源增量是否會導(dǎo)致超額。驗證準(zhǔn)入控制也是可擴(kuò)展的,我們可以通過配置 validating admission webhooks 將自定義的邏輯加入到驗證準(zhǔn)入控制鏈路中。

如上圖所示,ResourceQuota 限額機(jī)制主要由兩個組件組成:

? ResourceQuota Controller:ResourceQuota Controller 是內(nèi)置在 controller manager 眾多 Controllers 中的一個,主要負(fù)責(zé)監(jiān)聽資源對象,如 pod、service 等變更事件,也包括 ResourceQuota 對象的變更事件,以便及時刷新關(guān)聯(lián)的 ResourceQuota 的 status 狀態(tài);

? QuotaAdmission Controller:QuotaAdmission 是內(nèi)置在 apiserver 請求訪問控制鏈路驗證準(zhǔn)入控制環(huán)節(jié)的控制器,QuotaAdmission 主要負(fù)責(zé)攔截資源對象的創(chuàng)建、更新請求,對關(guān)聯(lián) ResourceQuota 的額度進(jìn)行校驗。

下面以 Pod 對象的限額為例,分析 ResourceQuota 限額機(jī)制原理。其他資源,比如 service、pvc 等對象限額的實現(xiàn)思路基本一致,只不過不同對象有不同的資源計算邏輯實現(xiàn)(Evaluator)。

ResourceQuota Controller

當(dāng)集群的 controller manager 進(jìn)程啟動后,選主成功的那個 controller manager leader 就會將包括 ResourceQuota Controller 在內(nèi)的所有內(nèi)置的 Controller 跑起來,ResourceQuota Controller 會以生產(chǎn)者 - 消費者的模式,不斷刷新集群命名空間的 ResourceQuota 對象的資源使用狀態(tài):

560a5cce-bc7d-11ed-bfe3-dac502259ad0.png

作為任務(wù)生產(chǎn)者,為了及時把需要刷新狀態(tài)的 ResourceQuota 放到任務(wù)隊列,ResourceQuota Controller 主要做了以下 3 件事情:

· 監(jiān)聽 pod 對象的事件,當(dāng)監(jiān)聽到 pod 由占用資源狀態(tài)變更為不占資源狀態(tài)時,比如 status 由 terminating 變?yōu)?Failed、Succeeded 狀態(tài),或者 pod 被刪除時,會將 pod 所在命名空間下關(guān)聯(lián)的所有 ResourceQuota 放入任務(wù)隊列;

·監(jiān)聽 ResourceQuota 對象的創(chuàng)建、spec.Hard 更新(注意會忽略 status 更新事件,主要是為了將 spec.Hard 刷新到 status.Hard)、delete 等事件,將對應(yīng)的 ResourceQuota 放入任務(wù)隊列;

·定時(默認(rèn) 5m,可配置)把集群所有的 ResourceQuota 放入任務(wù)隊列,確保 ResourceQuota 的狀態(tài)最終是跟命名空間實際資源使用情況一致的,不會因為各種異常情況而出現(xiàn)長期不一致的狀態(tài)。

作為任務(wù)消費者,ResourceQuota Controller 會為任務(wù)隊列啟動若干 worker 協(xié)程(默認(rèn) 5 個,可配置),不斷從任務(wù)隊列取出 ResourceQuota,計算 ResourceQuota 所在命名空間所有 pod 的容器配置的資源量來刷新 ResourceQuota 的狀態(tài)信息。

QuotaAdmission Controller

對于 pod 的限額,QuotaAdmission 只會攔截 Pod create 操作,不會攔截 update 操作,因為當(dāng)部署對象的 pod 容器資源申請被變更后,原 pod 是會被刪除并且創(chuàng)建新 pod 的,pod 的刪除操作也不用攔截,因為刪除操作肯定不會導(dǎo)致超額。

當(dāng) QuotaAdmission 攔截到 Pod 的創(chuàng)建操作后,會找出對應(yīng)命名空間所有相關(guān)聯(lián)的 ResourceQuota,并分析創(chuàng)建 pod 會不會造成資源使用超額,只要有一個 ResourceQuota 會超額,那么就拒絕創(chuàng)建操作。如果所有的 ResourceQuota 都不會超額,那么先嘗試更新 ResourceQuota 狀態(tài),即是將此個 pod 的容器配置的資源量累加到 ResourceQuota status.Used 里,更新成功后,放行此次 pod 操作。

問題分析

總的來說,ResourceQuota Controller 負(fù)責(zé)監(jiān)聽各類對象的變更事件,以便能及時刷新對應(yīng)的 ResourceQuota status 狀態(tài),而 QuotaAdmission 則負(fù)責(zé)攔截對象操作,做資源額度的校驗。有幾個比較關(guān)鍵的問題我們需要分析一下,這幾個問題也是 bizrq 實現(xiàn)的關(guān)鍵點:

1、ResourceQuota status 的并發(fā)安全問題

ResourceQuota Controller 會不斷刷新 ResourceQuota status 里各類資源使用量,所有 apiserver 進(jìn)程的 QuotaAdmission 也會根據(jù) ResourceQuota status 校驗是否超額,并將校驗通過的 pod 資源增量累加到 ResourceQuota status.Used,所以 status 的更新是非常頻繁的,這就會導(dǎo)致并發(fā)安全問題:

假設(shè)某個業(yè)務(wù)組 CPU 配額有 10 核,已使用量為 1 核,業(yè)務(wù) A 和業(yè)務(wù) B 同時請求申請 5 核,此時對剩余額度的校驗都是足夠的(剩余 CPU 核數(shù)為 10-1=9),兩個請求都會將 CPU 已使用量更新為 1+5=6 核,并且都申請成功,這將導(dǎo)致限額失效,因為實際的資源申請已經(jīng)超額了(1+5+5=11 核)。

解決辦法通常有以下 3 種方式:

·方式 1:將所有對 status 的訪問邏輯打包成任務(wù)放入隊列,并通過單點保證全局按順序進(jìn)行處理;

·方式 2:將所有對 status 的訪問邏輯加鎖,獲取到鎖才可以進(jìn)行處理,保證任何時刻都不會有并發(fā)的訪問;

·方式 3:通過樂觀鎖來確保對 status 的訪問是安全的。

ResourceQuota 采用的是第三種方式,方式 1、2 都要引入額外的輔助手段,比如分布式隊列、分布式鎖,并且同一時刻只能處理一個請求,效率比較低下,容易造成處理延時,此外還要考慮 ResourceQuota Controller 跟 QuotaAdmission Controller 的協(xié)同(都會更新 ResourceQuota status.Used),在限額場景實現(xiàn)起來不是那么簡潔高效,而方式 3 可以直接基于 K8s 的樂觀鎖來達(dá)到目的,是最簡潔高效的方式。

K8s 樂觀鎖實現(xiàn)原理

K8s 樂觀鎖的實現(xiàn),有兩個前提:

·一是 K8s 資源對象的 resourceVersion 是基于 etcd 的 revision,revision 的值是全局單調(diào)遞增的,對任何 key 的修改都會使其自增;

·二是 apiserver 需要通過 etcd 的事務(wù) API,即 clientv3.KV.Txn(ctx).If(cmp1, cmp2, ......).Then(op1, op2, ......).Else(op1, op2, ......),來實現(xiàn)事務(wù)性的比較更新操作。

具體流程示例如下:

561b1eec-bc7d-11ed-bfe3-dac502259ad0.png

·第 1 步:client 端從 apiserver 查詢出對象 obj 的值 d0,對應(yīng)的 resourceVersion 為 v0;

·第 2 步:client 端本地處理業(yè)務(wù)邏輯后,調(diào)用 apiserver 更新接口,嘗試將對象 obj 的值 d0 更新為 dx;

·第 3 步:apiserver 接受到 client 端請求后,通過 etcd 的 if...then...else 事務(wù)接口,判斷在 etcd 里對象 obj 的 resourceVersion 值是否還是 v0,如果還是 v0,表示對象 obj 沒被修改過,則將對象 obj 的值更新為 dx,并為對象 obj 生成最新的 resourceVersion 值 v1;如果 etcd 發(fā)現(xiàn)對象 obj 的 resourceVersion 值已經(jīng)不是 v0,那么表示對象 obj 已經(jīng)被修改過,此時 apiserver 將返回更新失??;

·第 4 步:對于 client 端,apiserver 返回更新成功時,對象 obj 已經(jīng)被成功更新,可以繼續(xù)處理別的業(yè)務(wù)邏輯;如果 apiserver 返回更新失敗,那么可以選擇重試,即重復(fù) 1~4 步的操作,直到對象 obj 被成功。

apiserver 的更新操作實現(xiàn)函數(shù):

https://github.com/kubernetes/kubernetes/blob/v1.20.5/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go

56251c6c-bc7d-11ed-bfe3-dac502259ad0.png

相對于方式 2 的鎖(悲觀鎖),樂觀鎖不用去鎖整個請求操作,所有請求可以并行處理,更新數(shù)據(jù)的操作可以同時進(jìn)行,但是只有一個請求能更新成功,所以一般會對失敗的操作進(jìn)行重試,比如 QuotaAdmission 攔截的用戶請求,加了重試機(jī)制,重試多次不成功才返回失敗,在并發(fā)量不是非常大,或者讀多寫少的場景都可以大大提升并發(fā)處理效率。

QuotaAdmission 中樂觀鎖重試機(jī)制實現(xiàn)函數(shù)是 checkQuotas,感興趣可自行閱讀源碼:

https://github.com/kubernetes/kubernetes/blob/v1.20.5/staging/src/k8s.io/apiserver/pkg/admission/plugin/resourcequota/controller.go

568565a4-bc7d-11ed-bfe3-dac502259ad0.png

另外,更新沖突在所難免,客戶端可以借助 client-go 的 util 函數(shù) RetryOnConflict 來實現(xiàn)失敗重試:

https://github.com/kubernetes/client-go/blob/kubernetes-1.20.5/util/retry/util.go

569b579c-bc7d-11ed-bfe3-dac502259ad0.png

2、超額問題

問題:QuotaAdmission 校驗 pod 創(chuàng)建是否超額時,查詢出來的 ResourceQuota 的 status.Used 狀態(tài)能否反映命名空間下資源最新實際使用量,會不會造成超額情形?

問題分析:

由于 ResourceQuota Controller 和 QuotaAdmission 都會不斷刷新 ResourceQuota 的 status.Used 狀態(tài),并且 QuotaAdmission 基本是通過 informer cache 來獲取 ResourceQuota 的,informer 監(jiān)聽會有延遲,所以校驗額度時查詢的 ResourceQuota status 狀態(tài)可能并不是準(zhǔn)確反映命名空間下資源最新實際使用量,概括起來有以下 3 種不一致的情況:

·(1) informer 事件監(jiān)聽延遲,查詢到的可能不是 etcd 最新的 ResourceQuota;

·(2)即使查詢到的是 etcd 最新的 ResourceQuota,在額度校驗過程中,ResourceQuota 也有可能先被別的請求或被 ResourceQuota controller 修改掉,那么 ResourceQuota 數(shù)據(jù)也是“過期”的;

·(3)即使查詢到的是 etcd 最新的 ResourceQuota,并且在額度校驗過程中,ResourceQuota 數(shù)據(jù)沒有被修改,這個查詢出來的 etcd 最新的 ResourceQuota,也有可能不是最新的數(shù)據(jù),因為 ResourceQuota controller 刷新 ResourceQuota 可能不是那么及時。

對于(1)、(2)這兩種情形,由于 QuotaAdmission 基于 K8s 樂觀鎖做 ResourceQuota 資源額度的校驗及狀態(tài)更新,如果 ResourceQuota 不是 etcd 最新的,那么更新 ResourceQuota 狀態(tài)時會失敗,QuotaAdmission 將進(jìn)行重試,這樣就能保證只有用最新的 ResourceQuota 來做額度校驗,資源申請請求才能被通過;

對于(3)情形,ResourceQuota controller 刷新 ResourceQuota 可能不及時,但也不會造成超額,因為資源使用量的增加(例如 pod 創(chuàng)建操作)都是要通過 QuotaAdmission 攔截校驗并通過樂觀鎖機(jī)制將資源增量更新到 ResourceQuota,ResourceQuota controller 刷新 ResourceQuota 不及時,只會導(dǎo)致當(dāng)前 etcd 最新 ResourceQuota 的剩余額度比實際“偏小”(例如 pod 由 terminating 變?yōu)?Failed、Succeeded 狀態(tài),或者 pod 被刪除等情形),所以符合我們限額的目的。

當(dāng)然,如果手動把配額調(diào)小,那可能會人為造成超額現(xiàn)象,比如原先 CPU 配額 10 核,已使用 9 核,此時手動把配額改成 8 核,那么 QuotaAdmission 對于之后的 pod 創(chuàng)建的額度校驗肯定因為已經(jīng)超額,不會通過操作了,不過這個是預(yù)期可理解的,在實際業(yè)務(wù)場景也有用處。

3、全局刷新問題

問題:為什么要定時全量刷新集群所有的 ResourceQuota 狀態(tài)?

問題分析:

·定時全量刷新可以防止某些異常導(dǎo)致的 ResourceQuota status 跟實際的資源使用狀態(tài)不一致的情形,保證狀態(tài)回歸一致性。例如 QuotaAdmission 更新了 ResourceQuota 后(額度校驗通過后),實際的資源操作由于遇到異常而沒有成功的情形(如 pod 創(chuàng)建操作在后續(xù)環(huán)節(jié)失敗的場景);

·不過由于 ResourceQuota Controller 會不斷更新 ResourceQuota,在額度校驗通過后到實際資源操作被持久化前這段時間內(nèi)(這段時間很短),ResourceQuota 可能會被 ResourceQuota Controller 更新回舊值,如果后續(xù)有新的資源申請操作,就可能造成超額情形,不過出現(xiàn)這個問題的幾率應(yīng)該很小,通過業(yè)務(wù)層補償處理(比如超額告警、回收資源)即可。

關(guān)于 ResourceQuota Controller 和 QuotaAdmission 的代碼細(xì)節(jié),感興趣可以自行閱讀:

https://github.com/kubernetes/kubernetes/blob/v1.20.5/cmd/kube-controller-manager/app/core.go

56b94248-bc7d-11ed-bfe3-dac502259ad0.png

https://github.com/kubernetes/kubernetes/blob/v1.20.5/staging/src/k8s.io/apiserver/pkg/admission/plugin/resourcequota/admission.go

56d1e8c0-bc7d-11ed-bfe3-dac502259ad0.png

bizrq 實現(xiàn)

在分析了 ResourceQuota 的實現(xiàn)原理后,我們再看看如何實現(xiàn) bizrq 的限額方案。

整體架構(gòu)

參考 ResourceQuota 的實現(xiàn),我們可以將 bizrq 按功能職責(zé)劃分成 3 個模塊:

56dbbe54-bc7d-11ed-bfe3-dac502259ad0.png

bizrq controller

負(fù)責(zé)監(jiān)聽相關(guān)事件,及時刷新 bizrq 狀態(tài)以及定時(默認(rèn) 5m)刷新整個集群的 bizrq 狀態(tài)。

·監(jiān)聽 bizrq 的創(chuàng)建、更新(Spec.Hard 變更),及時刷新 bizrq status;

·監(jiān)聽部署對象,例如 deployment、argo rollout、tfjob 等對象的更新(這里僅監(jiān)聽 biz.group.resource.quota label 配置的更新)、刪除事件,及時刷新關(guān)聯(lián)的 bizrq status。

bizrq validating admission webhook(server)

基于 apiserver 的驗證準(zhǔn)入控制 webhook,負(fù)責(zé)攔截 bizrq 的增刪改事件,做 bizrq 本身以及 bizrq parent 相關(guān)的約束性校驗,從而實現(xiàn)分級配額。

·攔截創(chuàng)建事件:對 bizrq name 做重名校驗,確保 cluster scope 全局唯一;然后進(jìn)行父 bizrq 校驗;

·攔截更新事件:確保父 parentBizGroup 字段不能變更,不能更改父 bizrq;然后進(jìn)行父 bizrq 校驗;

·攔截刪除事件:確保只能刪除葉子節(jié)點的 bizrq,防止不小心將一棵樹上所有的 bizrq 刪除;然后進(jìn)行父 bizrq 校驗;

·父 bizrq 校驗:校驗非 root bizrq(Spec.ParentBizGroup 字段不為 "")的父 bizrq 的相關(guān)約束條件是否滿足:

父 bizrq 必須已存在;

子 bizrq 必須包含父 bizrq 配額配置的所有資源類型名稱 key(Spec.Hard 的 key 集合),父 bizrq 的配額配置是子 bizrq 配額配置的子集,例如父 bizrq 包含 limits.cpu.A4,則創(chuàng)建子 bizrq 時也必須包含 limits.cpu.A4;

父 bizrq 的剩余額度必須足夠,子 bizrq 申請的額度是從父 bizrq 里扣取的額度。如果父 bizrq 不會超額,則先更新父 bizrq 的 status,更新成功才表示從父 bizrq 申請到了額度。

object validating admission webhooks(server)

基于 apiserver 的驗證準(zhǔn)入控制 webhook,負(fù)責(zé)攔截需要限額的資源對象的創(chuàng)建和更新事件,例如 deployment、argo rollout、tfjob 等對象的攔截,做額度校驗,從而實現(xiàn)部署對象的限額校驗,而不是 pod 對象粒度的校驗。

攔截到部署對象的請求時,從部署對象提取出以下信息,就能計算各類資源的增量:

·資源型號(如果有指定具體型號的話),從 label biz.group.resource.quota/cpu-type 或 biz.group.resource.quota/gpu-type 提取;

·部署對象的副本數(shù)(obj.Spec.Replicas);

·PodTemplateSpec,用來計算容器配置的資源量。

然后再判斷部署對象的請求是否會導(dǎo)致關(guān)聯(lián)的 bizrq 超額(從 label biz.group.resource.quota 提取關(guān)聯(lián)的 bizrq name),如果 bizrq 不會超額,那么先嘗試更新 bizrq 資源使用狀態(tài),更新成功后,放行此次請求操作。

關(guān)于核時、卡時的限額實現(xiàn)

controller 會不斷刷新 bizrq 的 status,bizrq 的 status 跟 ResourceQuota 的 status 有點不一樣,bizrq 的 status 添加了一些輔助信息,用來計算核時、卡時的使用狀態(tài)(cash 額度使用狀態(tài)):

56fbac0a-bc7d-11ed-bfe3-dac502259ad0.png

對比 ResourceQuota 的 status:

·bizrq status 包含了 ResourceQuota 也有的 hard、used 字段;

·增加 selfUsed 字段來記錄 bizrq 本身(業(yè)務(wù)組本身)已使用資源量,selfUsed 不包含子 bizrq 所申請的配額,并滿足關(guān)系:selfUsed + 子 bizrq 申請的配額 = used;

·增加 lastReconcileTime 字段來記錄 controller 最后一次刷新 bizrq status 的時間,因為計算核時、卡時是要基于資源量乘于使用時間來計算的,而且是要基于 selfUsed 累加的。計算 cash 時,controller 會將部署對象包含的所有 pod 查詢出來,然后結(jié)合 lastReconcileTime、pod 開始占用資源的時間點(成功調(diào)度到節(jié)點上)、pod 釋放資源的時間點(比如 Succeeded、Failed),再結(jié)合 pod 容器配置的資源量,從而計算出整個 bizrq 業(yè)務(wù)組較準(zhǔn)確的 cash 使用量。注意 cash 的計算不是百分百精確的,有些情形,例如 pod 被刪除了,那么下個計算周期這個 pod 的 cash 就不會累加到 status 了,不過由于 controller 更新 status 的頻率很高(最遲每 5m 更新一次),所以少量誤差并不會影響大部分業(yè)務(wù)場景下的 cash 限額的需求;

·object validating admission webhooks 攔截到請求后進(jìn)行核時、卡時的額度校驗時,判斷 bizrq.status.used 里相關(guān)資源類型的 cash(如 cash/limits.cpu)是否已經(jīng)大于等于 bizrq.status.hard 里配置的配額,是則表示 cash 已經(jīng)超額,直接拒絕攔截的請求即可;

·另外,當(dāng) cash 已經(jīng)超額時,并不能強(qiáng)制把相應(yīng)業(yè)務(wù)組正在運行的業(yè)務(wù)停掉,而是可以通過監(jiān)控告警的等手段通知到業(yè)務(wù)方,由業(yè)務(wù)方自行決定如何處置。

開發(fā)工具簡介

kubebuilder

Kubebuilder 是一個基于 CRD 搭建 controller、admission webhook server 腳手架的工具,可以按 K8s 社區(qū)推薦的方式來擴(kuò)展實現(xiàn)業(yè)務(wù)組件,讓用戶聚焦業(yè)務(wù)層邏輯,底層通用的邏輯已經(jīng)直接集成到腳手架里。

bizrq 主要用到了 kubebuilder 的以下核心組件 / 功能:

· manager:負(fù)責(zé)管理 controller、webhook server 的生命周期,初始化 clients、caches,當(dāng)我們要集成不同資源對象的 client、cache 時,只需寫一行代碼將資源對象的 schema 注冊一下就可以了;

· caches:根據(jù)注冊的 scheme(schema 維護(hù)了 GVK 與對應(yīng) Go types 的映射) 同步 apiserver 中所有關(guān)心的 GVK(對象類型)的 GVR(對象實例),維護(hù) GVK -> Informer 的映射;

· clients:封裝了對象的增刪改查操作接口,執(zhí)行查詢操作時,底層會通過 cache 查詢,執(zhí)行變更類操作時,底層會訪問 apiserver;

· indexer:可以通過 indexer 給 cache 加索引提升本地查詢效率,例如實現(xiàn) bizrq 功能時,可以通過建立父子關(guān)系索引,方便通過父查找所有子 bizrq;

· controller:controller 的腳手架,提供腳手架讓業(yè)務(wù)層只需要關(guān)注感興趣的事件(任務(wù)生產(chǎn)),及實現(xiàn) Reconcile 方法(任務(wù)消費),以便對 crd 定義的對象進(jìn)行狀態(tài)調(diào)和;

· webhook server:webhook server 的腳手架,業(yè)務(wù)層只需要實現(xiàn) Handler 接口,以便對攔截的請求做處理。

code-generator

實現(xiàn)了 bizrq 組件之后,可以通過 code-generator 工具生成 bizrq 的 informer、lister、clientset 等客戶端代碼,以便其他應(yīng)用進(jìn)行集成:

· client-gen:生成 crd 對象的標(biāo)準(zhǔn)操作方法:get、list、create、update、patch、delete、deleteCollection、watch 等;

· informer-gen: 生成監(jiān)聽 crd 對象相關(guān)事件的 informer;

· lister-gen: 生成緩存層只讀的 get、list 方法。

落地情況及后續(xù)規(guī)劃

目前 bizrq 分級配額管理方案已經(jīng)在平臺的在線業(yè)務(wù)場景全面落地,我們基于 bizrq 組件,對在線業(yè)務(wù)的 argo rollout 部署對象進(jìn)行攔截和額度校驗,結(jié)合在線業(yè)務(wù)場景中的“項目 - 服務(wù) - 流水線”等層級的配額管理需求,實現(xiàn)了分級配額管理界面,讓用戶可以自行管理各層級的資源配額,從而解決業(yè)務(wù)資源爭搶、減輕了平臺資源管理壓力、提高了相關(guān)問題的定位和解決效率。

后續(xù)我們將持續(xù)完善 bizrq 組件的功能,例如:

·以插件式的方式支持更多種類對象的攔截和額度校驗(例如離線訓(xùn)練任務(wù) tfjob、有狀態(tài)部署對象 statefulset 等),從而使 bizrq 分級配額管理組件能落地到更多的離在線業(yè)務(wù)場景中;

·完善自動擴(kuò)縮容(HPA)場景下的額度校驗。目前我們是通過分析用戶 HPA 配置的最大副本數(shù)是否會導(dǎo)致超額,來判斷用戶配置的值是否合理,后續(xù)可以給 bizrq 增加一個 validating admission webhook,通過攔截 scale 對象的方式來進(jìn)行額度校驗,從而讓校驗邏輯更加健壯、完整。

另外,我們也會結(jié)合實際的業(yè)務(wù)場景,完善上層功能的使用體驗。例如優(yōu)化資源額度的遷入遷出、借用流程;完善資源碎片分析、給業(yè)務(wù)推薦更加合理的部署資源套餐等能力,提升用戶資源配額管理的體驗和效率。

審核編輯 :李倩

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10768

    瀏覽量

    210417
  • AI
    AI
    +關(guān)注

    關(guān)注

    87

    文章

    29347

    瀏覽量

    267630
  • 計算平臺
    +關(guān)注

    關(guān)注

    0

    文章

    50

    瀏覽量

    9603
  • vivo
    +關(guān)注

    關(guān)注

    12

    文章

    3274

    瀏覽量

    62972
  • 深度學(xué)習(xí)
    +關(guān)注

    關(guān)注

    73

    文章

    5429

    瀏覽量

    120787

原文標(biāo)題:vivo AI 計算平臺的 K8s 分級配額管理實踐

文章出處:【微信號:AI前線,微信公眾號:AI前線】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    k8s核心原理學(xué)習(xí)指南3

    k8s學(xué)習(xí)3 - 核心原理
    發(fā)表于 09-25 16:37

    搭建K8s環(huán)境平臺的步驟

    1 搭建K8s環(huán)境平臺規(guī)劃1.1 單master集群1.2 多master集群
    發(fā)表于 11-04 06:03

    OpenStack與K8s結(jié)合的兩種方案的詳細(xì)介紹和比較

    OpenStack與K8S結(jié)合主要有兩種方案。一是K8S部署在OpenStack平臺之上,二是K8S和OpenStack組件集成。
    的頭像 發(fā)表于 10-14 09:38 ?2.7w次閱讀

    如何使用kubernetes client-go實踐一個簡單的與K8s交互過程

    【導(dǎo)讀】Kubernetes項目使用Go語言編寫,對Go api原生支持非常便捷。 本篇文章介紹了如何使用kubernetes client-go實踐一個簡單的與K8s交互過程
    的頭像 發(fā)表于 02-02 11:16 ?6649次閱讀
    如何使用kubernetes client-go<b class='flag-5'>實踐</b>一個簡單的與<b class='flag-5'>K8s</b>交互過程

    Docker不香嗎為什么還要用K8s

    Docker 雖好用,但面對強(qiáng)大的集群,成千上萬的容器,突然感覺不香了。 這時候就需要我們的主角 Kubernetes 上場了,先來了解一下 K8s 的基本概念,后面再介紹實踐,由淺入深步步為營
    的頭像 發(fā)表于 06-02 11:56 ?3369次閱讀

    簡單說明k8s和Docker之間的關(guān)系

    這篇文章主要介紹了k8s和Docker關(guān)系簡單說明,本文利用圖文講解的很透徹,有需要的同學(xué)可以研究下 最近項目用到kubernetes(以下簡稱k8s,ks之間有
    的頭像 發(fā)表于 06-24 15:48 ?3301次閱讀

    K8S集群服務(wù)訪問失敗怎么辦 K8S故障處理集錦

    問題1:K8S集群服務(wù)訪問失??? ? ? 原因分析:證書不能被識別,其原因為:自定義證書,過期等。 解決方法:更新證書即可。 問題2:K8S集群服務(wù)訪問失??? curl: (7) Failed
    的頭像 發(fā)表于 09-01 11:11 ?1.6w次閱讀
    <b class='flag-5'>K8S</b>集群服務(wù)訪問失敗怎么辦 <b class='flag-5'>K8S</b>故障處理集錦

    K8S(kubernetes)學(xué)習(xí)指南

    K8S(kubernetes)學(xué)習(xí)指南
    發(fā)表于 06-29 14:14 ?0次下載

    mysql部署在k8s上的實現(xiàn)方案

    的 RDBMS (Relational Database Management System,關(guān)系數(shù)據(jù)庫管理系統(tǒng)) 應(yīng)用軟件之一。這里主要講 mysql 部署在 k8s 上,mysql 部署在 k8s 上的優(yōu)勢主要有以下幾點。
    的頭像 發(fā)表于 09-26 10:39 ?2404次閱讀

    k8s是什么意思?kubeadm部署k8s集群(k8s部署)|PetaExpres

    k8s是什么意思? kubernetes簡稱K8s,是一個開源的,用于管理平臺中多個主機(jī)上的容器化的應(yīng)用,Kubernetes的目標(biāo)是讓部署容器化的應(yīng)用簡單并且高效(powerful
    發(fā)表于 07-19 13:14 ?1065次閱讀

    什么是K3sK8s?K3sK8s有什么區(qū)別?

    Kubernetes,通??s寫為 K8s,是領(lǐng)先的容器編排工具。該開源項目最初由 Google 開發(fā),幫助塑造了現(xiàn)代編排的定義。該系統(tǒng)包括了部署和運行容器化系統(tǒng)所需的一切。
    的頭像 發(fā)表于 08-03 10:53 ?6924次閱讀

    k8s生態(tài)鏈包含哪些技術(shù)

    1. Apache APISIX Ingress 定義 ? 在 K8s 生態(tài)中,Ingress 作為表示 K8s 流量入口的一種資源,想要讓其生效,就需要有一個 Ingress Controller
    的頭像 發(fā)表于 08-07 10:56 ?1109次閱讀
    <b class='flag-5'>k8s</b>生態(tài)鏈包含哪些技術(shù)

    跑大模型AIK8s與普通K8s的區(qū)別分析

    Kubernetes是一個在大量節(jié)點上管理容器的系統(tǒng),其主要功能總結(jié)起來,就是在想要啟動容器的時候,負(fù)責(zé)“找一個「空閑」節(jié)點,啟動容器”。但是它默認(rèn)考慮的啟動因素(資源類)主要就是“CPU+內(nèi)存”。就是容器指定“我要多少CPU+多少內(nèi)存”,然后K8s找到符合這個要求的節(jié)點
    發(fā)表于 09-03 12:07 ?813次閱讀

    K8s多集群管理:為什么需要多集群、多集群的優(yōu)勢是什么

    隨著K8s和云原生技術(shù)的快速發(fā)展,以及各大廠商在自己的數(shù)據(jù)中心使用K8s的API進(jìn)行容器化應(yīng)用編排和管理,讓應(yīng)用交付本身變得越來越標(biāo)準(zhǔn)化和統(tǒng)一化,并且實現(xiàn)了與底層基礎(chǔ)設(shè)施的完全解耦,為多集群和混合云提供了一個堅實技術(shù)基礎(chǔ)。
    發(fā)表于 09-14 10:48 ?1076次閱讀
    <b class='flag-5'>K8s</b>多集群<b class='flag-5'>管理</b>:為什么需要多集群、多集群的優(yōu)勢是什么

    K8S落地實踐經(jīng)驗分享

    k8s 即 Kubernetes,是一個開源的容器編排引擎,用來對容器化應(yīng)用進(jìn)行自動化部署、 擴(kuò)縮和管理
    的頭像 發(fā)表于 01-02 11:45 ?955次閱讀
    <b class='flag-5'>K8S</b>落地<b class='flag-5'>實踐</b>經(jīng)驗分享