自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

假如重新設(shè)計Kubernetes

云計算
最近,我和領(lǐng)域內(nèi)的專家Vallery Lancey有過一次閑聊,主題是關(guān)于Kubernetes的。具體說來,假設(shè)我們從零開始設(shè)計一個新的編排系統(tǒng),不必拘泥于與現(xiàn)有Kubernetes的兼容性,我們可能會采取哪些不同的做法。我發(fā)現(xiàn)對話過程非常有意義,以至于我覺得有必要記錄下期間涌現(xiàn)的諸多想法,所以就有了這篇文章。

最近,我和領(lǐng)域內(nèi)的專家Vallery Lancey有過一次閑聊,主題是關(guān)于Kubernetes的。具體說來,假設(shè)我們從零開始設(shè)計一個新的編排系統(tǒng),不必拘泥于與現(xiàn)有Kubernetes的兼容性,我們可能會采取哪些不同的做法。我發(fā)現(xiàn)對話過程非常有意義,以至于我覺得有必要記錄下期間涌現(xiàn)的諸多想法,所以就有了這篇文章。

[[359537]]

落筆前,我想強調(diào)幾點:

  • 這不是一個完全成形的設(shè)計。其中某些想法可能根本無法落地,或者需要進行大量的重構(gòu)。很多章節(jié)的內(nèi)容都是想到哪寫到哪。
  • 這些觀點不單純是我一個人的想法。有些是我原創(chuàng)的,更多的是集體思考,交流碰撞后的產(chǎn)物,就像Kubernetes社區(qū)中的許多設(shè)計一樣。我知道至少Vallery和Maisem Ali不止一次地啟發(fā)了我的思考,還有更多我說不出名字的。如果你覺得文中有些想法很不錯,那請把它當成是集體努力的結(jié)果。如果你不太認同其中一些看法,那把它當成是我個人的小小失誤吧。
  • 文中的一些觀點是非常極端激進的。我只是嘗試把腦海的一些設(shè)計表達出來,一吐為快。

設(shè)計原則

我過往的Kubernetes實踐經(jīng)驗來自兩個截然不同的地方:一個是為裸金屬機集群維護MetalLB;另一個是在GKE SRE中運維大型的集群即服務(wù)(clusters-as-a-service)。這兩個經(jīng)歷都讓我覺得,Kubernetes相當復雜,要達到目前市面上宣傳的效果,往往需要做大量的前置工作,而大多數(shù)躍躍欲試的用戶對此都沒有充分的準備。

維護MetalLB的經(jīng)歷告訴我,想構(gòu)建與Kubernetes集成的健壯性優(yōu)異的軟件十分困難。我認為MetalLB穩(wěn)定性堪稱優(yōu)秀,但是Kubernetes還是非常容易使它出現(xiàn)配置錯誤的情況,而調(diào)試起來也相當費勁。 GKE SRE的運維經(jīng)歷則教會我,即使是最出色的Kubernetes專家也無法不出差錯地運維大規(guī)模Kubernetes集群(盡管GKE SRE借助一些工具能做得非常出色)。

Kubernetes可以類比成編排軟件中的C ++。功能強大,特性完備,看上去似乎挺簡單,但你會不停踩坑,直到你投入大量時間和精力,去弄清它的所有原理為止。即便如此,Kubernetes的配置和部署方式方法眾多,生態(tài)還在不停發(fā)展,以至于很難讓人覺得可以停下腳步歇口氣了。

按照這個類比,我理想的參照物是Go。如果Kubernetes是C ++,那么編排系統(tǒng)中Go會是什么樣的呢?極度簡潔,特點突出,緩慢而謹慎地拓展新特性,你可以在不到一周的時間里上手,然后就能用它去完成你的工作。

接下來,我們就遵循上面這些原則開始了。重新設(shè)計一個Kubernetes,可以不考慮和現(xiàn)有的兼容,另辟蹊徑,該考慮哪些點呢?

可修改的Pod

在Kubernetes中,大部分(但不是全部)Pod在創(chuàng)建后是不可變的。如果你想直接修改一個Pod,不行。得重新創(chuàng)建一個再刪掉舊的。這與Kubernetes中的大多數(shù)資源對象的處理方式不同,在Kubernetes中的大多數(shù)對象都是可變的,并且當預期狀態(tài)出現(xiàn)變化時,可以優(yōu)雅地將實際狀態(tài)協(xié)調(diào)到與預期一致。

因此,我不想讓Pod成為一個例外。我打算將它設(shè)計成完全可讀寫,并讓它擁有像其它資源對象一樣的調(diào)諧邏輯。

對此我第一時間想到的方案是原地重啟。如果Pod的調(diào)度約束和需求資源前后沒有改變,猜一下如何實現(xiàn)? 發(fā)出SIGTERM信號終止runc,使用不同的參數(shù)重新啟動runc,就已完成重啟。這樣一來,Pod有點像從前的systemd服務(wù),必要時還可以在不同機器之間漂移。

請注意,這不需要在運行時層面操作可變性。如果用戶更改了Pod定義,仍然可以先終止容器并使用新配置重新啟動容器。 Pod仍會保留在原節(jié)點上預留的資源,因此從概念上講,它等效于systemctl restart blah.service。你也可以嘗試在并在運行時層級上來執(zhí)行Pod的更新操作,但其實沒有必要這樣做。不這么做的主要好處是將調(diào)度、Pod生命周期及運行時生命周期管理解耦。

