新聞中心
Kubernetes Node規(guī)模突破7500
作者:張亞龍翻譯 2021-02-22 11:29:16
云計算 自上一篇有關(guān)擴展到2,500個節(jié)點的文章發(fā)表以來,我們一直在不斷擴展基礎(chǔ)架構(gòu)以滿足研究人員的需求,在此過程中我們還學(xué)到了很多經(jīng)驗。這篇文章對此作了總結(jié),以便Kubernetes社區(qū)共同受益,最后介紹我們?nèi)匀灰鎸Φ膯栴}以及解決辦法探討。

我們提供的服務(wù)有:網(wǎng)站建設(shè)、成都做網(wǎng)站、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、榆社ssl等。為近1000家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的榆社網(wǎng)站制作公司
【編者的話】2018年1月OpenAI官方博客稱,他們已將Kubernetes集群擴展到2500個節(jié)點。時隔三年,在2021年1月,OpenAI官方博客再度宣布Kubernetes集群擴展到7500個節(jié)點,目前不僅可以滿足GPT-3、CLIP 和DALL·E等大型模型的需求,而且也可以服務(wù)于快速的小規(guī)模迭代研究。下面文章來自于OpenAI官方博客,描述了走向這個7500節(jié)點規(guī)模過程中遇到的問題和解決辦法,以及對于未來走向的暢想。
我們的Kubernetes集群規(guī)模已經(jīng)上升到7,500個節(jié)點,主要為諸如GPT-3、CLIP和DALL·E等大型訓(xùn)練模型提供可擴展的基礎(chǔ)架構(gòu),而且還可用于小規(guī)??焖俚芯浚缟窠?jīng)語言模型的標(biāo)度律等。將單個Kubernetes集群擴展到如此規(guī)模很難完成,同時在這個過程中需要格外小心。但好處是借助這種簡單的基礎(chǔ)架構(gòu)使得我們的機器學(xué)習(xí)研究團隊無需更改其代碼就可以快速擴容。
自上一篇有關(guān)擴展到2,500個節(jié)點的文章發(fā)表以來,我們一直在不斷擴展基礎(chǔ)架構(gòu)以滿足研究人員的需求,在此過程中我們還學(xué)到了很多經(jīng)驗。這篇文章對此作了總結(jié),以便Kubernetes社區(qū)共同受益,最后介紹我們?nèi)匀灰鎸Φ膯栴}以及解決辦法探討。
工作負(fù)載
在我們深入討論之前,介紹一下我們的工作負(fù)載是很重要的。我們運行Kubernetes軟硬件和您在公司的情況可能不太一樣。我們的問題和相應(yīng)的解決方案可能是,也可能不是,也請您視情況而應(yīng)用!
大型機器學(xué)習(xí)作業(yè)跨越許多節(jié)點,并且只有當(dāng)可以訪問每個節(jié)點上的所有硬件資源時,才能最大化運行效率。如此一來,GPU就可以通過 NVLink直接進行交叉通信,或者GPU也可以通過GPUDirect直接與NIC通信。因此,對于我們的許多工作負(fù)載,一個節(jié)點上只放置一個Pod。任何NUMA、CPU或PCIE資源爭用都不是調(diào)度的因素,因此裝箱調(diào)度或碎片化不是一個常見的問題。我們現(xiàn)有的集群擁有完整的對分帶寬,因此也無需考慮任何機架或網(wǎng)絡(luò)拓?fù)?。所有這些都表明,我們的Kubernetes擁有許多節(jié)點,但是調(diào)度的壓力相對較低。
不過,kube-scheduler上經(jīng)常會出現(xiàn)峰值壓力。一個新的Job可能包含數(shù)百個一次性創(chuàng)建的Pod,但具有較低的使用率。
我們最大的Job上運行著 MPI 協(xié)議(消息傳遞接口協(xié)議),該Job內(nèi)的所有Pod都加入了同一個MPI通信器。如果某個Pod宕機,則整個Job都將暫停,需要重新啟動。我們會定期保存檢查點,Job重啟時會從上一個檢查點恢復(fù)。因此,可以認(rèn)為Pod是半狀態(tài)化的,終止的Pod可以被替換掉,而且Job還可以繼續(xù),但是這種做法會干擾正常的Job,應(yīng)盡量減少。
由于HTTPS通道流量很少,也不需要進行A/B測試、藍(lán)/綠或金絲雀部署,我們沒有完全依賴Kubernetes進行負(fù)載均衡。Pod之間通過SSH(而不是服務(wù)端點),利用IP地址直接通過MPI相互通信。我們的服務(wù)“發(fā)現(xiàn)”功能很有限,一般只需要在Job啟動的時候執(zhí)行一次查找去找到MPI中的Pod。
我們的大多數(shù)Job都使用了某種形式的Blob存儲。通常,它們會直接從Blob存儲,以流的形式讀取數(shù)據(jù)及或檢查點的某些分片,或?qū)⑵渚彺娴脚R時的本地磁盤。在需要POSIX語義的時候,我們也使用了一些持久卷,但是Blob存儲更容易擴展,而且不需要緩慢的分離/附加操作。
最后要提醒,我們的工作大多是基于研究性質(zhì)的,這意味著負(fù)載本身在不斷變化。盡管超算團隊努力提供了生產(chǎn)級別的計算基礎(chǔ)架構(gòu),但集群上運行的應(yīng)用程序的生命周期很短,而且開發(fā)人員的迭代非常快。新的使用模式隨時可能出現(xiàn),因此我們很難預(yù)料發(fā)展趨勢,并做出適當(dāng)?shù)恼壑?。我們需要一個可持續(xù)發(fā)展的系統(tǒng),以便在事情發(fā)生變化時迅速做出響應(yīng)。
網(wǎng)絡(luò)
由于集群內(nèi)的Node數(shù)和Pod數(shù)不斷增長,我們發(fā)現(xiàn)Flannel難以擴展到所需的吞吐量。于是,我們轉(zhuǎn)而使用原生Pod網(wǎng)絡(luò)技術(shù)來管理Azure VMSSes的IP配置和相關(guān)的CNI插件。這樣我們的Pod就能夠獲得宿主級別的網(wǎng)絡(luò)吞吐。
我們最大的集群上大約有20萬個IP地址正在使用中,在測試基于路由的Pod網(wǎng)絡(luò)時,我們發(fā)現(xiàn)可以有效利用的路由數(shù)量受到了嚴(yán)重限制。因此我們改用基于別名的IP尋址。
避免封裝增加了對底層SDN或路由引擎的要求,但它使我們的網(wǎng)絡(luò)設(shè)置保持簡單。無需任何額外的適配器就可以添加隧道。我們不需要擔(dān)心數(shù)據(jù)包分片,因為網(wǎng)絡(luò)的某些部分MTU較低。網(wǎng)絡(luò)策略和流量監(jiān)控也很簡單;數(shù)據(jù)包的源和目的地不存在歧義。
我們在宿主上使用iptables來跟蹤每個命名空間和Pod上網(wǎng)絡(luò)資源的使用情況。這樣研究人員就可以可視化網(wǎng)絡(luò)的使用情況。具體來說,因為許多實驗的互聯(lián)網(wǎng)和Pod間通信都有獨特的模式,所以能夠調(diào)查何處可能出現(xiàn)瓶頸是非常必要的。
iptables的mangle規(guī)則可以給任何符合特定規(guī)則的數(shù)據(jù)包做標(biāo)記。我們采用了以下規(guī)則來檢測流量屬于內(nèi)部還是發(fā)向外網(wǎng)。FORWARD規(guī)則負(fù)責(zé)Pod間的流量,而INPUT和OUTPUT負(fù)責(zé)來自宿主的流量:
- iptables -t mangle -A INPUT ! -s 10.0.0.0/8 -m comment --comment "iptables-exporter openai traffic=internet-in"
- iptables -t mangle -A FORWARD ! -s 10.0.0.0/8 -m comment --comment "iptables-exporter openai traffic=internet-in"
- iptables -t mangle -A OUTPUT ! -d 10.0.0.0/8 -m comment --comment "iptables-exporter openai traffic=internet-out"
- iptables -t mangle -A FORWARD ! -d 10.0.0.0/8 -m comment --comment "iptables-exporter
做好標(biāo)記后,iptables就會統(tǒng)計符合該規(guī)則的數(shù)據(jù)包的字節(jié)數(shù)。使用iptables命令就可以看到這些統(tǒng)計結(jié)果:
- % iptables -t mangle -L -v
- Chain FORWARD (policy ACCEPT 50M packets, 334G bytes)
- pkts bytes target prot opt in out source destination
- ....
- 1253K 555M all -- any any anywhere !10.0.0.0/8 /* iptables-exporter openai traffic=internet-out */
- 1161K 7937M all -- any any !10.0.0.0/8 anywhere
我們使用了一個名為 iptables-exporter 的開源 Prometheus 導(dǎo)出程序,將這些跟蹤信息導(dǎo)出到監(jiān)控系統(tǒng)中。這樣就可以直接跟蹤符合各種條件的數(shù)據(jù)包了。
我們的網(wǎng)絡(luò)模型的獨特之處在于,Node、Pod和服務(wù)網(wǎng)絡(luò)的CIDR范圍是完全暴露給研究者的。網(wǎng)絡(luò)采用了輪輻模型,使用原生節(jié)點和Pod的CIDR范圍進行路由。研究者連接到中央樞紐,從那里可以訪問到任何集群。但是兩個集群之間不能互相通信。這樣可以保證每個集群都是隔離的,不會出現(xiàn)跨集群依賴(否則會破壞故障隔離原則)。
我們使用一個“NAT”宿主對來自集群外部的流量進行CIDR范圍轉(zhuǎn)譯。這種結(jié)構(gòu)可以讓研究人員自由地選擇使用何種網(wǎng)絡(luò)配置以及怎樣使用,以滿足實驗的需要。
API Servers
對于健康工作的集群來講, API Servers和etcd是Kubernetes的關(guān)鍵組件,所以我們特別關(guān)注這些組件。我們采用了kube-prometheus提供的Grafana儀表板,以及自己設(shè)計的儀表板。我們發(fā)現(xiàn),針對API Servers上發(fā)生的HTTP 429(Too Many Requests)和5xx(Server Error)發(fā)送高級別報警非常有效。
雖然許多人在Kubernetes內(nèi)部運行API Servers,但我們選擇了在集群外部運行。etcd和API Servers都運行在獨立的節(jié)點上。最大的集群運行了5個API Servers和5個etcd節(jié)點,并以分散負(fù)載減小宕機造成的影響。自從將Kubernetes Events分離到單獨的etcd集群上以后,就再也沒有出現(xiàn)過因etcd問題導(dǎo)致的故障。API Servers是無狀態(tài)的,因此只需要運行一個自我修復(fù)的實例組或scaleset就可以。我們沒有嘗試過針對etcd集群構(gòu)建自我修復(fù)自動化,因為它極少出故障。
API Servers占用的內(nèi)存相當(dāng)多,而且內(nèi)存占用會隨著集群中的節(jié)點數(shù)量增加而呈線性增長。對于我們擁有7500節(jié)點的集群,每個API Servers上的堆空間占用最多為70GB,還好這依然在硬件能夠承受的范圍內(nèi)。
API Servers上比較大的壓力之一就是端點上的WATCH。有幾個服務(wù)的服務(wù)對象是集群中的所有成員,如kubelet、node-exporter等。每當(dāng)集群中添加或刪除節(jié)點時,就會觸發(fā)WATCH。而且由于每個節(jié)點自身都會通過kube-proxy監(jiān)視kubelet服務(wù),這些服務(wù)的響應(yīng)數(shù)量和所需帶寬就會呈N^2增長,大約每秒增加1 GB。Kubernetes 1.17中發(fā)布的EndpointSlices極大地緩解了這個壓力,它將負(fù)載降低了1000倍。
一般而言,我們會注意任何API Servers請求數(shù)量隨著集群大小而變化的情況。我們會盡量避免讓任何DaemonSet與API Servers交流。如果需要讓每個節(jié)點監(jiān)控變化,那么引入中間緩存服務(wù)(如DatadogCluster Agent)或許是避免集群范圍瓶頸的好辦法。
隨著集群的增長,我們的自動伸縮越來越少了。但偶爾也會出現(xiàn)大幅自動伸縮的情況。新的節(jié)點加入集群會產(chǎn)生許多請求,而一次性增加幾百個節(jié)點會超過API Servers能夠承受的容量。平滑請求速度,甚至僅僅增加幾秒鐘,就可以有效地避免這個問題。
使用Prometheus和Grafana測量時序列度量
我們使用Prometheus收集時序列度量,利用Grafana繪制成圖表、顯示儀表板并生成警告。首先我們部署了kube-prometheus來收集各種度量和可視化的儀表板。隨著時間的推移,我們已經(jīng)添加了許多我們自己的儀表板、指標(biāo)和警報。
隨著節(jié)點越來越多,我們逐漸難以理解Prometheus收集到的度量。盡管kube-prometheus公開了許多非常有用的數(shù)據(jù),但有些數(shù)據(jù)我們并不需要,而有些數(shù)據(jù)過于細(xì)致,很難收集、存儲和有效地查詢。因此我們使用Prometheus 規(guī)則“放棄”了一些度量。
長期以來,有一個問題一直困擾我們:Prometheus消耗的內(nèi)存越來越多,最終由于內(nèi)存耗盡而崩潰。即使給Prometheus提供大量的內(nèi)存也無濟于事。更糟糕的是,每當(dāng)出現(xiàn)崩潰,它就需要花費好幾個小時重新執(zhí)行預(yù)寫式日志(write-ahead log)文件,之后才能正常使用。
最后我們研究了Prometheus的源代碼,發(fā)現(xiàn)內(nèi)存耗盡是由于Grafana和Prometheus之間的交互導(dǎo)致的,Grafana會使用Prometheus上的/api/v1/series這個API,進行{le!=""}的查詢(含義是“獲取所有直方圖的度量”)。而/api/v1/series的實現(xiàn)在運行時間和空間上都沒有任何限制,如果查詢結(jié)果過多,就會消耗越來越多的內(nèi)存和時間。即使請求者放棄請求并關(guān)閉連接,查詢也會繼續(xù)執(zhí)行。對于我們的情況而言,無論多少內(nèi)存都不夠,Prometheus最終總會崩潰。于是,我們給Prometheus打了補丁,將這個API包裹在一個Context中以實現(xiàn)超時,終于修復(fù)了該問題。
雖然Prometheus的崩潰次數(shù)大大減少了,但我們依然需要經(jīng)常重啟,因此預(yù)寫式日志(簡稱WAL)的重新執(zhí)行依然是一個問題。重新執(zhí)行所有 WAL 通常需要花費好幾個小時,之后Prometheus才能啟動,并開始收集度量和查詢請求。在Robust Perception的幫助下,我們發(fā)現(xiàn)設(shè)置GOMAXPROCS=24可以極大地改善這個問題。因為Prometheus會在執(zhí)行WAL期間嘗試使用所有CPU核心,對于核心數(shù)量極多的服務(wù)器而言,核心之間的競爭會導(dǎo)致性能大幅度下降。
我們正在探索新的選項,以增加我們的監(jiān)測能力,如下面的“未解決的問題”一節(jié)所述。
健康檢查
面對如此龐大的集群,我們必須依賴自動化來檢測并移除任何有問題的節(jié)點。慢慢地,我們建立起了一系列健康檢查系統(tǒng)。
被動健康檢查
一些健康檢查是被動的,永遠(yuǎn)在節(jié)點上運行。這些健康檢查會監(jiān)視基本的系統(tǒng)資源,如網(wǎng)絡(luò)不通暢、磁盤失敗、磁盤寫滿或GPU錯誤等。GPU會呈現(xiàn)多種錯誤,但最常見的就是“Uncorrectable ECC error”(無法修復(fù)的ECC錯誤)。Nvidia的Data Center GPU Manager (DCGM)工具可以幫助查詢該錯誤,以及許多其他的“Xid”錯誤。跟蹤錯誤的方法之一就是使用dcgm-exporter工具將度量導(dǎo)出到Prometheus監(jiān)視系統(tǒng)中。這樣就可以創(chuàng)建DCGM_FI_DEV_XID_ERRORS度量,其內(nèi)容為最近發(fā)生過的錯誤代碼。此外,NVMLDevice Query API還可以提供有關(guān)GPU的健康情況和操作的更詳細(xì)信息。
檢測到錯誤之后,通常重啟就能修復(fù)GPU或系統(tǒng),盡管有些情況下需要更換顯卡。
另一種健康檢查會跟蹤來自上游云服務(wù)提供商的維護事件。每個主流云服務(wù)提供商都會提供一種方法,獲知當(dāng)前使用的VM是否即將維護,從而導(dǎo)致服務(wù)中斷。VM可能需要重啟,因為需要給監(jiān)視程序打補丁,或者給物理服務(wù)器更換硬件。
這些被動健康檢查在所有節(jié)點的后臺不斷運行。如果運行狀況檢查開始失敗,將自動隔離該節(jié)點,這樣就不會在該節(jié)點上調(diào)度新的Pod。對于更嚴(yán)重的健康檢查失敗,我們還將嘗試終止Pod,請求所有當(dāng)前運行的Pod立即退出。它仍然取決于Pod本身,通過Pod中斷預(yù)算進行配置,以決定它是否希望允許這種終止發(fā)生。最終,在所有Pod終止或7天過去(我們SLA的一部分)之后,我們將強制終止VM。
主動GPU測試
不幸的是,并非所有的GPU問題都能從DCGM中看到錯誤碼。我們自己構(gòu)建了GPU測試庫,能夠捕獲額外的錯誤,確保硬件和驅(qū)動程序按照預(yù)期運行。這些測試無法在后臺運行,因為運行測試需要獨占GPU幾秒鐘或幾分鐘。
首先,我們會在節(jié)點啟動時運行測試,稱為“預(yù)運行”。所有加入集群的節(jié)點都會加上“preflight” 污染并打標(biāo)簽。該污染可以防止普通Pod被調(diào)度到節(jié)點上。然后配置一個DaemonSet,在所有帶有該標(biāo)簽的Pod上運行預(yù)運行測試。測試成功后,測試程序會移除污染,節(jié)點就可以正常使用了。
我們還會在節(jié)點的生命周期內(nèi)定期執(zhí)行測試。測試通過CronJob運行,因此可以在集群中的任何可用節(jié)點上執(zhí)行。雖然這樣無法控制測試在哪個節(jié)點上運行,但我們發(fā)現(xiàn),只要時間足夠長,它就能提供足夠的測試覆蓋,同時不會對服務(wù)造成太多干擾。
配額和資源利用
當(dāng)我們擴大集群時,研究人員開始發(fā)現(xiàn)他們很難獲得分配給它們的所有能力。傳統(tǒng)的Job調(diào)度系統(tǒng)有很多不同的特性,可以在競爭團隊之間公平地運行工作,而Kubernetes沒有這些特性。隨著時間的推移,我們從這些Job調(diào)度系統(tǒng)中獲得了靈感,給Kubernetes添加了幾個原生功能。
Team taints
我們在每個集群都有一個服務(wù)“team-resource-manager”,它具有多種功能。它的數(shù)據(jù)源是一個ConfigMap,它為在給定集群中有能力的所有研究團隊指定元組(節(jié)點選擇器、要應(yīng)用的團隊標(biāo)簽、分配數(shù)量)。它與集群中的當(dāng)前節(jié)點保持一致,從而設(shè)置適當(dāng)數(shù)量的節(jié)點。
- openai.com/team=teamname:NoSchedule.
“team-resource-manager”還具有一個admission webhook服務(wù),例如,當(dāng)提交每個作業(yè)時,會根據(jù)提交者的團隊成員申請相應(yīng)的容忍度。使用taints允許我們靈活地約束Kubernetes Pod調(diào)度程序,例如允許對較低優(yōu)先級的Pod有“any”容忍度,這允許團隊在不需要重量級協(xié)調(diào)的情況下借用彼此的能力。
CPU & GPU balloons
除了使用cluster-autoscaler來動態(tài)伸縮集群之外,我們還會刪除并重新添加集群內(nèi)的不健康節(jié)點。實現(xiàn)方法是將集群的最小尺寸設(shè)置為零,最大尺寸設(shè)置為可用的容量。但是,如果cluster-autoscaler看到空閑節(jié)點,就會嘗試將集群收縮至必要限度大小。從許多角度來看(VM的啟動延遲、預(yù)分配的成本、對API服務(wù)器的影響)來看,這種空閑狀態(tài)的伸縮并不理想。
所以,我們同時為僅支持CPU的宿主和支持GPU的宿主引入了氣球部署。該部署包含一個ReplicaSet,其中設(shè)置了低優(yōu)先級Pod的最大數(shù)量。這些Pod會占用一個節(jié)點內(nèi)的資源,所以自動縮放器就不會認(rèn)為該節(jié)點閑置。但是由于這些Pod優(yōu)先級很低,因此調(diào)度器可以隨時將其驅(qū)逐,給真正的作業(yè)騰出空間。(我們選擇了使用部署而不是DaemonSet,避免DaemonSet在節(jié)點上被認(rèn)為是閑置負(fù)載。)
需要注意的一點是,我們使用了Pod反親和性來保證Pod最終會均勻地分布到節(jié)點上。Kubernetes早期版本的調(diào)度器在處理Pod反親和性時的性能為O(N^2),不過這一點在1.8版本后就修正了。
有問題的調(diào)度
我們的實驗經(jīng)常涉及到一個或多個StatefulSet,每個負(fù)責(zé)訓(xùn)練作業(yè)的一部分。至于優(yōu)化器,研究人員要求所有的StatefulSet都被調(diào)度,訓(xùn)練作業(yè)才能完成(因為我們經(jīng)常使用MPI來協(xié)調(diào)優(yōu)化器的各個成員,而MPI對于組內(nèi)成員數(shù)量的變化非常敏感)。
但是,Kubernetes默認(rèn)并不一定會優(yōu)先滿足某個StatefulSet的所有請求。例如,如果兩個實驗都要求100%的集群容量,那么Kubernetes不一定會調(diào)度某個實驗的所有Pod,而是可能會為每個實驗調(diào)度一半的Pod,導(dǎo)致死鎖狀態(tài),每個實驗都無法完成。
我們嘗試了幾種方案,但都遇到了一些極端情況,會與正常Pod的調(diào)度產(chǎn)生沖突。Kubernetes 1.18為核心調(diào)度器引入了一個插件架構(gòu),因此添加功能變得非常容易了。我們最近剛剛發(fā)布了Coscheduling plugin,以解決這個問題。
未解決的問題
隨著我們的Kubernetes集群不斷擴大,還有許多問題有待解決。一些問題包括:
度量
在目前的規(guī)模下,Prometheus自帶的TSDB存儲引擎有許多問題,例如速度很慢、重啟時需要很長時間重新執(zhí)行WAL(預(yù)寫入日志)等。查詢也很容易導(dǎo)致“查詢可能會加載過多數(shù)據(jù)”的錯誤。我們正在遷移到與Prometheus兼容的另一個存儲和查詢引擎上。
Pod網(wǎng)絡(luò)流量
隨著集群的擴大,每個Pod都會占用一定的互聯(lián)網(wǎng)帶寬。因此,每個人的互聯(lián)網(wǎng)帶寬加起來就無法忽略不計了,我們的研究人員有可能無意間給互聯(lián)網(wǎng)的其他部分帶來不可忽略的資源壓力,例如下載數(shù)。
總結(jié)
我們發(fā)現(xiàn)Kubernetes對于我們的研究需求來說是一個非常靈活的平臺。它有能力擴大規(guī)模,以滿足最苛刻的工作負(fù)載。不過,Kubernetes還有很多需要改進的地方,OpenAI的超級計算團隊將繼續(xù)探索Kubernetes如何擴展。
本文題目:KubernetesNode規(guī)模突破7500
文章位置:http://fisionsoft.com.cn/article/cdeicsd.html


咨詢
建站咨詢
