2021華為軟體精英挑戰賽總結(粵港澳賽區複賽第7)

妙妙園發表於2022-01-06

本屆挑戰賽賽題來源於華為公司實際面對的一個生產場景,即雲上資源規劃、排程的優化,而優秀的優化演算法不僅能為雲運營商節約上億的運營成本,更能為客戶提供更穩定、更流暢的雲端體驗。該賽題要求參賽者依據不同伺服器、虛擬機器以及某個時間段的訂單需求,設計出購買伺服器、放置虛擬機器以及定價博弈等完善的排程策略,力圖降低雲運營商成本,提升盈利。因此我覺得這個題目是十分有趣而且有意義的,可以一窺雲上資源排程的問題。我們的成績是粵港澳賽區複賽第7,差一些進決賽,有點遺憾,但是也拿到了華為的面試直通卡。【附上沒有整理的程式碼https://github.com/MiaoMiaoGarden/Huawei-CodeCraft-2021

賽題解讀

題目中描述了一些名詞。

伺服器

伺服器型別:在公有云的運營場景中,我們的資料中心可以選購的伺服器型別有多種。伺服器使用NUMA架構,有單節點和雙節點之分。雙節點伺服器可以分為A和B兩個節點,伺服器擁有的資源(CPU 和記憶體)均勻分佈在這兩個節點上,虛擬機器不能橫跨兩個節點。
伺服器成本:資料中心使用每臺伺服器的成本由兩部分構成:硬體成本和能耗成本。硬體成本是在採購伺服器時的一次性支出,使用者可以根據自己的需求來自由選購。能耗成本是後續伺服器使用過程中的持續支出,只要某一天這臺伺服器上放置了虛擬機器,那麼這一天就要計算能耗成本。

虛擬機器

虛擬機器型別:我們面向使用者提供了多種型別的虛擬機器售賣服務,使用者可以根據自己的需求來自由選購。不同的虛擬機器型別,有不同的CPU、記憶體需求。
單節點/雙節點部署:由於伺服器存在兩個節點,對應的虛擬機器也存在兩種部署方式:單節點部署或雙節點部署。單節點部署指的是一臺虛擬機器所需的資源(CPU和記憶體)完全由主機上的一個節點提供;雙節點部署指的是一臺虛擬機器所需的資源(CPU 和記憶體)必須由一臺伺服器的兩個節點同時提供,並且每個節點提供總需求資源的一半。

資源規劃與排程

  • 容量約束:伺服器可以用來容納使用者的虛擬機器,但是伺服器上的任意一個節點(A和B)上的資源負載(CPU 和記憶體)均不能超過其容量上限。
  • 請求型別:使用者的請求共分為兩種型別:建立請求和刪除請求。
  • 請求序列:由一系列請求構成的序列。題目會給出接下來若干天中每一天使用者的請求序列,根據每天的請求序列,你需要進行相應的資源規劃和排程。
  • 資料中心擴容:在得知了一天的請求序列後,你可以在實際進行排程前進行一次資料中心擴容。即購買一些新的伺服器來容納後續使用者請求的虛擬機器,同時你需要付出所購買伺服器相應的硬體成本。
  • 虛擬機器遷移:在完成擴容後,在處理每一天的新請求之前,你還可以對當前存量虛擬機器進行一次遷移,即把虛擬機器從一臺伺服器遷移至另一臺伺服器。遷移的虛擬機器總量不超過當前存量虛擬機器數量的百分之三。即假設當前有n 臺存量虛擬機器,每天你可以遷移的虛擬機器總量不得超過3n/100 向下取整。
  • 部署虛擬機器:在完成擴容和遷移之後,你需要按順序處理當天所有的新請求。對於每一個建立虛擬機器的新請求,你要為虛擬機器指定一臺伺服器進行部署。若虛擬機器是單節點部署的,你還需要指明部署在伺服器的A 節點還是B 節點。處理請求的過程中,任意一臺伺服器上每個節點容納的虛擬機器資源總和都不能超出節點本身的資源容量(指CPU 和記憶體兩個維度)。
  • 未知請求序列:在初賽中,我們面對的是提前知曉了未來所有使用者請求序列的場景,這在實際中是很難作到的。現實場景中,我們往往只能預測後續較短的一段時間內使用者可能的請求序列。所以在複賽中,你需要面對這種存在未知的場景,進行雲上的資源規劃和排程。