版本管理

來繼續(xù)討論Pod的設(shè)計:既然它現(xiàn)在是可變的了,那么我接下來考慮的事情自然而然就是Pod回滾。為此,讓我們保留Pod舊版本的定義,如此一來“回滾到特定舊版本”就輕而易舉了。

現(xiàn)在,Pod更新流程如下:編寫文件更新Pod定義,并進行更新以符合預期定義。更新出錯?回滾上一個版本,流程結(jié)束。

上述流程的好處是:無需依賴所謂的GitOps,即可輕松了解到集群中應(yīng)用的版本迭代。如無必要就不用引入GitOps,盡管它有不少優(yōu)點。如果,你只希望解決一個很基本的“集群發(fā)生了什么變化?”的問題,僅僅使用集群中的數(shù)據(jù)就夠了。

其中還涉及到更多設(shè)計細節(jié)。尤其是我想將外部更改(用戶提交Pod的變更)與系統(tǒng)變更(Kubernetes內(nèi)部觸發(fā)的Pod定義變更)這兩者區(qū)分開。我還沒有考慮清楚如何對這兩種變更的歷史信息進行編碼,并使得運維人員和其他系統(tǒng)組件都可以獲取到這些變更。也許可以設(shè)計成完全通用的,“修改人”在提交新版本時會標識自己。然后用戶可以指定特定修改人(或排除特定修改人)以查詢某類變更(類似于標簽查詢的工作原理)。同樣的,這里還需要更多的設(shè)計考量,我確定的是我想要一個具有版本管理特性的Pod對象,可以查詢它的歷史版本記錄。

最后,還需要考慮垃圾回收。具體說就是,對每個Pod的更改應(yīng)該可以很好地進行增量壓縮。默認設(shè)置是保留所有變更內(nèi)容,積累到一定數(shù)據(jù)量后,在此基礎(chǔ)上進行一次壓縮。保留所有變更內(nèi)容也會對系統(tǒng)產(chǎn)生一定壓力,但可避免“因頻繁提交更改”而給系統(tǒng)的其它部分帶來影響。這里用戶要注意,為方便聚合,應(yīng)該進行次數(shù)更少同時更有意義的變更,而不是每次改動一個字段進而產(chǎn)生一系列版本。

一旦有了歷史版本這個功能后,我們還可以整一些其它的小功能。例如,節(jié)點可以將最近的若干個版本的容器鏡像保留在節(jié)點上,從而使回滾更快。原來的垃圾回收超過一定期限就觸發(fā),有了歷史版本記錄,可以更精確地保留需要的版本數(shù)。概括而言,所有編排軟件都將舊版本用作各種資源對象的GC roots,以加快回滾速度。回滾是避免服務(wù)中斷的基本方式,這是非常有價值的事情。

用PinnedDeployment替換Deployment

這是部分內(nèi)容比較簡短,主要是受Vallery啟發(fā)。他的PinnedDeployment設(shè)計非常讓人驚嘆。PinnedDeployment使運維人員可以通過跟蹤兩個版本的Deployment狀態(tài)來控制應(yīng)用發(fā)布。這是由SRE設(shè)計的部署對象。設(shè)計人員非常清楚SRE在部署中的關(guān)注的焦點。我個人很喜歡這個設(shè)計。

這可以和上面的可版本管理、可原地更新的Pod結(jié)合得非常好,真想不到還有什么可以添加的了。它非常清晰的解釋了多個Pod時的工作流程。要從Kubernetes各項約束中脫離來適應(yīng)這一個全新的流程,可能需要做些調(diào)整,但是大體設(shè)計是非常不錯的。

顯式的編排流程

我認為Kubernetes的“API machinery”機制最大問題是編排,即一系列獨立控制循環(huán)的松散集合所構(gòu)成工作流程。從表面上看,這似乎是一個好主意:你有好幾十個微小的控制循環(huán),每個控制循環(huán)只負責一個小功能。當它們被整合到一個集群時,它們彼此互相協(xié)作以調(diào)諧資源對象狀態(tài)并收斂至符合預期的最終狀態(tài)。所以這其中有何問題?

問題就在于出現(xiàn)錯誤時幾乎不可能去進行調(diào)試。 Kubernetes中典型的出錯,就是用戶將變更提交給集群,然后反復刷新以等待資源對象符合預期,如果遲遲沒有符合……那么,來問題了。 Kubernetes分辨不出“對象已符合預期”和“控制循環(huán)被中斷并阻塞了其他事物”之間的區(qū)別。你或許希望有問題的控制循環(huán)會發(fā)布它所管理對象的一些事件來幫你排錯,但總的來說它們發(fā)揮不了多少作用。

此時,你唯一的可行選擇是收集可能涉及的每個控制循環(huán)的日志,尋找被中斷的循環(huán)。如果你對所有控制循環(huán)的工作機制都有深入的了解,則定位錯誤的速度可以更快一些,豐富的經(jīng)驗可以讓你從資源對象的當前狀態(tài),推斷出是哪個控制循環(huán)出錯,并正嘗試恢復運行。

