陳 雁,黃嘉鑫
(西南石油大學 計算機科學學院,成都 610500)
隨著計算機技術的更新迭代,傳統的應用正在變得越來越復雜,需要支持更多的用戶、更強的計算能力,而且還需要更加穩定和安全,為了支撐這些不斷增長的需求,一種新型的計算模式“云計算”應運而生.云計算是一種新的基于互聯網的計算方式和資源應用平臺,從計算方式上來說,“云計算”是通過網絡將龐大復雜的數據交由多臺服務器組成的集群系統進行存儲、計算、分析后,將處理結果回傳給用戶的一種計算模式[1].
虛擬化是云計算的基石,傳統虛擬化技術是在硬件資源級別的虛擬化,需要有虛擬機管理程序和虛擬機操作系統.隨著Docker[2]的出現,容器技術正在成為打包和部署應用程序的新趨勢,Docker是直接建立在操作系統上的虛擬化,它直接復用本地操作系統,更加輕量級.使用Docker比使用虛擬機(VM)部署應用啟動更快,系統的開銷更小,能夠更有效地支持應用程序的快速迭代[3].目前,Docker容器已在云基礎架構中得到廣泛部署,例如Amazon Ec2 Container Service,Google Container Engine,Rackspace,Docker Data Center[4].
Docker 是一個相對底層的容器引擎,在大規模的服務器集群中,需要一個集中的控制器來進行任務的編排,調度和控制.而Kubernetes[5]提供了大規模運行容器的編排系統和管理平臺,它實現了包括應用部署、高可用管理和彈性伸縮在內的一系列基礎功能,并封裝成為一整套完整、簡單易用的RESTful API對外提供服務.
Kubernetes的彈性擴展功能是由Horizontal Pod Autoscaler (HPA)[6]控制器來實現的,HPA會根據自定義指標控制副本集中的容器數量,這種自動擴展伸縮機制使用固定閾值的規則,并且Kubernetes的擴展和收縮Pod數量是根據同一套算法,同一個檢測指標來判定是否需要擴容或者收縮.
由于Kubernetes的擴容和收縮決策判斷基于同一個指標,這就會產生頻繁擴容縮容的問題;在應對大規模突發流量時,Kubernetes的擴容幅度不足以應對大規模流量,就會造成訪問中斷;在訪問流量驟降的時候,Kubernetes內置算法則會迅速收縮Pod 的數量,收縮速度太快,當出現第二次突發流量,收縮后的Pod還來不及擴展,就會導致應用負載過大,造成應用崩潰.
針對Kubernetes彈性擴展的調度策略,Casalicchio等[7]對CPU密集型的工作負載,提出KHPA-A算法,該算法采用絕對度量指標來做擴展決策,使用絕對指標來觸發容器彈性伸縮和確定Pod的數量,將度量指標保持在設定閾值以下.Al-Dhuraibi等[8]在Docker層面提出ELASTICDOCKER算法,該算法是垂直彈性擴展容器的CPU和內存大小,當應用程序工作負載上升/下降時,ELASTICDOCKER將分配給每個容器的CPU和內存向上/下彈性伸縮.垂直彈性僅受限于主機容量,當所有主機資源都已分配給容器時,容器將會執行從源主機到目標主機的動態遷移.
以上研究是針對于特殊場景的CPU密集型計算和縱向擴展增加容器的CPU和內存,而如何在通用常規情況下,使用方便簡單的方法保證系統平穩、穩定運行則是更需要解決的問題.因此,本文提出步長容忍度算法水平伸縮的控制器,它能在當應用服務器中業務負載上升的時候,迅速創建多個新的Pod來保證業務系統穩定運行,當Pod中業務負載下降的時候,在保證系統穩定運行的前提下逐步銷毀Pod來提高資源利用率.對于應用,需要保障能夠穩定平滑的運行,而不只是盡可能的節約成本.步長容忍度算法在擴展的時候,保證應用服務器能夠承擔不斷增長的連接數,做到快速擴展,甚至過度擴展.在收縮的時候采取較為寬松的收縮策略,達到逐漸收縮的目的.
HPA是Kubernetes里面Pod彈性伸縮的實現,它在Kubernetes中被設計為一個controller,能根據設置的監控閥值進行Pod的彈性擴縮容[9].HPA Controller默認30 s輪詢一次,查詢指定的resource中Deployment/RS的資源使用率[10],并且與創建時設定的值和指標做對比,從而實現自動伸縮的功能.HPA控制器工作原理如圖1所示.

