作者:常懷鑫、丁天琛
**讓人討厭的 CPU 限流影響容器執行,有時人們不得不犧牲容器部署密度來避免 CPU 限流出現。我們設計的 CPU Burst 技術既能保證容器執行服務質量,又不降低容器部署密度。CPU Burst 特性已合入 Linux 5.14,Anolis OS 8.2、Alibaba Cloud Linux2、Alibaba Cloud Linux3 也都支援 CPU Burst 特性。
在 K8s 容器排程中,容器的 CPU 資源上限是由 CPU limits 引數指定。設定 CPU 資源上限可以限制個別容器消耗過多的 CPU 執行時間,並確保其他容器拿到足夠的 CPU 資源。CPU limits 限制在 Linux 核心中是用 CPU Bandwidth Controller 實現的,它通過 CPU 限流限制 cgroup 的資源消耗。所以當一個容器中的程式使用了超過 CPU limits 的資源的時候,這些程式就會被 CPU 限流,他們使用的 CPU 時間就會受到限制,程式中一些關鍵的延遲指標就會變差。
面對這種情況,我們應該怎麼辦呢?一般情況下,我們會結合這個容器日常峰值的 CPU 利用率並乘以一個相對安全的係數來設定這個容器的 CPU limits ,這樣我們既可以避免容器因為限流而導致的服務質量變差,同時也可以兼顧 CPU 資源的利用。舉個簡單的例子,我們有一個容器,他日常峰值的 CPU 使用率在 250% 左右,那麼我們就把容器 CPU limits 設定到 400% 來保證容器服務質量,此時容器的 CPU 利用率是 62.5%(250%/400%)。
然而生活真的那麼美好嗎?顯然不是!CPU 限流的出現比預期頻繁了很多。怎麼辦?似乎看上去我們只能繼續調大 CPU limits 來解決這個問題。很多時候,當容器的 CPU limits 被放大 5~10 倍的時候,這個容器的服務質量才得到了比較好的保障,相應的這時容器的總 CPU 利用率只有 10%~20%。所以為了應對可能的容器 CPU 使用高峰,容器的部署密度必須大大降低。
歷史上人們在 CPU Bandwidth Controller 中修復了一些 BUG 導致的 CPU 限流問題,我們發現當前非預期限流是由於 100ms 級別 CPU 突發使用引起,並且提出 CPU Burst 技術允許一定的 CPU 突發使用,避免平均 CPU 利用率低於限制時的 CPU 限流。在雲端計算場景中,CPU Burst 技術的價值有:
- 不提高 CPU 配置的前提下改善 CPU 資源服務質量;
- 允許資源所有者不犧牲資源服務質量降低 CPU 資源配置,提升 CPU 資源利用率;
- 降低資源成本(TCO,Total Cost of Ownership)。
你看到的 CPU 利用率不是全部真相
秒級 CPU 利用率不能反映 Bandwidth Controller 工作的 100ms 級別 CPU 使用情況,是導致非預期 CPU 限流出現的原因。
Bandwidth Controller 適用於 CFS 任務,用 period 和 quota 管理 cgroup 的 CPU 時間消耗。若 cgroup 的 period 是 100ms quota 是 50ms,cgroup 的程式每 100ms 週期內最多使用 50ms CPU 時間。當 100ms 週期的 CPU 使用超過 50ms 時程式會被限流,cgroup 的 CPU 使用被限制到 50%。
CPU 利用率是一段時間內 CPU 使用的平均,以較粗的粒度統計 CPU 的使用需求,CPU 利用率趨向穩定;當觀察的粒度變細,CPU 使用的突發特徵更明顯。以 1s 粒度和 100ms 粒度同時觀測容器負載執行,當觀測粒度是 1s 時 CPU 利用率的秒級平均在 250% 左右,而在 Bandwidth Controller 工作的 100ms 級別觀測 CPU 利用率的峰值已經突破 400% 。
根據秒級觀察到的 CPU 利用率 250% 設定容器 quota 和 period 分別為 400ms 和 100ms ,容器程式的細粒度突發被 Bandwidth Controller 限流,容器程式的 CPU 使用受到影響。
如何改善
我們用 CPU Burst 技術來滿足這種細粒度 CPU 突發需求,在傳統的 CPU Bandwidth Controller quota 和 period 基礎上引入 burst 的概念。當容器的 CPU 使用低於 quota 時,可用於突發的 burst 資源累積下來;當容器的 CPU 使用超過 quota,允許使用累積的 burst 資源。最終達到的效果是將容器更長時間的平均 CPU 消耗限制在 quota 範圍內,允許短時間內的 CPU 使用超過其 quota。
如果用 Bandwidth Controller 演算法來管理休假,假期管理的週期(period)是一年,一年裡假期的額度是 quota ,有了 CPU Burst 技術之後今年修不完的假期可以放到以後來休了。
在容器場景中使用 CPU Burst 之後,測試容器的服務質量顯著提升。觀察到 RT 均值下降 68%(從 30+ms 下降到 9.6ms );99% RT 下降 94.5%(從 500+ms 下降到 27.37ms )。
CPU Bandwidth Controller 的保證
使用 CPU Bandwidth Controller 可以避免某些程式消耗過多 CPU 時間,並確保所有需要 CPU 的程式都拿到足夠的 CPU 時間。之所以有這樣好的穩定性保證,是因為當 Bandwidth Controller 設定滿足下述情況時,
有如下的排程穩定性約束:
其中,
是第 i 個 cgroup 的 quota,是一個 period 內該 cgroup 的 CPU 需求。Bandwidth Controller 對每個週期分別做 CPU 時間統計,排程穩定性約束保證在一個 period 內提交的全部任務都能在該週期內處理完;對每個 CPU cgroup 而言,這意味著任何時候提交的任務都能在一個 period 內執行完,即任務實時性約束:
不管任務優先順序如何,最壞情況下任務執行時間(WCET, Worst-Case Execution Time)不超過一個 period。
假如持續出現
排程器穩定性被打破,在每個 period 都有任務積攢下來,新提交的作業執行時間不斷增加。
使用 CPU Burst 的影響
出於改善服務質量的需要,我們使用 CPU Burst 允許突發的 CPU 使用之後,對排程器的穩定性產生什麼影響?答案是當多個 cgroup 同時突發使用 CPU,排程器穩定性約束和任務實時性保證有可能被打破。這時候兩個約束得到保證的概率是關鍵,如果兩個約束得到保證的概率很高,對大多數週期來任務實時性都得到保證,可以放心大膽使用 CPU Burst;如果任務實時性得到保證的概率很低,這時候要改善服務質量不能直接使用 CPU Burst,應該先降低部署密度提高 CPU 資源配置。
於是下一個關心的問題是,怎麼計算特定場景下兩個約束被打破的概率。
評估影響大小
定量計算可以定義成經典的排隊論問題,並且用蒙特卡洛模擬方法求解。定量計算的結果表明,判斷當前場景是否可以使用 CPU Burst 的主要影響因素是平均 CPU 利用率和 cgroup 數目。CPU 利用率越低,或者 cgroup 數目越多,兩個約束越不容易被打破可以放心使用 CPU Burst。反之如果 CPU 利用率很高或者 cgroup 數目較少,要消除 CPU 限流對程式執行的影響,應該降低部署提高配置再使用 CPU Burst。
問題定義是:一共有 m 個 cgroup,每個 cgroup 的 quota 限制為 1/m,每個 cgroup 在每個週期產生的計算需求(CPU 利用率)服從某個具體分佈,這些分佈是相互獨立的。假設任務在每個週期的開始到達,如果該週期內的 CPU 需求超過 100%,當前週期任務 WCET 超過 1 個 period,超過的部分累積下來和下個週期新產生的 CPU 需求一起在下個需求處理。輸入是 cgroup 的數目 m 和每個 CPU 需求滿足的具體分佈,輸出是每個週期結束 WCET > period 的概率和 WCET 期望。
以輸入的 CPU 需求為帕累託分佈、m=10/20/30 的結果為例進行說明。選擇帕累託分佈進行說明的原因是它產生比較多的長尾 CPU 突發使用,容易產生較大影響。表格中資料項的格式為
其中
越接近 1 越好,
概率越低越好。
結果跟直覺是吻合的。一方面,CPU 需求(CPU 利用率)越高,CPU 突發越容易打破穩定性約束,造成任務 WCET 期望變長。另一方面,CPU 需求獨立分佈的 cgroup 數目越多,它們同時產生 CPU 突發需求的可能性越低,排程器穩定性約束越容易保持,WCET 的期望越接近 1 個 period。
場景和引數設定
我們設定整個系統存在 m 個 cgroup,每個 cgroup 公平瓜分總量為 100% 的 CPU 資源,即 quota=1/m。每個 cgoup 按相同規律(獨立同分布)產生計算需求並交給 CPU 執行。\
我們參考排隊論的模型,將每個 cgroup 視為一位顧客,CPU 即為服務檯,每位顧客的服務時間受到 quota 的限制。為了簡化模型,我們離散化地定義所有顧客的到達時間間隔為常數,然後在該間隔內 CPU 最多能服務 100% 的計算需求,這個時間間隔即為一個週期。
然後我們需要定義每位顧客在一個週期內的服務時間。我們假定顧客產生的計算需求是獨立同分布的,其平均值是自身 quota 的 u_avg 倍。顧客在每個週期得不到滿足的計算需求會一直累積,它每個週期向服務檯提交的服務時間取決於它自身的計算需求和系統允許的最大 CPU time(即其 quota 加上之前週期累積的 token)。
最後,CPU Burst 技術中有一項可調引數 buffer,表示允許累積的 token 上限。它決定了每個 cgroup 的瞬時突發能力,我們將其大小用 quota 的 b 倍表示。
我們對上述定義的引數作出瞭如下設定:
負指數分佈是排隊論模型中最常見、最多被使用的分佈之一。其密度函式為
其中
帕累託分佈是計算機排程系統中比較常見的分佈,且它能夠模擬出較大的延遲長尾,從而體現 CPU Burst 的效果。其密度函式為:
為了抑制尾部的概率分佈使其不至於過於誇張,我們設定了:
此時當 u_avg=30% 時可能產生的最大計算需求約為 500%。
資料展示
按上述引數設定進行蒙特卡洛模擬的結果如下所示。我們將第一張(WCET 期望)的圖表 y 軸進行顛倒來更好地符合直覺。同樣地,第二張圖表(WCET 等於 1 的概率)表示排程的實時性得到保證的概率,以百分制表示。
負指數分佈
帕累託分佈
結論
一般來說,u_avg(計算需求的負荷)越高,m(cgroup 數量)越少,WCET 越大。前者是顯然的結論,後者是因為獨立同分布情況下任務數量越多,整體產生需求越趨於平均,超出 quota 需求的任務和低於 quota 從而空出 cpu 時間的任務更容易互補。
提高 buffer 會使得 CPU Burst 發揮更好的效果,對單個任務的優化收益更明顯;但同時也會增大 WCET,意味著增加對相鄰任務的干擾。這也是符合直覺的結論。
在設定 buffer 大小時,我們建議根據具體業務場景的計算需求(包括分佈和均值)和容器數量,以及自身需求來決定。如果希望增加整體系統的吞吐量,以及在平均負荷不高的情況下優化容器效能,可以增大 buffer;反之如果希望保證排程的穩定性和公平性,在整體負荷較高的情況下減少容器受到的影響,可以適當減小 buffer。
一般而言,在低於 70% 平均 CPU 利用率的場景中,CPU Burst 不會對相鄰容器造成較大影響。
模擬工具與使用方法
說完了枯燥的資料和結論,接下來介紹可能有許多讀者關心的問題:CPU Burst 會不會對我的實際業務場景造成影響?為了解決這個疑惑,我們將蒙特卡洛模擬方法所用工具稍加改造,從而能幫助大家在自己的實際場景中測試具體的影響~
工具可以在這裡獲取:https://codeup.openanolis.cn/...
詳細的使用說明也附在 README 中了,下面讓我們看一個具體的例子吧。
小 A 想在他的伺服器上部署 10 臺容器用於相同業務。為了獲取準確的測量資料,他先啟動了一臺容器正常執行業務,繫結到名為 cg1 的 cgroup 中,不設限流以獲取該業務的真實表現。
然後呼叫 sample.py 進行資料採集:(演示效果只採集了 1000 次,實際建議有條件的情況下采集次數越大越好)
這些資料被儲存到了./data/cg1_data.npy 中。最後輸出的提示說明該業務平均佔用了約 6.5% 的 CPU,部署 10 臺容器的情況下總的平均 CPU 利用率約為 65%。(PS:方差資料同樣列印出來作為參考,也許方差越大,越能從 CPU Burst 中受益哦)
接下來,他利用 simu_from_data.py 計算配置 10 個 和 cg1 相同場景的 cgroup 時,將 buffer 設定為 200% 的影響:
根據模擬結果,開啟 CPU Burst 功能對該業務場景下的容器幾乎沒有負面影響,小 A 可以放心使用啦。
想要進一步瞭解該工具的用法,或是出於對理論的興趣去改變分佈檢視模擬結果,都可以訪問上面的倉庫連結找到答案~
關於作者
常懷鑫(一齋),阿里雲核心組工程師,擅長 CPU 排程領域。
丁天琛(鷹羽),2021 年加入阿里雲核心組,目前在排程領域等方面學習研究。
點選此處,即可檢視阿里雲專有云敏捷版雲原生 Stack 相關介紹!