這里要注意關(guān)鍵一點,我們看待復雜度的視角已經(jīng)從控制循環(huán)的設(shè)計者轉(zhuǎn)換到到了集群運維人員。設(shè)計一個可以獨立執(zhí)行單一任務(wù)的控制循環(huán)很容易(并非是說其不重要)。但是,要在集群中維護數(shù)十個這樣的控制循環(huán),就需要運維人員非常熟悉這些控制循環(huán)的操作,以及它們之間的交互,并嘗試理解這樣一個組織松散的系統(tǒng)。這是必須認真考慮的問題,通常設(shè)計者編寫控制循環(huán)代碼驗證其功能這樣的工作是一次性的,但是運維人員可能要終日和它們打交道,并反復處理控制循環(huán)出現(xiàn)的問題。簡化那些你只需要做一次的事情對運維人員來說不公平。

為了解決這個問題,我會參照systemd的做法。它解決了類似的生命周期管理問題:給定當前狀態(tài)A和目標狀態(tài)B,如何從A變?yōu)锽?區(qū)別是,在systemd中,操作步驟及其依賴是顯式的。你告訴systemd,你的服務(wù)單元是multi-user.target服務(wù)組的一部分,則它必須在掛載文件系統(tǒng)之后聯(lián)網(wǎng)之前啟動運行。您還可以依賴系統(tǒng)的其他具體組件,例如說只要sshd運行,你的服務(wù)就需要運行(聽起來像邊車,是吧?)。

這樣做的好處是systemd可以準確地告訴用戶,是系統(tǒng)的哪一部分發(fā)生故障,哪部分仍在運行,或是哪個前置條件沒通過。它甚至還可以打印出系統(tǒng)啟動的執(zhí)行過程,以供分析定位問題,例如“哪個服務(wù)的啟動耗時最長”。

我想批量的照搬這些設(shè)計到我的集群編排系統(tǒng)中。不過也確實需要一些微調(diào),但大致來說:控制循環(huán)必須聲明它們對其他控制循環(huán)的依賴性,必須生成結(jié)構(gòu)化日志,以便用戶可以輕松搜索到“有關(guān)Pod X的所有控制循環(huán)的操作日志”,并且編排系統(tǒng)處理生命周期事件,可以采取像systemd那樣的做法,逐個排查定位到出問題的服務(wù)組單元。

這在實踐起來會是怎么樣的?先結(jié)合Pod的生命周期說起??赡芪覀儗⒍x一個抽象的“運行”target,這是我們要達到的狀態(tài)——Pod已經(jīng)啟動并且一切正常。容器運行時將添加一個任務(wù)到“運行”之前,以啟動容器。但它可能要到存儲系統(tǒng)完成網(wǎng)絡(luò)設(shè)備掛載后才能運行,因此它將在“存儲”target之后自行啟動。同樣地,對于網(wǎng)絡(luò),容器希望在“網(wǎng)絡(luò)”target之后啟動。

現(xiàn)在,你的Ceph控制循環(huán)將自己安排在“存儲”target之前運行,因為它負責啟動存儲。其他存儲控制循環(huán)也是相同的執(zhí)行流程(local bind mount,NFS等)。請注意,這里的執(zhí)行流程可以是并發(fā)執(zhí)行,因為它們都聲明要在存儲準備就緒之前執(zhí)行,但是并不在意在其他存儲插件的控制循環(huán)之前還是之后執(zhí)行。也有可能存在例外情況!比如你編寫了一個很棒的存儲插件,它功能出色,但是必須先進行NFS掛載,然后才能運行。好了,我們只需要在nfs-mounts步驟中添加一個依賴項,就可以完成了。這就和systemd類似,我們既規(guī)定了順序,又規(guī)定了“還需要其他組件才能正常工作”這樣的硬性要求,因此用戶可以輕松定義服務(wù)的啟動步驟。

(此處的討論我稍微簡化了一下,并假設(shè)各項操作步驟沒有太多的循環(huán)依賴。要深入的話,這可以展開出更復雜的流程。請參閱下文進一步探討,這里先不討論太過于復雜的流程。)

有了這些設(shè)計,編排系統(tǒng)可以回答用戶“為什么Pod沒有啟動?”用戶可以dump下Pod的啟動流程圖,并查看哪些步驟已完成,哪些步驟失敗,哪些已在運行。 NFS掛載已經(jīng)進行了5分鐘?會不會有可能是NFS服務(wù)器已掛掉,但控制循環(huán)沒報超時?服務(wù)的各項配置和可能的狀態(tài),疊加出來的結(jié)果矩陣是非常龐大的:如果有了我們設(shè)計的這樣一個輔助調(diào)試的工具,這也不算個大問題。 Systemd允許用戶以任意順序、任意約束往服務(wù)的啟動過程添加內(nèi)容。但是當出現(xiàn)問題時,我仍然可以輕松對其進行故障排查,根據(jù)約束條件,在調(diào)試工具的輔助下,我可以第一時間定位到問題的關(guān)鍵所在。

和Systemd給系統(tǒng)啟動帶來的好處類似,這讓系統(tǒng)可以盡可能地并行執(zhí)行生命周期操作,但也僅此而已。而且由于工作流程是顯式的,它還可以擴展。你的集群是否存在這種情況:在每個Pod上都有企業(yè)定制的操作,且必須在某個生命周期階段內(nèi)執(zhí)行的?可以定義一個新的中間target,使其依賴于于正確的前置或后置條件,然后將控制循環(huán)掛接(hook)到這里接收回調(diào)。編排系統(tǒng)將確保控制循環(huán)在生命周期的各階段發(fā)揮作用。