圖1 HPA控制器原理圖
HPA使用式(1)中的方法計算

其中,dP(desired Replicas)為期待的副本數量,即算法計算后得到的副本數量;cR(current Replicas)為當前的副本數量,探測器檢測到的副本的數量;cM(current Metric value)為當前設置的檢測指標的值,比如CPU利用率,內存使用情況;dM(desired Metric value)為期望的Pod 的檢測指標的值;ceil為表示取大于或等于某數的最近一個整數.
HPA中Pod水平彈性伸縮的實現是通過定期輪循查詢Pod的狀態(默認輪詢時間為30 s)通過式(1)計算.在每個周期時間,控制器管理器根據每個HPA定義中指定的度量標準查詢資源利用率.控制管理器從資源指標的API中獲取度量,獲取到Pod的監控數據,然后通過現有Pod使用率的平均值跟目標使用率進行比較,計算出需要伸縮的具體值并進行操作.在每次擴容和收縮都有一個窗口時間,在執行伸縮操作后,在這個窗口時間內,不會再進行伸縮操作,默認擴容為3 min,收縮為5 min.
例如當前的副本數量為1個,設置當前度量標準值cM是內存,檢測到當前Pod 的內存為150 MB,而初始設定的期望為100 MB,則副本的數量將變為2個,因為 1×(150/100)= 1.5 向上取整為 2,所以Pod數量會彈性擴展到 2個.
參考 Kubernates源代碼發現,原生彈性伸縮的實現非常簡單:(1)計算所有Pod的度量指標使用率;(2)根據彈性伸縮算法計算Pod的需求量;(3)按照計算出的Pod數量進行擴容和伸縮.這一自動縮放算法靈活性差,擴容和收縮都是用同一個算法,對突發流量會造成頻繁擴容問題,突發流量過后會造成頻繁收縮的問題;容器啟動是需要一定的時間的,在應對大規模突發流量時,擴容還來不及擴展Pod,就可能會造成當前應用承受不了負載而崩潰,應當為新擴容的容器實例預留啟動時間,給正在運行的Pod充足的資源應對負載;逐漸收縮,在保證訪問量承載的前提下,逐步遞減收縮Pod,防止收縮太快低于系統的承載能力,造成系統的不穩定,針對這些問題本文提出了步長容忍度算法.
設計步長容忍度算法的時候,考慮到自動擴展的決策需要一段時間才會生效,若當前Pod的CPU負荷過大,在創建一個新Pod的過程中,應用系統的CPU使用量可能會同樣有一個攀升的過程.所以在循環探測中,在每一次作出決策后的一段時間內,將不再進行擴展/收縮決策.對于擴容而言,這個時間段為3分鐘,縮容為5分鐘.因為需要盡可能滿足Pod業務的正常使用,所以擴容的優先級要大于縮容.步長容忍度算法會通過調整副本數量使得檢測指標使用率盡可能向期望值靠近,而且不是完全相等.步長容忍度算法在HPA Controller中引入一個tolerance (容忍度)的概念,它允許在一定范圍內使用量的不穩定,設置容忍度為15%,這也是出于維護系統穩定性的考慮.例如,設定HPA調度策略為CPU使用率高于60%觸發擴容,那么只有當使用率大于75%或者小于45%才會觸發伸縮活動,HPA會盡力把Pod的使用率控制在這個范圍之間.自動伸縮的監測指標包括CPU平均使用量、內存平均使用量、用戶自定義一些監控指標.
步長容忍度算法包含兩個部分:快速擴展算法和逐步收縮算法.
步長容忍度算法的快速擴展將Pods的目標期望利用率Tmetric(作為MetricValue指標的百分比)、前一個輪詢控制周期(Ts)中運行的Pods的ActPod集合、實例化的初始Pod擴容數量、設定的擴展步長UpSetup等作為輸入,輸出是則要部署的Pods的目標數量P.
系統啟動之后,每隔Ts(如30 s)循環檢查一次所有HPA,檢測結果若有一個Pod指標超過初始設置的期望值加上容忍度的值,就以步長(如2)執行擴展.每次擴展完成后,記錄擴展時間間隔,保證擴展操作間隔不小于3 min以確保系統的穩定性.Pod的數量按照式(1)進行計算,而利用率/期望的比例大于1.15(容忍度為15%),才能進行擴容,防止抖動.
系統每Ts(實驗設置為30 s),算法收集Pod的MetricValue利用率,用cAdvisor(容器監控工具)測量并將其存儲在向量U中.最后,計算出DisredPods的目標數量P,P由式(2)計算得出.

