Rancher首席架構師解讀Fleet:它何以管理百萬集群?
作者簡介
Darren Shepherd,Rancher Labs聯(lián)合創(chuàng)始人及首席架構師。在加入Rancher之前,Darren是Citrix的高級首席工程師,他在那里從事CloudStack、OpenStack、Docker的工作,并構建下一代基礎設施編排技術。在加入Citrix之前,Darren曾在GoDaddy工作,他設計并領導一個團隊實施公有和私有IaaS云。
2020年年初,Rancher開源了海量集群管理項目Fleet,為大量的Kubernetes集群提供集中的GitOps式管理。Fleet最重要的目標是能夠管理100萬個分布于不同地理位置的集群。當我們設計Fleet架構時,我們希望使用標準的Kubernetes controller架構。這意味著我們可以擴展Kubernetes的數(shù)量比之前要多很多。在本文中,我將介紹Fleet的架構、我們用于測試擴展規(guī)模的方法和我們的發(fā)現(xiàn)。
為什么是100萬個集群?
隨著K3s用戶量爆發(fā)式增長(目前Github Star已經(jīng)超過15,000),邊緣Kubernetes也開始迅猛發(fā)展。一些企業(yè)已經(jīng)采用了邊緣部署模型,其中每個設備都是單節(jié)點集群?;蛘吣憧赡軙吹绞褂?個節(jié)點的集群來提供高可用性(HA)。關鍵點在于我們需要處理的是大量小型集群,而不是一個有很多節(jié)點的大型集群?,F(xiàn)如今,幾乎任何地方的工程師都在使用Linux,他們都希望使用Kubernetes來管理工作負載。雖然大多數(shù)K3s的邊緣部署少于10,000個節(jié)點,但達到100萬個節(jié)點并非不可能。而Fleet將滿足你的規(guī)模擴展要求。
Fleet架構
Fleet架構的關鍵部分如下:
Fleet使用兩階段pull方法
Fleet是一組由標準K8S API交互驅(qū)動的K8S Controller
Fleet agent不需要一直保持連接
Fleet agent本身是另一組Kubernetes controller
要從git中進行部署,F(xiàn)leet Manager首先要復制并存儲git中的內(nèi)容,然后Fleet manager決定需要使用git中的內(nèi)容更新哪個集群,并創(chuàng)建部署記錄供agent讀取。當agent可以讀取時,agent將check in以讀取部署集群,部署新的資源并報告狀態(tài)。