方案

設計伺服器、虛擬機器的資料結構自然不必多說,直接討論解決方案。從問題的定義來看,我們扮演的是雲服務運營商的角色。保證使用者的虛擬機器請求得以滿足的情況下,儘量降低自己的成本,這是首要目標(次要目標是決策速度,速度不能超過一定時限)。整體解決方案需要考慮三種行為的策略:購買伺服器、放置虛擬機器和虛擬機器遷移

購買伺服器

成本分為硬體成本和能耗成本,購買伺服器的策略優化主要目的是降低硬體成本。降低硬體成本要求我們購買伺服器的花銷儘可能少,儘可能少買並且儘可能買價格便宜的機器。我們檢視給定的伺服器資源和價格的關係,發現一些伺服器的價格高,可能是因為cpu資源比較多,一些伺服器的價格低,可能是因為mem資源比較多,當然也有兩種資源都比較多的機器。總體來說,貴的機器基本上是有貴的理由的。買伺服器有四種容易想到的策略:

  • 買貴的伺服器(或者資源多的伺服器):貴的伺服器意味著一臺伺服器上的資源可能比較充足,一則可以放置大的虛擬機器,二則由於虛擬機器的聚集放置,可以減少”內部碎片“。缺點是貴的伺服器通常能耗比較大,如果使用者需求的機器量比較少,一味地購買貴的機器將不得不負擔不必要的大能耗成本。
  • 買便宜的伺服器(或者資源少的伺服器):便宜的機器與貴的機器恰好相反,一位地購買便宜的機器可能不足以放置大的虛擬機器,且容易產生”內部碎片“。但能耗也許可以降低。

從上面的考量中我們發現,伺服器的購買與使用者請求的虛擬機器型別相關。我們的方案是在每次伺服器不足以滿足一臺虛擬機器請求的時候,就新購買一臺。購買伺服器的型號根據這個虛擬機器請求來決定。我們設計了一個weight函式,這個函式比較複雜,因為權衡了很多因素。

  • left_Day是剩餘天數,life是這臺虛擬機器的生命天數。

  • p0和p1是今天所有請求放置的虛擬機器的cpu資源佔總資源比例和mem資源佔總資源比例。這決定了今天的虛擬機器請求要求的主要是cpu還是mem資源。

  • last_cpu和last_mem是這個型號的伺服器放置完這臺虛擬機器之後剩餘的cpu和mem資源。這決定了這臺伺服器的剩餘碎片大小,是否物盡其用,是否能供給其他虛擬機器請求。

  • q0和q1是伺服器上的cpu佔總資源比例和mem佔總資源比例。這說明了這臺伺服器資源主要是cpu還是mem,與p0和p1是供需關係。

  • 設計了一個交叉熵函式來求得(p0, p1)和(q0, q1)之間的相似程度。js_1表示的是這臺伺服器放置了虛擬機器之後的供需相似程度,js_new表示放置之前的供需相似程度。

  • 用一個線性組合來求得一臺伺服器的得分。這臺伺服器的能提供的資源與需求資源的匹配程度的影響遍佈剩餘的天數,因此js_1以left_Day為權重,js_new類似。money是伺服器的價格,money與資源的商表示伺服器的價效比。如果這一天有大量的虛擬機器插入請求,那麼購買伺服器的時候可以不用考慮太多的放置之前的供需相似程度。這個線性組合彙集了眾多影響因素,通過調整引數可以增加對場景的適應性。(在歷史資料量足夠大、足夠典型的情況下,根據歷史資料調整引數應該是可以適應未來的虛擬機器請求的。)

最後遍歷所有可選的伺服器型號,找到能放置這臺虛擬機器的得分最高的伺服器購買一臺。