算法1為步長容忍度算法中的快速擴展算法.

算法1.步長容忍度算法的快速擴展算法Inputs:TMetric:期望的檢測指標的平均使用率;AvgMetric:檢測指標的平均使用率;ActPod:正在運行的Pod的數量;DisredPods:期望Pod的數量;Tolerance:期望的容忍度;UpSetup:擴容的步長;T:定期輪詢時間.run Application(s)init ActPod = 1 while true do if [AvgMetric > (Tmetric + Tolerance)] {if (ActPod = = 1){DesiredPod = ActPod + UpSetup ActPod = DesiredPod}}else{DesiredPod=Ceil[ActPod*(AvgMetric/Tmetric)+UpSetup]ActPod = DesiredPod}wait(T)end while
假設Tmetric= 60%.正在運行3個應用程序副本,每個Pod的CPU利用率分別為73%,75%和82%.在下一個控制周期,步長容忍度算法確定應該部署新的PodP= 6.這樣快速擴展以應對大規模的數據流量,防止應用崩潰.負載將在Pod之間分配,預計的每Pod調整利用率由式(3)計算出:

在下一次擴容的時間間隔3 min內,以便有足夠的資源來負載訪問流量,達到快速擴容的目的.
步長容忍度算法節點收縮采取的策略是當監測指標值低于所設定的縮容條件,以默認步長2減少節點數量.當監測節點數量為2,停止減少容器數量.
約束規則為縮容條件的可選范圍是(0%~45%).縮容的時候當節點數<= 集群最小節點數(設置為2)的時候,不會進行縮容操作.
算法2為步長容忍度算法中的逐步收縮的算法.
假設Tmetric= 60%.正在運行6個應用程序副本,每個Pod的CPU利用率分別為50%,45%,47%,52%,43%和40%.在下一個控制周期,步長容忍度算法確定收縮節點的數量P=4.這樣以步長為2逐步收縮.負載將在Pod之間分配,預計的每Pod調整利用率由式(4)計算出變為:


算法2.步長容忍度算法的逐步收縮算法Inputs:TMetric:期望的檢測指標的平均使用率AvgMetric:檢測指標的平均使用率ActPod:正在運行的Pod的數量DisredPods:期望Pod的數量Tolerance:期望的容忍度DownSetup:收縮的步長T:定期輪詢時間run Application(s)init ActPod = 1 while true do if [AvgMetric < (Tmetric-Tolerance)] {if (ActPod = = 2){ActPod = 2}}else{DesiredPod = ActPod -DownSetup]ActPod = DesiredPod}wait(T)end while
在下一次收縮的時間間隔5 min內,保持系統的穩定性,達到逐步收縮的目的.
在實驗中,為了驗證步長容忍度算法,實驗環境采用2套相同的Kubernetes實驗環境做對比測試,一套Kubernetes實驗環境使用原生的彈性伸縮算法,對比實驗采用步長容忍度算法.Kubernetes實驗環境都是1個master節點和2個node節點,Kubernetes版本1.11.3,docker版本為17.03.3-ce.在實驗中使用的環境配置如表1所示.

表1 實驗環境配置
設定了一個在進行壓力測試的情況下檢驗Pod個數的變換以及每個Pod的CPU負載的變換情況.
實驗包含兩個部分:(1)使用ApacheBench進行壓力測試,在壓力測試的時候檢驗pod的變化情況,在壓力測試完成后,觀察Pod隨即的數量變化狀況,驗證在應對突發流量時擴容/收縮情況.(2)使用不同的并發請求數,請求5 000 000個連接,在高壓高并發的情況下檢測服務的吞吐率、用戶平均請求等待時間、服務器平均處理時間,驗證步長容忍度算法是否提高系統的穩定性.每個實驗重復進行5次迭代,取實驗結果的平均值.
實驗設定:創建部署2個一主兩從的 Kubernetes集群.建一個包含nginx-stress容器的deployment,對這個deployment持續進行并發請求,模擬用戶在客戶端頻繁地訪問web應用,應用會將訪問流量轉發到后端一個Pod上,繼而增加這個Pod的壓力,Kubernetes系統檢測到Pod內存和CPU在不斷升高,到達擴容的臨界值就會擴展Pod,來應對這些流量.
實驗表明,啟動一個Pod容器實例所需的時間大約是6 s.在實驗中,Kubernetes資源探測的時間間隔定義為30 s,雖然也可以將監視間隔指定為更短的時間,但是將其設置為30 s可以減少通信流量負載和測量的監視開銷.在實驗中,本文把閾值被設置為65%,這樣步長容忍度算法將有足夠的時間對工作負載中的運行時變化作出反應,而不是閾值非常接近100%才進行反應,從而防止壓力過大應用崩潰的問題.擴容間隔時間設置為3 min,可以防止運行容器實例數量的過于頻繁的變化.收縮時間間隔為5 min,避免收縮過快引發系統不穩定性.
對應用進行持續不斷的請求,進行壓力測試,圖2顯示了在工作負載急劇上升的場景下內置算法和步長容忍度算法的Pod的數量變換情況.
由圖2可見,在應用收到大規模流量請求時,內置算法擴容的比例比步長容忍度算法小的多,從應用的負載情況來看,步長容忍度算法能夠很好的支持大規模訪問.步長容忍度算法幾乎沒有失敗請求數,而內置算法出現了不少的失敗請求數(見表2),由于工作量增加導致系統過載,內置算法的應用提供了一段時間的慢響應時間.步長容忍度算法這種快速擴展能夠很好支撐大規模流量訪問.