請注意,這還解決了諸如Istio之類的存在奇葩問題。在Istio中,它們必須注入一些額外的開發(fā)者提供的定義才能起作用。沒必要!提供對應(yīng)的控制循環(huán)介入到生命周期管理中,并根據(jù)實際需要在進行調(diào)整。只要你可以向系統(tǒng)表示,在生命周期中某個特定階段需要執(zhí)行操作的,就無需考慮通過向運維人員提供額外的資源對象去操作。

這部分內(nèi)容很長,但想表達的意思很簡短。這和Kubernetes的原來的API machinery大相徑庭,因此需要大量新的設(shè)計工作才能實現(xiàn)。最突出的變化,控制循環(huán)不再只是簡單地觀察集群對象的狀態(tài)并做出修正,還必須等待編排器(Orchestrator)完成對特定對象的調(diào)用,當這些對象達到符合預期的狀態(tài)時,控制循環(huán)再進一步響應(yīng)。你現(xiàn)在可以通過注解和約定,將其在Kubernetes實現(xiàn)上。但除非對工作機制的細枝末節(jié)的都了解得一清二楚,否則就可觀察性和可調(diào)試性來說,沒什么幫助。

有趣的是,Kubernetes已經(jīng)有其中一些想法的原型實現(xiàn):Initializers和Finalizers 。它們分別是生命周期兩個不同階段里執(zhí)行預操作的鉤子。它使您可以將控制循環(huán)掛接到兩個硬編碼的“target”上。他們將控制循環(huán)分為三個部分:初始,“默認”和終結(jié)。雖然是硬編碼,這是顯式工作流程圖的雛形。我打算把這個設(shè)計推廣到更一般的情況。

顯式的字段歸屬

承接上一部分設(shè)計的適度擴展:使資源對象的每個字段都被特定的控制循環(huán)顯式擁有。該循環(huán)是唯一允許寫入該字段的循環(huán)。如果未定義所有者,則該字段可被集群運維人員寫入,但運維人員不能寫其他任何內(nèi)容。這是由API machinery(而非約定)強制執(zhí)行的。

這已經(jīng)是大多數(shù)情況,但還是存在字段所有權(quán)模糊不清的時候。這導致兩個問題:如果字段錯誤,則很難弄清誰負責;而且一不小心就會進入到兩個控制器修改一個字段的情況,陷入循環(huán)。

后者是MetalLB存在的大麻煩,它與其他一些負載均衡器實現(xiàn)方式發(fā)生了沖突。不應(yīng)該出現(xiàn)這樣的情況。Orchestrator應(yīng)該拒絕MetalLB添加到集群中,因為與LB相關(guān)的字段將有兩個所有者。

可能需要留個后門,讓用戶處理一個字段有多個歸屬者的情況。但簡單起見,我在這里先不考慮,然后看看設(shè)計是不是經(jīng)得起考驗。除非有充分證明支持,否則共享所有權(quán)就是一個會帶來潛在隱患的設(shè)計。

如果你還要求控制循環(huán)顯式注冊讀取的字段(并把那些沒有注冊的字段剝離出來——不準作弊),這也可以讓你做一些有意義的事情,比如證明系統(tǒng)收斂(沒有讀->寫->讀的循環(huán)),或是幫你照出拖慢系統(tǒng)響應(yīng)速度的調(diào)用環(huán)節(jié)。

有且只有IPv6

我對Kubernetes網(wǎng)絡(luò)部分非常熟悉,它是我最想全盤推倒重來的一個部分。有很多原因造成了網(wǎng)絡(luò)模塊發(fā)展成今天這個局面,我并不是想說Kubernetes網(wǎng)絡(luò)設(shè)計得一無是處。但網(wǎng)絡(luò)不在我的編排體系里。這部分內(nèi)容很長,請帶點耐心。

首先,讓我們先徹底拋開Kubernetes現(xiàn)有的網(wǎng)絡(luò)。覆蓋網(wǎng)絡(luò),Service, CNI,kube-proxy,網(wǎng)絡(luò)插件,這些統(tǒng)統(tǒng)都不要了。

(順便提一句,為什么網(wǎng)絡(luò)插件是不應(yīng)該出現(xiàn)K8s理想的設(shè)計中的。目前,已經(jīng)有不少企業(yè)開始兜售他們的網(wǎng)絡(luò)插件了,你最好不要相信他們能保持客觀中立,讓我來列出反駁他們的理由。無論是自然界還是軟件界,所有生態(tài)系統(tǒng)的第一要務(wù)都是確保其繼續(xù)存在。你不能要求一個生態(tài)系統(tǒng)自我進化到滅亡,你必須從外部觸發(fā)滅亡。)

回到正題,現(xiàn)在一切歸零了。我們有容器,它們需要互相通信,和外部通信。那該做什么?

讓我們賦予每個Pod一個IPv6地址。是的,只有一個IPv6地址。它們從哪里來?這里要求局域網(wǎng)支持IPv6(假定具備這樣的條件,畢竟我們的設(shè)計要面向未來),IP地址就從這來。你幾乎都不需要做IP地址沖突檢測,2^64足夠大,隨機生成的IP地址基本上就滿足需求了。我們需要一個機制,好讓每個節(jié)點上之間能互相發(fā)現(xiàn),這樣就可以找到其他 Pod 在哪個節(jié)點上。這應(yīng)該不難實現(xiàn),這么做的理由很簡單:對集群網(wǎng)絡(luò)內(nèi)的其他部分而言,一個 Pod 看起來就像是在運行其中的某個節(jié)點。

