Airbnb 如何實現 Kubernetes 集羣動態擴展

作者 | David Morrison,Evan Sheng,David Morrison

譯者 | 平川

策劃 | Tina

本文最初發佈於 Airbnb 技術博客。

運營 Airbnb 基礎設施的一項重要工作是,確保我們的雲開支隨着需求自動增長和下降。我們的流量每天波動很大,爲此,我們的雲資源佔用應該能夠動態擴展。

爲了實現這種擴展,Airbnb 利用了 Kubernetes 這個開源的容器編排系統。我們還利用了 OneTouch,一個建立在 Kubernetes 之上的服務配置界面,在之前的 文章 中有更詳細的描述。

在這篇文章中,我們將討論如何使用 Kubernetes Cluster Autoscaler 動態調整集羣大小,並重點介紹我們爲 sig-autoscaling 社區 貢獻的特性。這些改進增加了可定製性和靈活性,滿足了 Airbnb 獨特的業務需求。

1 Airbnb 的 Kubernetes 集羣

在過去幾年裏,Airbnb 已經將幾乎所有的在線服務從手工編排的 EC2 實例遷移到 Kubernetes。如今,我們在近百個集羣中運行着數千個節點以適應我們的工作負載。然而,這種變化不是一夜之間發生的。在遷移過程中,隨着更多的工作負載和流量轉移到新技術棧,底層 Kubernetes 集羣的配置得到了優化,也變得更加複雜。這種演變可以分爲三個階段:

階段 1:同構集羣,手動擴展

階段 2:多集羣類型,獨立自動擴展

階段 3:異構集羣,自動擴展

階段 1:同構集羣,手動擴展

在使用 Kubernetes 之前,服務的每個實例都運行在自己的機器上,當流量增加時需要手動擴展。每個團隊的容量管理方式不一樣,負載下降時,已分配的容量也很少會取消。

我們最初的 Kubernetes 集羣設置相對簡單。我們有少數幾個集羣,每個集羣都有一個底層節點類型和配置,它們只運行無狀態的在線服務。隨着其中一些服務開始遷移到 Kubernetes,我們開始在多租戶環境中運行容器化服務(一個節點上有許多 pod)。這種聚合減少了資源浪費,並將服務的容量管理整合到了 Kuberentes 控制平面上。在這個階段,集羣擴展是手動的,但相比之前仍然有顯著的改進。

圖 1:EC2 節點 vs Kubernetes 節點

階段 2:多集羣類型,獨立自動擴展

集羣配置的第二個階段始於我們試圖在 Kubernetes 上運行更多不同類型的工作負載,每個類型都有不同的需求。爲了滿足這些需求,我們創建了一個集羣類型抽象。“集羣類型” 定義了集羣的底層配置,集羣類型相同的集羣從節點類型到各種集羣組件設置都相同。

集羣類型越多集羣也越多,我們的初始策略 “手動管理每個集羣的容量” 很快就支撐不下去了。爲了解決這個問題,我們在每個集羣中添加了 Kubernetes Cluster Autoscaler。該組件根據 pod 請求自動調整集羣大小——如果集羣的容量耗盡了,就可以通過添加一個新節點來滿足待處理的 pod 請求,此時,Cluster Autoscaler 就會啓動一個節點。類似地,如果集羣中有節點在很長一段時間內未得到充分利用,那麼 Cluster Autoscaler 將從集羣中刪除這些節點。在我們的設置中,添加這個組件非常有效,爲我們省卻了大約 5% 的雲開銷,以及手動擴展集羣的操作開銷。

圖 2:Kubernetes 集羣類型

階段 3:異構集羣,自動擴展

當 Airbnb 幾乎所有的在線計算都轉向 Kubernetes 時,集羣類型的數量已經增加到 30 多個,而集羣的數量則達到 100 多個。這種擴張使得 Kubernetes 集羣管理變得非常繁瑣。例如,集羣的升級必須針對衆多集羣類型中的每一種單獨進行測試。

