[譯] 基於容器的分布式系統設計模式
名稱:Design patterns for container-based distributed systems
作者:Brendan Burns, David Oppenheimer, Google
發布:HotCloud'16: Proceedings of the 8th USENIX Conference on Hot Topics in Cloud Computing, June 2016
譯者:sangmado
1 簡介
在20世紀80年代末期和90年代早期,面向對象編程徹底改變了軟件的開發方式,使用模塊化的組件進行應用程序構建變得更為普遍。時至今日,我們看到分布式系統開發也在進行着類似的變革,基於容器化軟件組件構建的微服務架構正在變得越來越流行。容器,依賴其在容器化領域內創立的多重優點,已經成為分布式系統構建的基礎"對象"。隨着架構風格的成熟,我們也看到了設計模式的涌現,就像我們在面向對象編程時一樣,出於對細粒度代碼細節的封裝抽象,最終揭示了在各種應用程序和算法中相通的更高級的設計模式。
這篇論文描述了我們在基於容器構建的分布式系統中識別出的三種類型的設計模式:用於容器管理的單容器模式,多容器緊密協作的單節點模式,用於分布式算法的多節點模式。就像面向對象設計模式一樣,這些為分布式計算而生的設計模式引入了最佳實踐,簡化了代碼開發,並提升了使用它們的系統的可靠性。
2 分布式系統設計模式
設計模式的涌現和文檔化,是在面向對象編程被使用多年之后才發生的。這些設計模式將一些常規編程問題的解決方法進行了代碼化和規范化,而這些編碼規約更進一步地改善了行業的編程水平,因為它使得經驗不足的程序員也可以輸出高質量的代碼。同時,可復用的類庫機制也使得代碼越來越可靠,開發速度也比以往變得更快。
現如今,分布式系統軟件工程的最先進技術,看起來更像是20世紀80年代早期編程的模樣,而不像是后期的面向對象軟件開發。這其中的一個成功案例就是 MapReduce 模式。MapReduce 模式將大數據編程的力量引入至廣闊的應用領域和開發者群體,將正確的模式應用在合適的位置,極大地改進了分布式系統的質量、速度和可訪問能力。盡管如此,MapReduce 的成功仍然受到了單一編程語言的限制,截至目前 Apache Hadoop 生態系統仍主要是由 Java 語言編寫。所以,為分布式系統設計開發一套真正全面的模式,需要一套非常通用的、與語言無關的工具來表述系統中的元素。
萬幸的是,最近兩年 Linux 容器技術興起並取得了廣泛的采用。容器與容器鏡像完美的滿足了開發分布式系統中模式的抽象需求。迄今為止,容器和容器鏡像已被廣泛的驗證,成為從開發至生產過程中交付軟件的更好更可靠的方法。依靠緊密的封裝,依賴自治,原子部署與成敗標記等功能,其顯著地改進了老式的雲與數據中心應用部署方式。然而,除了在部署工具方面表現出色,實際上容器還有巨大的潛力,我們相信它注定可以成為類似對象在面向對象編程中的地位,並將推進分布式系統設計模式的發展。在接下來的章節中,將解釋為什么我們相信這是必然的,並描述在未來的若干年中,已涌現出並常規化指導分布式系統軟件工程設計的設計模式。
3 單容器管理模式
容器為接口定義提供了天然的邊界,就像對象的邊界一樣。容器不僅可以暴露應用級別的功能,也可以通過接口回調為管理系統服務。
傳統的容器管理接口都是極其有限的,一個容器一般僅暴露三個原語:run(), pause(), stop()。盡管這些接口用途很大,但更豐富的接口可以賦予系統開發和運維人員更多的管理功能。同時,目前幾乎每種現代編程語言均支持基於 HTTP 的 Web 服務,而且支持類似 JSON 等的通用數據格式,通過使用容器托管 Web 服務器並指定端點,不僅可以暴露主要功能,更使得定義基於 HTTP 管理的 API 成為可能,
容器可以針對北向暴露非常豐富的應用程序信息,比如應用特定的監控指標,開發者關注的分析信息,組件配置信息和服務日志等。具體的實現中,Kubernetes、Aurora、Marathon 等容器管理系統均允許用戶定義通過特定的端點來進行健康探測。
對於南向接口,容器提供一種機制來定義一個生命周期,使得編寫被管理系統管控的軟件組件更加方便。例如,一個集群管理系統可以為任務分配優先級,高優先級任務即使在集群超賣的條件下也可以被有保障的執行。這種保障是通過剔除正在運行的低優先級任務進行強制實施的,而那些低優先級任務則可在集群資源可用時再被執行。剔除機制可以簡單粗暴到直接殺死低優先級任務,但這種方式給開發者帶來了額外的負擔,他們不得不考慮代碼在任意時刻都能被殺死的場景。取而代之的,若是在應用程序和管理系統之間構建一個生命周期機制,開發者通過依賴和遵循契約,不僅系統開發變得更簡單,同時也可以讓應用程序變得更具管理性。例如,Kubernetes 使用 Docker 的 "優雅刪除" 功能來對容器進行提前預警,通過發送 SIGTERM 信號給即將被終結的容器,在應用程序設定的時間窗口之后再發送 SIGKILL 信號。這樣則可讓應用程序在被終結前完成正在執行的操作,將業務狀態寫入磁盤等。想象下,我們可以提供一種機制來支持狀態的序列化和狀態恢復,以便基於狀態的分布式系統對狀態的管理更優雅。
再舉個復雜生命周期的例子,比如 Android 系統中的 Activity 模型,它支持一系列的回調接口和一個用於定義系統該如何觸發這些回調的狀態機。如果沒有這些生命周期定義,開發魯棒的、可靠的 Android 應用程序將變得難上加難。而在基於容器的系統上下文中,通常會定義一系列的回調鈎子,以便在容器創建、啟動、終止時進行調用。另一個關於南向接口 API 的例子是容器對 "自我復制" 機制的支持,以便對服務進行擴展。
4 單節點、多容器應用模式
除了單個容器的接口之外,我們還可以看到跨容器設計模式的涌現。之前我們已經確定了幾個這樣的模式。這些單節點模式由共生容器組成,它們被共同調度到同一台主機上。容器管理系統支持將多個容器作為一個原子單元進行協同調度,Kubernetes 將此抽象為 "Pods",而 Nomad 則將其抽象為 "task groups",這也是本節中描述的模式所必需的特性。
4.1 邊車模式
邊車模式是首個也是最常見的多容器部署模式,邊車擴展並增強了主容器。例如,主容器可能是一個 Web 服務器,它可以與一個能夠從本地磁盤收集 Web 服務器的日志 "logsaver" 邊車容器配合,並將日志發送至存儲系統集群。圖1展示了邊車模式的一個示例。另一個常見的示例是一個基於 Web 的本地磁盤內容服務器,這些內容由邊車容器填充,邊車容器定期對 Git 倉庫、內容管理系統或其他數據源的內容進行定期同步。這兩個例子在 Google 內部應用中都很常見。在同一機器上的容器可以共享同一個本地磁盤卷,也使得邊車機制成為可能。
當然,盡管也可以將邊車容器的功能構建到主容器中,但是使用獨立容器有幾個好處。
- 首先,容器是資源計算和分配的單元,因此例如可以對一個 Web 服務器容器的 cgroup 進行配置,以便它對查詢提供一致的低延遲響應,而 logsaver 容器則可配置為當 Web 服務器不繁忙時再執行。
- 其次,容器是打包的單元,因此將業務服務和日志存儲分離到不同的容器中,可以很容易地將它們的開發職責划分給兩個獨立的開發團隊,並允許對它們進行獨立測試,當然也可以一起測試。
- 第三,容器是可復用的單元,因此邊車容器可以與許多不同的主容器進行搭配(例如,一個日志存儲容器可以與產生日志的任何組件一起使用)。
- 第四,容器提供了一個失敗管控邊界,使整個系統能夠正常降級(例如,即使日志存儲程序發生故障,Web 服務器仍然可以繼續提供服務)。
- 最后,容器是部署單元,它允許對每個功能塊進行升級,並在必要時獨立地回滾。(當然也需要指出,這個好處也有一個缺點,即整個系統的測試矩陣必須考慮所有可能在生產中遇到的容器版本組合,這可能是巨大的,因為一組容器通常不能被原子性的整體升級。當然,雖然單體應用程序沒有這個問題,但組件化系統在某些方面更加容易測試,因為它們是由可以獨立測試的較小單元構建的。)
請注意,以上五個優點適用於本文后續部分中描述的所有容器模式。
4.2 大使模式
我們觀察到的下一個模式是大使模式。大使容器代理了與主容器之間的通信。例如,開發人員可能將使用 memcache 協議的應用程序與 twemproxy 大使進行搭配。應用程序認為它只是與本地主機上的單個 memcache 進行交互,但實際上 twemproxy 則將分片請求轉發至在其他地方安裝的分布式 memcache 集群節點上。
這個容器模式在三方面對程序員的開發過程進行了簡化:他們只需要思考和編寫應用程序連接到一個在本地主機上的服務器,他們可以通過在本地機器上運行一個真正的 memcache 實例而非大使來對應用程序進行獨立測試,他們可以在其他應用程序中復用 twemproxy 大使,甚至可以是不同的編程語言。因為同一機器上的容器共享相同的本地主機網絡接口,方使得大使模式成為可能。圖2展示了此模式的一個示例。
4.3 適配器模式
我們觀察到的最后一個單節點模式是適配器模式。與簡化外部世界呈現視圖的大使模式相反,適配器模式則是通過簡化的、同質化的外部世界視圖來進行呈現。其通過標准化輸出和跨多個容器的接口來實現這一點。適配器模式的一個典型案例,就是通過適配器模式來確保系統中的所有容器都具有相同的監控接口。現今的應用程序使用各種各樣的方法來輸出它們的指標(例如 JMX、statsd 等)。但是,如果所有應用程序都提供一致的監控接口,則使用一個監控工具就可以更容易的收集、聚合和展示來自異構應用程序的指標。在 Google,我們通過代碼規約實現了這一點,但這只有在您從頭構建軟件時才有可能實現。那些遺留的應用程序和開源應用程序,可以通過適配器模式提供統一的接口,而不需要修改原始應用程序。主容器可以通過本地主機或共享的本地卷與適配器進行通信。如圖3所示。請注意,盡管一些現有的監控解決方案也能夠與多種類型的后端進行通信,那是因為它們在監控系統本身中使用特定於應用程序的代碼,這顯然不太符合關注點分離模式。
5 多節點應用模式
除了在一台機器上互相協作的容器外,模塊化的容器使得構建協調的多節點分布式應用程序變得更加容易。接下來,我們將描述其中的三種分布式系統模式。與前一節中的模式類似,這些模式也需要系統對 Pod 抽象的支持。
5.1 領導者選舉模式
分布式系統中最常見的問題之一就是領導者選舉。通常實例副本用於在一個組件的多個相同實例之間共享負載,但應用程序副本的另一個更復雜的使用場景是需要將一個 "領導者" 副本與其他副本區分開來。如果當前領導者副本故障了,則其他的副本需要迅速的進行取代。系統甚至可以並行地進行領導者選舉,例如,多個分片均需要確定領導者。
有許多類庫可以進行領導者選舉,它們通常很復雜,也難以正確理解和使用。此外,它們還受到使用特定編程語言實現帶來的限制。一種替代的方案就是將領導者選舉機制從應用程序中剝離至領導者選舉專屬容器中。提供一組領導者選舉容器,它們中的每個容器都與需要進行領導者選舉的應用程序共同調度,則可在這些領導者選舉容器之間執行選舉。同時,它們可以在 localhost 上為需要進行領導者選舉的應用程序容器提供一個簡化的 HTTP API (例如,becomeLeader、renewLeadership 等)。這些領導者選舉容器可以由這個復雜領域的專家進行構建,然后不管應用程序開發人員選擇何種編程語言,都可以復用其簡化的接口。這種方式代表了軟件工程中最好的抽象和封裝過程。
5.2 工作隊列模式
盡管工作隊列模式,也包括領導者選舉模式,已經是被研究得很深入的課題,並且有許多框架也實現了它們,但這些分布式系統設計模式仍然是可以在面向容器的系統架構中獲益。在以往的系統中,應用程序框架限制了項目的單一開發語言環境(如 Python 中的 Celery),或者任務分發和二進制執行已與實現者脫離(如 Condor)。
容器針對 run() 和 mount() 接口的實現,使得實現一個通用的工作隊列框架變得簡單直接,可以將任意的處理代碼打包成一個容器,與任意數據,構建成一個完整的工作隊列系統。
開發人員只需要構建一個容器,可以從文件系統接收一個數據文件,並將其轉換為一個輸出文件;這個容器還可以作為工作隊列的一個階段來使用。所有其他的涉及開發一個完整的工作隊列的工作均可由通用工作隊列進行處理,並可在任意時刻進行復用。用戶代碼集成到這個工作隊列共享框架的方式如圖4所示。
5.3 分散/收集模式
最后一個我們要着重介紹的分布式系統涉及模式是分散/收集模式。給定一個系統,一個外部客戶端向"根"或"父"節點發送一個初始請求,這個"根"節點將請求分發給大量服務器以執行並行計算,每個服務器分片均返回部分數據,然后"根"節點再將這些數據收集到原始請求的單個響應中。這種設計模式在搜索引擎中很常見。開發這樣一個分布式系統需要大量的樣板代碼:分散請求、收集響應、與客戶端交互等,而這些代碼在面向對象編程中是完全通用的,重復的。通過容器化的方案來進行重構,當客戶端發起請求至"根"容器時,按照開發人員的設計,請求被扇出至葉子容器集群中,開發人員再指定一個容器負責結果集的收集和聚合。
具體的說,實現分散/收集系統,用戶需要提供兩類容器。首先,一類容器實現葉子節點計算,這個容器執行部分計算並返回相應的結果。第二種容器是合並容器,這個容器需要匯總所有葉子容器的計算結果,並組織成一個單一的響應后輸出。很容易看出用戶可以實現任意深度的分散/收集系統,只需提供已實現這些相對簡單的接口的容器即可。這樣一個系統如圖5所示。
6 相關工作
早期的面向服務的 SOA 架構與基於容器的分布式系統架構有許多相通的特征。例如,兩者都強調可重用的組件需要通過定義良好的接口基於網絡進行通信。另一方面,與我們所描述的多容器模式相比,SOA 系統中的組件往往粒度更大,耦合更松散。此外,SOA 中的組件通常實現具體的業務,而我們在這里所關注的組件更傾向於通過通用類庫來簡化分布式系統的構建。近期涌現出的 "微服務" 一詞,也可以描述我們在本文中討論的組件類型。
網絡組件的標准化管理接口概念至少可以追溯到 SNMP 時代,而 SNMP 主要關注於硬件組件的管理,還沒有出現負責管理基於微服務或容器的系統的標准。但這並沒有阻礙多種多樣的容器管理系統的發展,比如 Aurora、ECS、Docker Swarm、Kubernetes、Marathon 和 Nomad 等。
我們在第5節中提到的所有分布式系統設計模式都有很長的歷史。你可以在 Github 上找到許多領導者選舉模式的實現,盡管它們看起來像是類庫而不是獨立的組件。也有許多流行的工作隊列實現,包括 Celery 和 Amazon SQS。分散/收集模式已被識別為一種企業級系統集成模式。
7 結論
正如面向對象編程引出了面向對象"設計模式"和代碼范式,我們也看到基於容器的架構也引出了基於容器的分布式系統設計模式。在本文中,我們識別了三種類型的模式:用於系統管理的單容器模式、用於容器間緊密協作的單節點模式和用於分布式算法的多節點模式。在這些模式中,容器提供了許多與面向對象系統中的"對象"相同的益處,例如可以方便地在多個團隊之間划分應用實現,在新的上下文中復用組件等。此外,它們還為分布式系統提供了一些獨特的好處,比如可以獨立地升級組件,可以用多種語言編寫組件,還可以讓整個系統優雅地降級。我們相信,容器模式將會持續不斷的擴充,在未來的幾年里,就像面向對象編程在幾十年前所做的那樣,通過對分布式系統開發的標准化和規范化,它們將帶來分布式系統編程的革命性變化。
8 鳴謝
想法不會憑空出現在我們的腦海里,本文的工作受到了 Brian Grant、Tim Hockin、Joe Beda 和 Craig McLuckie 深深地影響。
參考文獻
- [1] Docker Engine http://www.docker.com
- [2] rkt: a security-minded standards-based container engine https://coreos.com/rkt/
- [3] Erich Gamma, John Vlissides, Ralph Johnson, Richard Helm, Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, Massachusetts, 1994.
- [4] Jeffrey Dean, Sanjay Ghemawat, MapReduce: Simplified Data Processing on Large Clusters, Sixth Symposium on Operating System Design and Implementation, San Francisco, CA 2004.
- [5] Apache Hadoop, http://hadoop.apache.org
- [6] Kubernetes, http://kubernetes.io
- [7] Apache Aurora, https://aurora.apache.org.
- [8] Marathon: A cluster-wide init and control system for services, https://mesosphere.github.io/marathon/
- [9] Managing the Activity Lifecycle, http://developer.android.com/training/basics/activitylifecycle/index.html
- [10] Brendan Burns, The Distributed System ToolKit: Patterns for Composite Containers, http://blog.kubernetes.io/2015/06/the-distributedsystem-toolkit-patterns.html
- [11] Nomad by Hashicorp, https://www.nomadproject.io/
- [12] Gregor Hohpe, Enterprise Integration Patterns, Addison-Wesley, Massachusetts, 2004.
- [13] Celery: Distributed Task Queue, http://www.celeryproject.org/
- [14] Amazon Simple Queue Service, https://aws.amazon.com/sqs/
- [15] https://www.kernel.org/doc/Documentation/cgroupv1/cgroups.txt
- [16] Service Oriented Architecture, https://en.wikipedia.org/wiki/Serviceorientedarchitecture
- [17] Amazon EC2 Container Service, https://aws.amazon.com/ecs/
- [18] Docker Swarm https://docker.com/swarm
- [19] J. Case, M. Fedor, M. Schoffstall, J. Davin, A Simple Network Management Protocol (SNMP), https://www.ietf.org/rfc/rfc1157.txt, 1990.
- [20] R. G. Gallager, P. A. Humblet, P. M. Spira, A distributed algorithm for minimum-weight spanning trees, ACM Transactions on Programming Languages and Systems, January, 1983.
- [21] M.J. Litzkow, M. Livny, M. W. Mutka, Condor: a hunter of idle workstations, IEEE Distributed Computing Systems, 1988.
- [22] https://linuxcontainers.org/
原文地址:Design patterns for container-based distributed systems
聲明:本篇文章《[譯] Design patterns for container-based distributed systems》為翻譯文章,由譯者發布自博客園個人技術博客,未經作者本人同意禁止以任何的形式轉載,任何自動的或人為的爬蟲轉載行為均為耍流氓。