或者我們干脆組一個全是唯一本地地址的網(wǎng)絡(luò),然后手動在每個節(jié)點上做路由。這實現(xiàn)起來非常容易,而且地址分配基本上就是 "隨便選一個數(shù)字就可以了"。可能還需要設(shè)計一個子集,這樣節(jié)點到節(jié)點的路由才會更有效率,但這都是不太復雜的東西。

有個麻煩是云服務(wù)商喜歡介入到網(wǎng)絡(luò)的基礎(chǔ)部分。所以IPAM模塊要保持可插拔性(在上文所提到工作流模型之內(nèi)),這樣我們就可以做一些事情,比如向AWS解釋流量是如何轉(zhuǎn)發(fā)的。不過,使用IPv6可能就無法在GCP上運行了。

不管怎么說,有許多備選的方案來做這部分的設(shè)計。就其根本而言,我只想在節(jié)點之間使用IPv6和配置一些基本的、簡單的路由就可以了。這樣就可以在接近零配置的情況下,解決Pod之間的連接問題,因為IPv6是有足夠大的地址空間,我們選一些隨機數(shù)字就能完事。

如果你有更復雜的連接需求,你就把這些作為額外的網(wǎng)絡(luò)接口和我設(shè)計的簡單、可預測的IPv6路由接上。需要保證節(jié)點間的通信安全?引入wireguard隧道,添加路由,通過wireguard隧道推送節(jié)點IP,完事。編排系統(tǒng)不需要知道這些細枝末節(jié),除了可能會在節(jié)點生命周期管理中添加一個小小的控制循環(huán),這樣在隧道建立好之后,才讓節(jié)點處于就緒狀態(tài)。

好了,Pod之間的互聯(lián)互通,Pod和外部網(wǎng)絡(luò)的連接,這兩個問題都解決了??紤]到現(xiàn)在只有IPv6,我們?nèi)绾翁幚砹魅爰旱腎Pv4流量呢?

首先,我們規(guī)定IPv4只適用于Pod和Internet連通的這種情況。在集群內(nèi)必須強制使用IPv6。

我們可以用幾種方法來應(yīng)對這個限制。簡單來說,我們可以讓每個節(jié)點封裝IPv4流量,為Pod預留一小塊符合RFC 1918規(guī)范的地址空間(所有節(jié)點上預留的地址都從屬于這個空間)。這樣就可以讓它們到達IPv4互聯(lián)網(wǎng),但這都是每個節(jié)點的靜態(tài)配置,根本不需要集群可見。你甚至可以將IPv4的東西完全隱藏在控制平面中,這只是每臺機器運行時的一個實現(xiàn)細節(jié)。

我們也可以用NAT64和CLAT來找點樂子:讓整個網(wǎng)絡(luò)只用IPv6,但用CLAT來欺騙Pods,讓它們以為自己有IPv4連接。然后在Pod內(nèi)進行 IPv4到IPv6的地址翻譯,并將流量發(fā)送到NAT64網(wǎng)關(guān)??梢允敲颗_機器的NAT64,也可以是集群內(nèi)的部署的。如果你需要處理大量的NAT64流量,甚至可以用一個集群外部類似CGNAT這樣的東西。在這一點上,CLAT和NAT64已經(jīng)有很好的應(yīng)用:你的手機可能正是通過這樣的方式來讓你獲得IPv4地址接入互聯(lián)網(wǎng)。

我可能會從簡單的IPv4偽裝開始(第一種方案),因為所需的配置量極少,都可以由每臺機器在本地處理,不會有任何交叉影響,讓我們更容易著手實現(xiàn)。另外,后期改起來也很方便,因為在Pod看來都是一樣的,而且我們也不希望通過一個網(wǎng)絡(luò)插件來處理任何東西。

到這我們已經(jīng)處理了出站方面的問題,我們有雙棧上網(wǎng)。接下來怎么處理入站端呢?負載均衡器。不考慮把它構(gòu)建在核心編排系統(tǒng)中。編排系統(tǒng)應(yīng)該專注于一件事:如果一個數(shù)據(jù)包的目的IP是Pod IP,就把這個數(shù)據(jù)包交付給Pod。

正好,這應(yīng)該主要適合公有云的場景。廠商們也傾向于這樣的模型,這樣就可以把他們的負載均衡器產(chǎn)品賣給你了。好吧,你贏了,姑且先采取這樣的設(shè)計模型。不過我想要一個控制循環(huán)來控制負載均衡器,并將其與IPAM集成,這樣VPC就能明白如何將數(shù)據(jù)包路由到Pod IP。

這忽略了由物理機搭建集群的場景。但這也不是一件壞事,因為沒有一個放之四海而皆準的負載均衡器。如果我試圖給你一個負載均衡器,但它沒有完全按照你預想的工作。這說不定還會讓你抓狂,一氣之下裝起了Istio,這時我所討論到降低復雜性都是無用功了。