擴展規(guī)模測試方法
我們使用兩種方法來模擬100萬個集群。首先,我們部署了一組大型VM(m5ad.24xlarge - 384 GiB RAM)。每個VM使用k3d運行10個K3s集群。然后這10個集群每個都運行750個agent,每個agent都代表著一個下游的集群??偟膩碚f,每個VM模擬7500個集群。平均來看,部署一個VM、在Fleet注冊所有集群并達到穩(wěn)定狀態(tài)大約需要花費40分鐘。在兩天的時間里,我們以這種方式啟動虛擬機,直到達到10萬個集群。在最初的10萬個集群中,我們發(fā)現(xiàn)了大部分的擴展問題。在解決了這些問題之后,擴展變得相當可預測。以這一速度,模擬剩下的90萬個集群將會花費很長的時間以及相當可觀的資金。
然后,我們采用第二種方法:運行一個模擬器,它可以執(zhí)行100萬個集群會進行的所有API調(diào)用,而不需要下游的Kubernetes集群或部署Kubernetes資源。取而代之的是,模擬器進行API調(diào)用以注冊新集群、發(fā)現(xiàn)新部署并報告它們的成功狀態(tài)。使用這種方法,我們在一天內(nèi)實現(xiàn)了從0到100萬個模擬集群。
Fleet manager是一個運行在Kubernetes集群上的controller,運行在3個大型虛擬機(m5ad.24xlarge - 384 GiB RAM)和一個RDS(db.m5.24xlarge)實例上。實際上,我們使用K3s來運行Fleet Manager集群。我們這樣做是因為Kine已經(jīng)在其中集成了。我將在后面解釋什么是Kine以及為什么使用它。盡管K3s針對的是小規(guī)模的集群,但它可能是最容易大規(guī)模運行的Kubernetes發(fā)行版,我們使用它是因為其簡單易用。值得注意的是,在EKS這樣的托管提供商上,我們無法大規(guī)模運行Fleet,稍后我會解釋這一點。
發(fā)現(xiàn)1:調(diào)整服務賬戶和費率限制
我們遇到的第一個問題完全出乎意料。當一個Fleet agent注冊到Fleet Manager時,它會使用一個臨時的集群注冊令牌(token)。然后,該令牌被用來為該集群/agent創(chuàng)建新的身份和憑證。集群注冊令牌和該agent的憑證都是服務賬戶。我們注冊集群的速度受到controller-manager為服務賬戶創(chuàng)建令牌的速度的限制。經(jīng)過研究,我們發(fā)現(xiàn)我們可以修改controller-manager的默認設置來提高我們創(chuàng)建服務賬戶的速度(-concurrent-serviceaccount-token-syncs=100)和每秒API請求的總體數(shù)量(-kube-api-qps=10000)。
發(fā)現(xiàn)2:etcd不能在此規(guī)模下運行
Fleet是作為Kubernetes Controller編寫的。因此,將Fleet擴展到100萬個集群意味著要在Kubernetes中管理數(shù)千萬個對象。正如我們所了解的,etcd并沒有能力管理這么大的數(shù)據(jù)量。Etcd的主要空間有8GB的限制,默認設置為2GB。關鍵空間包括當前的值和它們之前尚未被垃圾收集的值。在Fleet中,一個簡單的集群對象大約需要6KB。對于100萬個集群,我們至少需要6GB。但是一個集群一般包含10個左右的Kubernetes對象,加上每個部署一個對象。所以在實際操作中,我們更有可能需要超過100萬個集群10倍的內(nèi)存空間。
?
為了繞過etcd的限制,我們使用了Kine(https://github.com/k3s-io/kine),這使得使用傳統(tǒng)的RDBMS運行任何Kubernetes發(fā)行版成為可能。在這個規(guī)模測試中,我們運行了RDS db.m5.24xlarge實例。我們沒有嘗試對數(shù)據(jù)庫進行適當?shù)拇笮≌{(diào)整,而是從最大的m5實例開始。在測試結(jié)束時,我們在Kine中擁有大約2000萬個對象。這意味著以這種規(guī)模運行Fleet不能在EKS這樣的托管提供商上進行,因為它是基于etcd的,也不會為我們的需求提供足夠可擴展的數(shù)據(jù)存儲。
?
這個測試似乎并沒有把數(shù)據(jù)庫push得很厲害。誠然,我們使用了一個非常大的數(shù)據(jù)庫,但很明顯我們還有很多垂直擴展的空間。單條記錄的插入和查找繼續(xù)以可接受的速度進行。我們注意到,隨機列出大量對象(最多一萬個)將會花費30秒到一分鐘的時間。但一般來說,這些查詢會在不到1秒的時間內(nèi)完成,或者在非常粗暴的測試下5秒也可以完成。因為這些耗時很長的查詢發(fā)生在緩存重載期間,所以對系統(tǒng)整體影響不大,我們將在后面討論。盡管這些緩慢的查詢并沒有對Fleet造成明顯的影響,但我們還是需要進一步調(diào)查為什么會出現(xiàn)這種情況。
發(fā)現(xiàn)3:增加監(jiān)控緩存大小
當controller加載緩存時,首先會列出所有對象,然后從列表的修訂版開始監(jiān)控。如果有非常高的變化率并且列出對象花費了很長的時間,那么你很容易陷入這樣的情況:你完成了列表但無法啟動監(jiān)控,因為API Server的監(jiān)控緩存中沒有這個修訂版,或者已經(jīng)在etcd中被壓縮了。作為一個變通辦法,我們將監(jiān)控緩存設置為一個非常高的值(–default-watch-cache-size=10000000)。理論上,我們認為我們會遇到Kine的壓實問題(compact),但我們沒有,這需要進一步調(diào)查。一般來說,Kine在壓實(compact)的頻率上要低很多。但在這種情況下,我們懷疑我們添加記錄的速度超過了Kine壓實的速度。這并不糟糕。我們并不希望堅持要保持一致的變化率,這只是因為我們正在快速注冊集群。
發(fā)現(xiàn)4:加載緩慢的緩存
Kubernetes controller的標準實現(xiàn)是將你正在處理的所有對象緩存在內(nèi)存中。對于Fleet,這意味著我們需要加載數(shù)百萬個對象來建立緩存。對象列表的默認分頁大小為500。加載100萬個對象需要2000個API請求。如果你假設我們可以每秒鐘進行一次列表調(diào)用、處理對象并開啟下一頁,這意味著加載緩存需要30分鐘左右。不幸的是,如果這2000個API請求中的任何一個失敗,這個過程就會重新開始。我們嘗試將頁面大小增加到10,000個對象,但看到整體加載時間并沒有明顯加快。我們開始一次列出1萬個對象之后,我們就遇到了一個問題,Kine會隨機花費超過1分鐘的時間來返回所有對象。然后Kubernetes API Server會取消請求,導致整個加載操作失敗,不得不重新啟動。我們通過增加API請求超時(-request-timeout=30m)來解決這個問題,但這不是一個可接受的解決方案。保持較小的頁面大小可以確保請求的速度更快,但請求的數(shù)量增加了失敗幾率,并導致整個操作重啟。
重啟Fleet controller將需要花費45分鐘的時間。這一重啟時間同樣適用于kube-apiserver和kube-controller-manager。這意味著你需要非常小心。這也是我們發(fā)現(xiàn)運行K3s不如運行RKE等傳統(tǒng)發(fā)行版的一點。K3s將api-server和controller-manager結(jié)合到同一個進程中,這使得重啟api-server或controller-manager的速度比原本應有的速度慢,而且更容易出錯。模擬一場災難性的故障,需要完全重啟所有服務,包括Kubernetes,這一切花了幾個小時才恢復上線。
加載緩存所需的時間和失敗的幾率是迄今為止我們發(fā)現(xiàn)的擴展Fleet的最大問題。今后,這是我們要解決的首要問題。
結(jié)? 論
通過測試,我們證明了Fleet的架構可以擴展到100萬個集群,更重要的是,Kubernetes可以作為一個平臺來管理更多的數(shù)據(jù)。Fleet本身與容器沒有任何直接的關系,可以看成只是一個簡單的應用,在Kubernetes中管理數(shù)據(jù)。這些發(fā)現(xiàn)為我們開啟了一個可能性,即把Kubernetes更多的當作一個通用的編排平臺來寫代碼。當考慮到你可以很容易地將一套controller與K3s捆綁在一起,Kubernetes就變成了一個很好的自成一體的應用server。
在擴展規(guī)模方面,重新加載緩存所需的時間令人擔憂,但絕對是可控的。我們將繼續(xù)在這方面進行改進,使運行100萬個集群不僅是可行的,而且是簡單的。因為在Rancher Labs,我們喜歡簡單。