在第三階段,我們的目標是通過創建 “異構” 集羣來整合集羣類型。這些集羣可以在一個 Kubernetes 控制平面上容納許多不同的工作負載。首先,這大大降低了集羣管理的開銷,因爲數量更少更通用的集羣可以減少需要測試的配置數量。其次,現在 Airbnb 大部分都在 Kubernetes 集羣上運行,集羣效率爲降低成本提供了一個重要槓桿。整合集羣類型使我們能夠在每個集羣中運行不同的工作負載。工作負載類型聚合——有大有小——可以實現更好的裝箱(bin packing)和效率,從而提高利用率。得益於工作負載的這種靈活性,我們有更多的空間在 Cluster Autoscaler 默認的擴展邏輯之外實施複雜的擴展策略。具體來說,我們的目標是實現與 Airbnb 具體業務邏輯相關的擴展邏輯。

圖 3:異構 Kubernetes 集羣

隨着我們對集羣進行擴展和整合(實現異構集羣,每個集羣有多個實例類型),我們開始在擴展過程中實現特定的業務邏輯,並發現有必要對自動擴展行爲做一些修改。下一節將描述我們對 Cluster Autoscaler 所做的一些改進,目的是使其更加靈活。

2 改進 Cluster Autoscaler

自定義 gRPC Expander

我們對 Cluster Autoscaler 所做的最重要的改進是提供了一種新的方法來確定要擴展的節點組。在內部,Cluster Autoscaler 維護着一個節點組列表(它們映射到不同的候選擴展),它會針對當前的 Pending(不可調度)pod 集進行調度模擬,過濾出不滿足 pod 調度需求的節點組。如果有任何 Pending(不可調度)pod,Cluster Autoscaler 會嘗試擴展集羣以容納這些 pod。任何滿足所有 pod 需求的節點組都會被傳遞給一個名爲 Expander 的組件。

圖 4:Cluster Autoscaler 和 Expander

Expander 負責根據操作要求進一步過濾節點組。Cluster Autoscaler 內置了許多不同的擴展器選項,每個選項都有不同的邏輯。例如,默認是隨機擴展器,它從可用的選項中機會均等地隨機選擇。另一個選項,也是 Airbnb 歷來使用的選項,是 優先級擴展器,它根據用戶指定的分層優先級列表選擇要擴展的節點組。

隨着我們邁向異構集羣邏輯,我們發現默認的擴展器不夠成熟,無法滿足我們以成本和實例類型選擇爲中心的更復雜的業務需求。

假如有這樣一個情況,我們想實現一個加權優先級擴展器。目前,優先級擴展器只允許用戶指定節點組的不同層級,這意味着它將始終以確定的方式按順序擴展層級。如果一個層中有多個節點組,它將隨機選擇。在默認設置下,無法實現將兩個節點組置於在同一層級的加權優先策略,在 80% 的時間內擴展一個,在 20% 的時間內擴展另一個。

除了目前支持的擴展器存在侷限性之外,還有一些操作上的考慮:

  1. Cluster Autoscaler 的發佈管道非常嚴格,變更需要時間審覈,然後再合併到上游。然而,我們的業務邏輯和所需的擴展策略在不斷變化。開發一個今天可以滿足需求的擴展器,可能無法滿足我們未來的需求。

  2. 我們的業務邏輯是特定於 Airbnb 的,其他用戶未必如此。任何我們針對自己的邏輯所做的修改,都不會對上游有什麼貢獻。

爲此,我們希望 Cluster Autoscaler 中有一個新擴展器類型可以滿足下列要求:

  1. 我們希望能有一個既可擴展又能爲他人所用的東西。其他人在大規模使用默認的 Expander 時可能會遇到類似的限制,我們希望提供一個通用的解決方案,並向上遊貢獻功能。

  2. 我們的解決方案應該可以與 Cluster Autoscaler 一起部署,並允許我們更迅速地響應不斷變化的業務需求。

  3. 我們的解決方案應該適合 Kubernetes Cluster Autoscaler 生態系統,這樣我們就不必無限期地維護 Cluster Autoscaler 的一個分叉。

基於這些要求,我們提出了一個設計,將擴展職責從 Cluster Autoscaler 的核心邏輯中分離出來。我們設計了一個可插拔的 “自定義擴展器”,它被實現爲一個 gRPC 客戶端(類似於 自定義雲提供商)。這個自定義擴展器由兩個部分組成。