放置虛擬機器

以單節點虛擬機器的放置為例子。server.fit()函式返回一個bool值,表示這個節點的剩餘資源是否足夠放置虛擬機器,如果足夠放置,才可能被選擇。在能放得下的節點中選擇代價最小的。我們用這兩點計算代價:

  • 最好的情況下是這臺虛擬機器放置完了之後,這個節點直接滿了,物盡其用,否則的話,越放置剩餘的資源越可能是不可利用的碎片。因此,如果放置完了這臺虛擬機器之後,該節點剩餘資源越多,那麼代價越大。

  • 如果這臺伺服器還沒開機,虛擬機器的放置導致能耗成本要計算了,那麼代價就更大了。

虛擬機器遷移

遷移這塊兒其實不是我實現的,但是我參與了方案的設計,簡單說說吧。遷移的目的是將虛擬機器重新排布,需要重新排布的原因是這一天可能有許多虛擬機器被刪除了,因此空出了一些資源,那麼可以把剩下的虛擬機器歸攏。遷移的好處有兩個,一是空出了資源,類似OS記憶體管理的記憶體回收,會減少內部碎片,為以後放置大的虛擬機器提供了空間;二是可以減少開機機器的數量,從而減少能耗成本。有了這兩個目的驅動,加上遷移次數的限制(小於3n/100向下取整,n是正在執行的虛擬機器總數),遷移的策略基本上就出來了:從比較空的機器遷移到比較滿的機器。

  • 首先把現有的伺服器排序,按照剩餘資源的多少。(這裡程式碼沒有整理,留下了實驗的痕跡。)
  • 從空的機器到滿的機器逐一遍歷,對機器上的每個虛擬機器,找到可放置的最滿的機器,遷移過去。直到沒有可以遷移的機器,或者已經達到最大遷移次數。

這個遷移策略看似很容易想到,也十分自然,考慮的因素比較少,但卻是經過了一番實驗才選擇的。如果伺服器排序遍歷的順序不合適,可能出現不夠好的遷移策略:例如按照利用率,伺服器的排序是A<B<C<D,一臺虛擬機器V原本放置在A,如果C不能放置,它可能被遷移到B,然後C上的虛擬機器遷移到了D,空出了位置給V,但V卻再也不會被遷移了。這也是為什麼我們的from_server和to_server的排序是按照不同的策略來進行,並且嘗試了多種策略才選擇到一個比較優的排序方案。

速度優化

除了縮減成本之外,程式碼執行速度也曾經掣肘我們,因為程式碼要求必須在120s之內處理完所有的請求並給出決策。尤其是遷移程式碼,其實對速度來說十分關鍵,假如用m和n分別表示虛擬機器和伺服器節點數量,那麼遷移會是O(mn)的複雜度,m和n輕易可以達到三位數的量。速度的優化基本上是和程式碼實現有關:

  • 初始化資料結構時指定初始大小,減少擴容耗時
  • 將除法改為位移
  • 將不必要放在for迴圈內的操作,移到迴圈外
  • 多執行緒排序(機器是雙核的其實多執行緒用處不大)
  • log運算其實很耗時間,複賽現場資料量大了超時,直接把log去掉了,成本相差不大
  • 剪枝,遷移的時候如果源伺服器的資源利用率已經很高了就不考慮遷移了

總結

這比賽整挺好,就是考慮的東西太多,很考驗分析問題的能力和工程能力,還有團隊協作的能力。比賽過程為了找思路我也翻了一些雲資源分配策略的論文,有一些方法比較高階:用上了蟻群演算法和模擬退火這種運籌學上的優化演算法,但這些方法既不好實現,又需要較大的計算量。還有一些論文預測未來的虛擬機器請求,以此來幫助當下做決策。複賽的時候要求輸出一天的結果之後才能得到下一天的請求輸入,因此我們是直接用歷史情況當作未來情況的預測,這種時序資料預測是假設資料是平穩的,其實準確率應該不高。

相關文章