在20世紀80年代末和20世紀90年代初,面向對象編程完全改革了軟件開發方法,普及了將應用程序做爲模塊化組件的建立方法。隨着基於容器化軟件組件的微服務架構的逐漸普及,如今在分佈式系統開發中也發生着相似的革命。容器之間相互隔離的優勢使得容器成爲了分佈式系統中合適的基本單元。隨着這種架構類型的成熟,咱們能夠看到設計模型的出現,從容器的角度來思考問題能夠將低層次代碼細節抽象化。java
這篇論文描述了咱們觀察到的三種基於容器分佈式系統的設計模型:單個容器模式,密切合做模式容器的單節點模式,以及分佈式算法的多節點模式。跟面向對象編程的設計模式相似,這些模式包含了最佳實踐,簡化了開發,使系統更加可靠。git
在面向對象編程被使用了幾年以後,設計模式出現並被編成文檔。這些模式被編纂好,而且被規整成解決特別常見的編程問題的方法。這個編纂進一步提升了編程最前沿的技術,由於它可讓經驗不那麼豐富的人也寫出很好的代碼,而且讓重複使用的庫文件的蓬勃發展,這些庫文件可讓開發代碼更加可靠、快速。算法
前分佈式系統工程的技術水準,比起面向對象開發,更像是20世紀80年代的編程的水平。顯然,MapReduce模式將「大數據」編程的力量帶入普遍的領域和更多的開發者是一個巨大的成功,將正確的模式放在合適的位置能夠很大程度上提升分佈式系統編程的質量、速度和可達性。雖然MapReduce的成功受限於單個編程語言,在Apache Hadoop生態系統範圍內,只對一種編程語言(java)產生了影響。爲分佈式系統開發一款全面的一套模式須要一個很是通用,與語言無關的交流工具來呈現系統的精髓。編程
因此,很幸運可以看到過去兩年內,愈來愈多的人選擇使用容器技術。容器和容器鏡像正是分佈式系統模式開發所須要的抽象。至今爲止,容器和容器鏡像已經在很大程度上受到了歡迎,由於它是一種更好,更可靠的經過產品來交付軟件的方法。由於容器是密閉的,包含了依賴關係,而且有一個原子的部署信號("succeeded」/「failed」),他們很大程度上提高了以前在數據中心或雲端部署軟件的技術。可是容器不只僅只是一個很好的部署工具,咱們認爲容器最終會成爲面向對象軟件系統中的對象同樣,而且所以驅使了分佈式系統設計模式的發展。在接下來的部分,咱們會解釋爲何咱們相信會是這樣,而且描述出現的一些在將來幾年中可使分佈式系統規整化的設計模式。json
容器爲定義接口提供了一個天然的邊界,相似於對象邊界。容器不只可以暴露專用應用功能,還可以經過這個接口跟管理系統掛鉤。後端
傳統的容器管理接口是極其有限的。對容器的有效操做爲:run,pause和stop。雖然這個接口十分有用,可是更豐富的接口能夠給系統開發者和操做者提供更多的實用功能。考慮到目前幾乎全部現代編程語言對HTTP服務器的開發以及對json這種數據格式的廣泛支持,很容易讓容器提供一個基於HTTP管理的API。設計模式
「upward」方向,容器能夠暴露一套豐富的應用程序信息,包括應用專用監控參數(QPS,應用程序健康等等),用戶感興趣的信息(threads,stack,lock contention,network,message statistics等等),組件配置信息和組件日誌。一個具體例子就是,Kubernetes,Aurora,Marathon和其它容器管理系統容許用戶經過特定的HTTP端點(好比,」/health」)來定義健康檢查。對於以前說過的「upword」API的其它元素的標準化支持就更少了。服務器
「downward」方向,容器接口提供一個地方來定義生命週期,使得寫管理系統控制的軟件組件更加容易。好比,集羣管理系統一般會將「priorities」歸到任務中,即便集羣訂閱超額,高優先級的任務也會保證運行。這個保證經過殺死已經在運行的低優先級的任務來完成,低優先級的任務要等到資源可用的時候纔會再繼續完成。驅逐只要經過簡單的殺死低優先級的任務就能夠實施,可是這就給開發人員帶來了不少沒必要要的負擔,他們須要在代碼中處理任務被殺掉的狀況。相反,若是在應用系統和管理系統中定義了正式的生命週期,應用程序組件就會變得更加可管理,由於開發者能夠依照正式的生命週期來開發。好比,Kubernetes使用Docker的「優雅終止」功能,經過SIGTERM信號來警告容器,它在一個自定義的時長以後會接受到SIGKILL信號,並被終止。這就容許應用程序經過完成當前任務,把狀態寫入磁盤等等操做以後再終止。你能夠想象擴展這種機制用來支持序列化和恢復,使得有狀態的分佈式系統狀態管理更加容易。網絡
對於更加複雜的生命週期的例子,考慮到Android的Activity模型,這個模型包含了一系列的回調函數(好比onCreate(),onStart(),onStop(),…)和一個狀態機來定義什麼時候觸發這些回調函數。沒有了正式的生命週期,健壯,開發可靠的安卓應用程就更加難了。在基於容器的系統中,這就對應了自定義的一些鉤子,這些鉤子會在容器建立時,容器開始運行時,容器結束運行前等狀況下被調用。架構
除了單個容器的接口,咱們也看到了一些跨容器的設計模式。咱們以前就已經確認了幾個這樣的模式。這種單節點的模式包括了同時運行在單個主機上的共生容器。容器管理系統支持同時運行多個容器做爲一個總體單元,因此抽象(Kubernetes叫它「pods」,Nomad叫它「task groups」)是一個用來啓動咱們以前描述過的模式的必需功能。
1. sidcar模式
對於多個容器配置來講,第一個,也是最廣泛的狀況就是sidcar模式。Sidecar擴展而且提升了大多數的容器。好比,主容器多是一個網頁服務器,它可能跟「logsaver」 sidecar容器配對,而後saidecar容器從本地磁盤收集網頁服務器的日誌,而且將他們stream到集羣存儲系統。圖1展現的就是sidcar模式的一個例子。另外一個廣泛的例子就是從本地磁盤內容服務的網頁服務器,這個sidecar會按期跟git庫進行內容管理系統或者其它數據源的存儲進行同步。這兩個例子在谷歌是十分廣泛的。由於在同一個機器上的容器能夠共享一個本地磁盤數據卷,因此sidecar是可能作的。
雖然建立sidecar容器的功能到主容器裏永遠可行,可是使用分開的容器有如下幾點好處。首先,容器是資源帳戶和分配的單元,那麼好比一個網頁服務器容器的cgroup能夠被配置,那樣的話,它就會提供持續的低延遲反應到問題,雖然logsaver容器在網頁服務器不忙的時候被配置來清除空閒CPU週期。第二,容器是打包的單元,因此將服務和日誌保存分到不一樣的容器可讓兩個獨立的編程團隊之間的可靠性分開,而且容許他們獨立測試,跟一塊兒測試的時候是同樣的。第三,容器是重複使用的單元,因此sidecar容器能夠跟不少不一樣的主容器(好比logsaver容器能夠被任意產生日誌的組件使用)。第四,容器控制邊界錯誤服務,使得整個系統可以正確推出(好比,網頁服務器即便在日誌保存運行失敗的狀態下也可以繼續服務)。最後一點,容器是配置的單元,每一個功能均可以更新,而且必要的時候能獨立回滾。(可是要注意的是,最後一點好處也有很差的地方——整體系統的測試模型必需要考慮到在生產過程當中全部的容器版本組合,這些版本可能會很大,由於容器整體上來講一般不能自動升級。固然,單一的應用程序沒有這個問題,組件化的系統在某種程度上更容易測試,由於他們是在更小的能夠獨立測試的單元的基礎上測試的)。注意,這五點優勢應用於咱們接下來在論文中描述的容器模式。
2. Ambassador模式
接下來要說的是咱們觀察到的ambassador模式。Ambassador容器代理服務會跟主容器進行交流。好比,開發者可能匹配一個正在跟twenproxy ambassador進行交流的memcache。應用程序相信這只是跟單個在本地主機上的memcache交流,可是現實中,twenproxy正在跟多個memcache集羣中的其餘節點的分佈式安裝進行共享。這個容器模式以三種方式簡化了編程:他們只須要思考編程,依據他們鏈接到本地主機的單個服務器,他們能夠經過運行真正的memcache實例來單獨測試他們的應用程序,並且他們也能夠利用其餘的應用程序(可能會用不一樣語言編寫)從新使用twenproxy ambassador。Ambassadors也是可能的由於在同一個機器上分享同一個本地主機網絡接口。這個模式的例子如圖片2所示的。
3. 適配器模式
最後一個要說的是咱們觀察到的適配器模式。相比於用外部的簡化來呈現應用程序的ambassador模式,適配器用的事簡化的,均質的應用程序來呈現外部世界。他們是經過將多容器間輸出和接口標準化才作到這樣的。適配器模式具體的例子就是,適配器確保全部在一個系統內的容器都有相同的監控接口。如今應用程序使用不少種方法來輸出他們的參數(好比,JMX,statsd等等)。可是對於單個監控工具來講,收集,集合,以及從異構應用程序呈現數據就很容易,若是全部應用程序呈現一致的監控界面的話。在谷歌,咱們經過編碼規範來完成,可是隻有在你從scratch建立本身的軟件的時候纔有可能。適配器模式讓異構的舊世界和開源應用程序呈現統一的接口,不須要修改原始程序。主容器可以跟適配器經過本地主機或者共享的本地數據卷交流。詳情請查看圖3。注意,一些已經存在的監控方法可以跟多種類型的後端,他們在監控系統中使用專用應用程序代碼,提供了一個不那麼清潔的關注點的隔離。
在單個機器上移動合做的容器,讓建立合做的多節點分佈式應用程序更加容易。以後咱們接下來會具體描述一下這些分佈式系統模式中的三種。好比以前章節提過的那些模式,這些也要求爲Pod抽象提供系統支持。
1. leader選舉模式
分佈式系統中常見問題之一就是leader選舉。副本被廣泛使用在一個組件的多個相同的實例之間共享負載,副本的另外一個更加複雜的做用就是在應用程序須要區分副本跟設置來做爲「leader」。其它的副本對於快速取代leader的位置是十分快速的,若是以前的副本失敗了的話。一個系統甚至能夠平行運行多個leader選舉,好比,要定義多個碎片中每個的leader。運行leader選舉有不少庫。用起來又複雜又難理解,要正確使用真的很困難,另外,他們的限制之處在於,只能用特定的編程語言來寫。把leader選舉庫鏈接到應用程序的另外一種方法就是使用leader選舉容器。leader選舉容器,每個都跟須要leader選舉的應用程序的實例同步,並且可以在他們本身之間進行選舉,同時他們也能夠在本地主機上呈現一個簡化的HTTP API給每個須要leader選舉的應用程序容器(好比,becomeLeader,renewLeadership等等)。這些leader選舉容器只能被建立一次,隨後簡化的接口能夠由應用程序開發人員從新使用,無論他們選擇什麼語言來實現。在軟件工程領域,這是最好的抽象和封裝的表明。
2. work quene模式
雖然work queen跟leader選舉同樣,是一種很好研究的項目,由於有不少框架能夠來實現他們,他們同時也是分佈式系統模式例子,這個模式能夠從面向容器的架構中受益。在以前的系統中,框架限制於單個語言環境編程(好比,Celery for Python),或者work和二進制的分佈練習留給了實現它的人(好比,Condor)。實現run()和mount()接口的容器可用性使實現一個通用的work queen框架十分簡單,這個框架能夠處理任意的進程中打包爲容器的代碼,而且建立一個完整的work queen系統。開發者只能選擇去建立一個能夠在文件系統處理輸入數據文件的容器,而且將之轉化爲一個輸出文件;這個容器將會變成work queen的一個階段。用戶的代碼整合到這個共享的work queen框架的方式圖4中已經闡述了。
3. Scatter/gather模式
最後一個要強調的分佈式系統模式就是Scatter/gather模式。在這樣一個系統中,一個外部客戶發送一個初始請求到「root」或者「parent」節點。這個root將請求分散到不少不少服務器上來執行平行計算。每一個碎片返回部分數據,root將這個數據收集起來歸成單個的迴應到原始請求。這個模式在搜索引擎中是十分廣泛的。開發這樣一個分佈式系統牽扯到不少樣板文件代碼:分散請求,收集迴應,與客戶端交互等等。代碼有不少是泛化的,再次,就如同在面向對象編程中,代碼能夠用這種方法被重構,單個實現能夠被提供的方法,這個方法也能夠被用在任意容器中,只要他們實施一個特殊的接口就能夠。特別是,爲了實施一個Scatter/gather系統,用戶被要求提供兩個容器。第一,容器實施了樹結構端節點;這個容器會執行部分求值,而且返回對應結果。第二個容器就是合併容器;這個容器帶走了全部樹結構端節點的總生產額,而且將它們歸到單個迴應的組。
這樣的系統如圖5所示。
面相服務的架構體系(SOA)更新地比原來早,和基於容器的分佈式系統共享不少特徵。好比,都強調可重用的定義好的經過網絡進行通訊的接口的組件。另外一方面,SOA系統中的組件趨向於大粒度,相比於咱們以前描述過的多容器模式,有更多的鬆耦合。此外,SOA中的組件實施商務活動,咱們在這裏重點關注的組件相似於比較容易建立分佈式系統的通用庫。「微服務」這個詞語最近出來,描述的是咱們在這篇論文中討論過的組件的類型。
標準化管理接口的概念要至少要追溯到SNMP。SNMP主要關注管理硬件組件,並且如今尚未出現用來管理微服務/基於容器的系統。這仍是沒能阻止許多容器管理系統的開發,包括Aurora,ECS,Docker Swarm,Kubernetes,Marathon和Nomad。
在第5節中提到的分佈式算法都有段很長的歷史。大家能夠在Github上找到不少leader選舉實施,雖然他們結構上做爲庫而不是獨立的組件。仍是有不少受歡迎的work quene實現了的,包括Celery和Amazon SQS。Scatter/gather已經成爲一個企業的繼承模式。
若是須要轉載,請聯繫咱們哦,尊重知識產權人人有責;)
原文連接