讓負載均衡器集中精力把一件事做好:如果要把數(shù)據(jù)包轉(zhuǎn)發(fā)給Pod,那就把數(shù)據(jù)包轉(zhuǎn)發(fā)給Pod。在遵循這一原則的前提下,你可以基于LVS、Nginx、無狀態(tài)、云廠商負載均衡服務(wù)、F5等來構(gòu)建負載均衡器,你可以自由發(fā)揮。這里也許可以考慮提供幾個“默認”實現(xiàn)。對于負載均衡器這部分,我確實有很多想法,也許我設(shè)計的方案就挺合適。這里的關(guān)鍵是編排系統(tǒng)對負載均衡器如何實現(xiàn)毫不關(guān)心,只要能把數(shù)據(jù)包轉(zhuǎn)發(fā)到Pod上。

我沒有觸及IPv4 ingress的問題,主要是我認為這是負載均衡器該做的事情,讓它們各自用最合適的方式來解決問題。像Nginx這樣的代理型負載均衡器,只需要通過IPv6轉(zhuǎn)發(fā)后端就可以了,沒什么問題。無狀態(tài)的負載均衡器可以很容易地將IPv4地址轉(zhuǎn)換成IPv6,其間有個轉(zhuǎn)換標準。源地址為::fffff:1.2.3.4數(shù)據(jù)包到達Pod時,Pod可以將其轉(zhuǎn)回IPv4。或者干脆將其視為IPv6直接處理,這樣的處理方式就假定網(wǎng)絡(luò)中的地址都是IPv6。如果使用了無狀態(tài)翻譯方式,出站的時候需要有狀態(tài)的跟蹤機制,來映射回IPv4。但這也比原先在IPv4下采取的層層封裝方式來得好。從節(jié)點的視角來看,這完全可以通過一條額外的::fffff:0000:0000/96的路由來處理。

將極簡貫徹到底

作為上述所有網(wǎng)絡(luò)問題的替代方案,我們干脆都不要了?;氐紹org式的端口分配,所有服務(wù)都在主機的網(wǎng)絡(luò)命名空間中運行,并且必須請求分配端口。不是監(jiān)聽:80,而是監(jiān)聽:%port%,然后編排系統(tǒng)會用一個未使用的端口號來代替。例如,最終會變成監(jiān)聽主機上的:53928。

這樣的設(shè)計真的非常非常簡單。簡單到基本沒什么需要額外做的。在分配端口時,需要做一些煩人的檢查,以避免端口沖突,這倒是一個令人頭疼的問題。還有一個端口耗盡的問題,因為如果你的客戶端非?;钴S且數(shù)量不少,65000個端口其實并不算太多。但這個真的非常非常簡潔。我個人崇尚簡潔。

我們也可以采用經(jīng)典的Docker方式,將其和上面的設(shè)計結(jié)合起來:容器在自己的網(wǎng)路命名空間中運行,使用一些臨時的私有 IP。你可以使用任意的端口,但對其他Pod和外部可見的只有那些告知運行時要暴露的端口。而且你只能聲明要暴露的容器端口,映射到主機上的端口是由容器運行時選擇的。(這里也可以留一些后門來應(yīng)對特例,你可以告訴系統(tǒng)你非要80端口不可,然后通過調(diào)度約束來起作用,調(diào)度到80端口沒被占用的機器——類似于當前Kubernetes在這方面的處理。)

上面論述的關(guān)鍵點是,這些設(shè)計極大地簡化了網(wǎng)絡(luò)層。以至于我可以在短短幾分鐘內(nèi)向別人解釋清楚,確保他們能夠了解其工作機制。

缺點是這把復雜性推給了服務(wù)發(fā)現(xiàn)。你不能使用“純粹的DNS”作為發(fā)現(xiàn)機制,因為大多數(shù)DNS客戶端不解析SRV記錄,因此不會動態(tài)發(fā)現(xiàn)隨機端口。

搞笑的是,服務(wù)網(wǎng)格的逐漸普及讓這個問題不再是個問題。因為人們現(xiàn)在假設(shè)存在一個本地智能代理,它可以做任何服務(wù)發(fā)現(xiàn)能做的事情,并將其映射到一個網(wǎng)絡(luò)視圖上,而這個視圖只被需要它的 Pod 看到。不過,我不太愿意接受這種做法,因為服務(wù)網(wǎng)格增加了太多的復雜性和維護成本,所以我不想采用它們……至少在有實踐方案表明能使它們良好運行之前,我維持這樣的觀點。

所以,我們不妨做一些類似服務(wù)網(wǎng)格的東西,但更簡單點。在源主機上做一些自動的IP端口轉(zhuǎn)換……不過這看起來很像kube-proxy,這隨之而來的就是復雜性和調(diào)試困難(這不是一個通過在不同的地方執(zhí)行tcpdump就能解決的問題,因為流量會在不同的跳數(shù)之間變化)。

所以,這個方案也表明顯式主機端口映射可能也算個解決方案,但仍存在很多隱藏的復雜性(我相信這就是為什么Kubernetes一開始就采用單Pod單IP的原因)。Borg通過強制規(guī)定解決了這種復雜性,它規(guī)定了應(yīng)用都必須用自家設(shè)計的依賴庫和框架。所以這里有個顯而易見的缺點,不能隨意更換的服務(wù)發(fā)現(xiàn)和負載平衡的實現(xiàn)框架。除非我們采用真正的服務(wù)網(wǎng)格,否則做不到這點。