第一個組件是一個內置於 Cluster Autoscaler 的 gRPC 客戶端。這個 Expander 使用了與 Cluster Autoscaler 中其他 Expander 相同的接口,負責將 Cluster Autoscaler 中有效的節點組信息轉換爲定義好的 protobuf 模式(如下圖所示),並接收 gRPC 服務器的輸出,變回最終的選項列表,供 Cluster Autoscaler 擴展。

service Expander {
  rpc BestOptions (BestOptionsRequest) returns (BestOptionsResponse)
}
message BestOptionsRequest {
  repeated Option options; 
  map<string, k8s.io.api.core.v1.Node> nodeInfoMap;
}
message BestOptionsResponse {
  repeated Option options;
}message Option {
   // ID of node to uniquely identify the nodeGroup
   string nodeGroupId;
   int32 nodeCount; 
   string debug; 
   repeated k8s.io.api.core.v1.Pod pod;
}

第二個組件是 gRPC 服務器,這需要由用戶自己編寫。這個服務器的用意是作爲一個單獨的應用程序或服務來運行,在選擇要擴展的節點組時,可以使用從客戶端傳過來的特定信息運行任意複雜的擴展邏輯。目前,通過 gRPC 傳遞的 protobuf 信息基於傳遞給 Cluster Autoscaler 中的 Expander 的信息稍微做了些轉換。

從我們前面提到的例子來看,加權隨機優先級擴展器的實現可以很簡單,讓服務器從優先級層級列表和配置圖中讀取加權百分比配置並作出相應的選擇即可。

圖 5:Cluster Autoscaler 和自定義 gRPC Expander

我們的實現包括一個故障安全選項。建議使用該選項,將 多個擴展器 作爲參數傳遞給 Cluster Autoscaler。有了這個選項,如果服務器發生故障,Cluster Autoscaler 仍然能夠使用後備擴展器進行擴展。

由於是作爲一個獨立的應用程序運行,擴展邏輯可以在 Cluster Autoscaler 之外開發。而且,由於 gRPC 服務器可以由用戶根據自己的需要進行定製開發,所以這個解決方案也是可擴展的,對整個社區都很有用。

在內部,自 2022 年初開始,Airbnb 一直在使用這個新的解決方案來擴展我們所有的集羣,沒有任何問題。它使我們能夠動態選擇何時擴展某些節點組,以滿足 Airbnb 的業務需求,實現了我們開發可擴展的自定義擴展器的最初目標。

在今年早些時候,我們的自定義擴展器 被上游的 Cluster Autoscaler 所接受,並將在下一個版本(v1.24.0)發佈時推出。

3 Autoscaler 的其他改進

在遷移到異構 Kubernetes 集羣的過程中,我們發現了其他一些 Bug 以及 Cluster Autoscaler 的可改進之處。下面簡要介紹一下這些情況:

4 小    結

在過去的四年裏,Airbnb 在 Kubernetes 集羣配置上取得了長足的進步。將 Airbnb 最大的計算部分置於單個平臺上,爲提高效率提供了一個強有力的槓桿。現在,我們正專注於一般化我們的集羣設置(考慮下 “Cattle 而非 Pets”)。通過在 Cluster Autoscaler 中開發和使用更復雜的擴展器(以及修復 Autoscaler 的其他一些小問題),我們已經實現目標,以成本和混合實例類型爲中心開發出特定於業務的複雜擴展策略,同時也爲社區貢獻了一些有用的特性。

要了解更多關於我們異構集羣遷移的細節,請觀看我們的 Kube-Con 演講。今年,我們也會參加 KubeCon EU,歡迎來和我們交流!

查看英文原文:

https://www.infoq.cn/link?target=https%3A%2F%2Fmedium.com%2Fairbnb-engineering%2Fdynamic-kubernetes-cluster-scaling-at-airbnb-d79ae3afa132%3FaccessToken%3DeyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJhY2Nlc3NfcmVzb3VyY2UiLCJleHAiOjE2NTQ5NDk4MTAsImZpbGVHVUlEIjoic1VVRnNhUEN3Tm9JMG93YyIsImlhdCI6MTY1NDk0OTUxMCwidXNlcklkIjo1MDA3OTEyfQ.OnBZWDBjAK4WxfHykQj5xEPw9I5O7XHxhG3hmNYcc0Q

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/aSPnXtmaudGjjbBuI9a77Q