圖2 Pod增長變化圖

表2 內置算法并發請求測試
當工作負載密度下降到1個請求,Pod的數量會根據執行時到達的請求的數量而減少.圖3顯示了在工作負載下降的場景的Pod的數量變化情況.

圖3 Pod收縮變化圖
步長容忍度算法和內置算法的自動彈性伸縮方法都不會立即停止在集群中運行的容器實例,Pod數量是呈現逐漸減少的狀態.與內置算法的自動縮放方法相比,步長容忍度算法分配的Pod數量明顯多于內置算法,是遞減收縮.在工作負載突然增加時,步長容忍度算法在開始及啟動時有足夠的Pod實例,比內置算法自動擴展方法更快.因此,對于急劇變化的工作負載模式,步長容忍度算法有更好的負載能力和維持系統的穩定性能.
本文也對在彈性擴展中Pod 的性能做了測試,在進行5 000 000個連接數情況下對應用的失敗請求數、吞吐率、用戶平均請求等待時間和服務器平均處理時間進行了測試,表2為內置算法的并發請求性能測試,表3為步長容忍度算法的并發請求性能測試.

表3 步長容忍度算法并發請求測試
對比內置算法和步長容忍度算法并發請求測試的結果,在并發請求數比較少的情況,內置算法和步長容忍度算法表現都差不多,在失敗請求數這個指標,內置算法由于持續壓力測試當前啟動的Pod不能負載流量,啟動新的Pod需要一定的啟動時間,造成應用的不穩定,產生較多的失敗請求.在并發請求數比較大的時候,步長容忍度算法的優勢就體現出來了,應用能在更短的時間內處理請求,響應客戶端的速度也更快,服務器的吞吐率也更大.
實驗表明,在并發請求數較大的情況下,步長容忍度算法的Kubernetes集群處理能力比內置算法的集群應對突發流量的能力更強,內置算法的集群對突發流量會出現承載過大,出現較多失敗請求,對比使用不同并發請求數的請求測試,步長容忍度算法的失敗請求數比內置算法的失敗請求數下降了 97.83%,整個彈性伸縮系統更加穩定健壯.步長容忍度算法集群的吞吐率比內置算法集群的吞吐率更大,處理請求的速度越快,使用步長容忍度算法的服務器的響應速度(依據用戶請求等待時間)比內置算法算法的響應速度更快,提高了4.54%.步長容忍度算法在其他方面的結果比內置算法結果好.此實驗通過用壓力測試模擬用戶請求觸發閾值進而進行擴容驗證了本文提出的步長容忍度算法的有效性,是在應對大規模流量橫向擴展的有效手段.
Kubernetes內置的 Horizontal Pod彈性縮放算法擴容和收縮采用相同的伸縮機制,會造成頻繁的擴容和收縮問題.本文提出了一種新的自動縮放算法,該算法能夠實現快速擴容和逐步收縮以達到系統穩定運行的狀態,對于服務的穩定性是一個很好的提升.本文提出的方案在并發請求數較大的情況下,步長容忍度算法比內置彈性伸縮算法應對突發流量的能力更強.由于目前彈性伸縮策略是根據實時的使用率來進行擴容,但其實對于很多應用來說,很多業務高峰期是有規律的,因此如果未來能做到提前預測出高峰期,做適應的擴容,那將會極大地提高資源使用率.