本節(jié)描述的方案還有可改善之處,但我更傾向于上一節(jié)的實現(xiàn)。它的設(shè)計時要考慮的東西更多一些,但可以得到是一個可組合、可調(diào)試、可理解的系統(tǒng),不需要無限制地增加功能以滿足新需求。

安全同樣重要

長篇大論的探討完網(wǎng)絡(luò)之后,來簡單說一下安全問題。容器默認應(yīng)該被最大限度的沙盒化,并需要顯式的雙重確認步驟。

我們可以直接應(yīng)用Jessie Frazelle在容器安全方面出色的工作成果。打開默認的apparmor和seccomp配置文件。不允許在容器中以root身份運行。使用user命名空間進行隔離,這樣哪怕有人設(shè)法在容器中升級為root,那也不是系統(tǒng)root。默認阻止所有設(shè)備掛載。不允許主機綁定掛載。為了達到效果,寫一個你能想的的最嚴格的Pod安全策略,然后把他們作為默認值,并且讓它們很難背離默認值。

Pod安全策略與此相當接近,因為它們強制執(zhí)行雙重確認:集群運維人員確認允許用戶做不安全的操作,而用戶必須顯式申請權(quán)限執(zhí)行不安全的操作。遺憾的是,Kubernetes現(xiàn)有的設(shè)計并沒有這么考慮。這里我們先不關(guān)心向下的兼容性,把默認值做得盡可能安全。

(溫馨提示:從這里開始,章節(jié)內(nèi)容開始有點天馬行空。這些都是我想要的設(shè)計,不過我強烈意識到,很多細節(jié)沒有考慮清楚。)

gVisor?Firecracker?

說到默認情況下的最高級別的安全,我覺得不妨采取更激進的沙盒化措施??梢钥紤]將gVisor或Firecracker作為默認容器沙箱,并開啟雙重確認機制,最終達到“與主機共享內(nèi)核的最安全的容器環(huán)境”這目的?

這里需要再斟酌斟酌。一方面,這些工具所承諾的極度安全非常吸引我。另一方面,這也不可避免地要運行一大堆額外的代碼,也帶來潛在漏洞和復雜性。而且這些沙盒對你能做的事情進行了極度的限制。甚至,任何與存儲有關(guān)的事情都會演變成“不,你不能有任何存儲”。這對于某些場景而言來說是不錯,但把它變成默認值就限制得太過分了。

至少在存儲方面,virtio-fs的成熟會解決很多這樣的問題,能讓這些沙盒在不破壞安全模型的前提下,執(zhí)行有效地綁定和掛載操作??赡芪覀儜?yīng)該在那個時候再重新審視這個決定?

去中心化集群

我猜這個時髦的術(shù)語應(yīng)該是“邊緣計算”,但我真正的意思是,我想讓我所有的服務(wù)器都在一個編排系統(tǒng)下,把它們作為一個單元來運作。這意味著我家里服務(wù)器機架上的計算機,我在DigitalOcean上的虛擬機,以及在互聯(lián)網(wǎng)上的其他幾臺服務(wù)器。這些都應(yīng)該是集群內(nèi)基本等效的一部分,實際上也具備這樣的能力。

這就導致了幾個與Kubernetes不一樣的地方。首先,工作節(jié)點應(yīng)該設(shè)計得比當前更加獨立,對控制節(jié)點的依賴更少??梢栽跊]有控制節(jié)點的情況下長時間(極端情況下是幾周)運行,只要沒有機器故障,導致需要Pod重新調(diào)度。

我認為主要的轉(zhuǎn)變是將更多的數(shù)據(jù)同步節(jié)點上,并存到持久化存儲中。這樣節(jié)點自身就有了恢復正常運行所需要的數(shù)據(jù),和主節(jié)點失聯(lián)后也能從冷啟動中恢復到可響應(yīng)狀態(tài)。理論上,我希望集群編排系統(tǒng)在每個節(jié)點上填充一組systemd單元,在節(jié)點的運行過程中扮演一個被動管理的角色。它在本地擁有它需要的一切,除非這些東西需要改變,否則節(jié)點是獨立于管理節(jié)點的。

這確實導致了如何處理節(jié)點失聯(lián)的問題。在“中心化”的集群中,這是觸發(fā)工作負載重新調(diào)度的關(guān)鍵信號,但在去中心化的情況下,我更有可能會說“別擔心,這可能是短暫的失聯(lián),節(jié)點很快就會回來”。所以,節(jié)點生命周期以及它與Pod生命周期的關(guān)系將不得不改變。也許你必須顯式聲明你是否希望Pod是“高可用”(即當節(jié)點失聯(lián)時主動地重新調(diào)度)或 “盡力”(只有當系統(tǒng)確定一個節(jié)點已經(jīng)掛了并無法恢復時才重新調(diào)度)。

一種說法是,在我設(shè)計的“去中心化”集群中,Pod的表現(xiàn)更像是“獨一無二的寵物”而不是“牧場里的羊群”。我會考慮設(shè)計類似無狀態(tài)應(yīng)用水平擴展的機制,但與Kubernetes不同,在這個場景下,當應(yīng)用副本數(shù)縮小到一個時,我可以干預這一個應(yīng)用運行在哪個節(jié)點上。這是Kubernetes不鼓勵的做法,所以此處我們不得不背離Kubernetes的某些做法。

另一種觀點是,將集群聯(lián)邦視為一級對象。實際上可以把分散的機器看成是單獨的集群,各自有自己的控制平面,然后將它們整合作為一個超大型集群來使用。這當然可以,并且回答了一些關(guān)于如何將節(jié)點與控制平面解耦的問題(我個人的答案:不要嘗試這么做,應(yīng)當將控制平面的功能盡可能地下放到數(shù)量龐大的工作節(jié)點)。在這種情況下,我希望控制平面是極其精簡的,否則在Kubernetes中這樣做的開銷會很大,我個人希望避免這種情況。

這也提高了網(wǎng)絡(luò)部分的難度,因為我們現(xiàn)在必須跨網(wǎng)連接。我的做法是以某種方式去和Tailscale集成,這剛好解決我們需求。也可以選擇需要一些更定制化的、組件更少的方案(不要進行多余的NAT轉(zhuǎn)換)。

納管虛擬機

注意:當我在這里說虛擬機的時候,我并不是指“用戶在Kubernetes上運行的Pod”。我指的是管理員自己創(chuàng)建的hypervisor的服務(wù)器虛擬機。類似Proxmox或ESXi創(chuàng)建出來的,但不是EC2這種托管的。

我希望我的編排系統(tǒng)能夠無縫地管理容器和虛擬機。否則,在實踐中,我將需要一個單獨的hypervisor,那樣一來我將有兩套的管理系統(tǒng)。

我不確定這究竟會成為一種怎樣的設(shè)計,只是一個粗略的想法。kubevirt提供的功能應(yīng)該內(nèi)置到系統(tǒng)中,并成為系統(tǒng)關(guān)鍵的一部分,就像容器一樣。這是一個相當龐大的問題,因為這可能意味著從“讓我運行一個帶有虛擬軟盤的系統(tǒng)”到“運行一個看起來和感覺都有點像EC2的管理程序”,這是非常不同的兩件事。我唯一確定的是,我不希望運行同時運行Proxmox和這套編排系統(tǒng),但我確實需要同時擁有虛擬機和容器。

如何存儲?

在我當前的設(shè)想中,存儲是一個巨大未知數(shù)。我缺乏充足的經(jīng)驗,沒有太多獨到的見解。我覺得CSI太復雜了,應(yīng)當精簡,但除了上面提到的,與生命周期工作流程有關(guān)的那一小部分,我也沒有好的想法可以提出來。存儲是我目前唯一個想保留目前Kubernetes插件化設(shè)計的模塊,不過一旦我對這方面的知識了解到位,我可能會有不同想法。

最后

寫到這里,文章的內(nèi)容很多,我相信我可能遺漏了一些我一開始想解決的問題或是一些古怪的想法。但是,如果我明天就要著手替換Kubernetes,上面列的幾點應(yīng)該是我一定要改的地方。

我沒有過多提及這個行業(yè)內(nèi)的其他玩家——Hashicorp的Nomad,F(xiàn)acebook的Twine,Google的Borg和Omega,Twitter的Mesos。除了Borg之外,我還沒有實踐過其它方案,無法對其有深刻見解。如果要著手開發(fā)一個全新的Kubernetes,我一定先投入更多的時間去了解清楚這些競品,這樣我就可以取其精華,去其糟粕。我也會對Nix進行深入的思考,好好想想如何把它糅合到我的設(shè)計中。

老實說,我可能也只是想想而已,什么也沒實踐。我從Borg上學到了很多關(guān)于云計算理念的精髓,而Kubernetes也促使我進行了反思。我目前依舊相信,最好的容器編排系統(tǒng)就是沒有容器編排系統(tǒng),而這種努力將不惜一切代價避免Kubernetes各種坑。顯然,這個想法與構(gòu)建容器編排系統(tǒng)是格格不入的。

責任編輯:未麗燕 來源: Dockone.io
相關(guān)推薦

2012-03-14 21:27:52

PayPal

2012-03-20 21:05:53

Windows Pho

2021-12-09 09:09:41

蘋果開源主頁開源項目

2025-02-11 09:51:52

2009-09-04 13:49:55

Android Mar

2013-08-30 10:55:31

網(wǎng)站設(shè)計

2012-01-09 09:08:07

微軟window phon蘋果

2012-03-20 21:17:22

移動

2009-07-23 09:17:13

App Store軟件

2019-04-15 15:08:06

多云網(wǎng)絡(luò)網(wǎng)絡(luò)架構(gòu)混合云

2012-03-20 15:34:57

搜索引擎移動互聯(lián)網(wǎng)

2022-03-02 09:46:29

Mozilla開發(fā)者網(wǎng)站MDN

2013-11-25 09:07:31

Ubuntu桌面系統(tǒng)移動系統(tǒng)

2020-12-10 11:12:31

Teams微軟Zoom

2011-10-21 09:09:10

GmailGoogle

2012-02-21 09:25:49

蘋果iTunesApp Store

2018-01-30 10:17:05

AI芯片處理器

2022-01-07 07:47:53

Windows 11照片應(yīng)用更新

2021-07-26 18:20:04

微軟Windows 11Windows

2022-02-08 08:08:27

Windows 11Windows 10macOS
點贊
收藏

51CTO技術(shù)棧公眾號