略談分佈式系統中的容器設計模式

本文做者:zytan_cocoajava

略談分佈式系統中的容器設計模式git

譚中意github

2020/3/5web

前言:雲原生(Cloud Native)不只僅是趨勢,更是如今進行時,它是構建現代的,可彈性伸縮的,快速迭代的計算網絡服務的事實標準。其中容器編排系統Kubernetes和容器是基石。因此每一個工程師都須要學習和了解他們。學習過程當中,不少工程師可能會問:爲何Pod而不是容器是K8S部署的最小單位基於K8S設計分佈式系統有沒有什麼套路?本文針對這些問題,並參考K8S創始人的不少文檔,給出瞭解答。本文適合進行研發工做2到3年的同窗,對架構設計比較感興趣,有必定架構設計意識,同時對容器(Docker)和容器編排系統(kubernetes)有必定了解。但願能夠經過此文,讓同窗們更深刻的瞭解到分佈式容器系統中的幾種常見模式,以便之後更好的設計和實現雲原生的分佈式系統。算法

 

先從一篇論文提及docker

首先介紹一篇論文,標題是《Design patterns for container-based distributed systems》,做者是Brendan Burns和David Oppenheimer,論文發表於2016年,是原原生領域系統設計的表明做。編程

第一做者Brenda Burns,相信熟悉雲原生領域的同窗都認識他,他以前是Google的工程師,是Kubernetes的三位創始人之一,他在這項目中負責系統設計,不少關鍵的設計決策都是出自他之手,包括聲明式,Pod等。設計模式

在論文中,他介紹了基於容器的分佈式系統中出現的幾種設計模式:單容器模式,單節點多容器模式,以及多節點模式。像以前的面對對象模式同樣,這些分佈式系統中的pattern能夠簡化開發,並使使用他們的系統更加可靠。他感言,在google作了不少年的分佈式系統設計,從web search作到後來的cloud系統,基本都是從頭作起,很費力,由於沒有什麼成熟可重用的模塊;可是進入雲原生時代,隨着容器(docker)和容器編排(k8s)的出現,極大的改變了分佈式系統的設計和開發,從而爲大規模的重用提供了巨大的可能。網絡

系統設計是隨着計算機體系的發展而發展的,計算機系統從單機發展到Client/Server再到Distributed System,而系統設計也從單一的算法Algorithm(例如Quick sort)發展到OOP(面對對象的編程語言),再發展到如今容器系統。架構

Design Pattern是a repeatable solution to a problem,即對軟件設計中廣泛存在(反覆出現)的各類問題所提出的解決方案,相似圍棋中的定式(圍棋愛好者應該很熟悉)。在GANG of Four的名著《Design Patters》中,對用面對對象語言實現的,針對編程語言中Interface級別的各類Design Pattern作了很深刻的解釋。其中一些設計原則,例如Single Responsibility對後續的系統設計也是很是適用。

爲何咱們須要使用Design Pattern?由於絕大多數咱們在系統開發中碰到的問題是類似的,能夠借鑑別人的經驗,因此站在巨人的肩上做創新;其次Design Pattern給咱們提供了共享的概念和詞彙,很容易溝通,例如咱們一提及factory,咱們就知道這是一個用來建立其餘對象的對象;還有咱們能夠build可重用的組件。Design Pattern的核心:解耦(Decouple)和重用(Reuse)。這兩個核心原則貫穿系統設計的始終,無論咱們是基於一種編程語言,使用接口來實現,仍是在分佈式系統設計中。

分佈式系統中以前不多有pattern,mapreduce算是一個,可是在容器和容器編排成爲主流後,有了大量的可重用的組件,也總結出了大量的pattern,單容器,單節點多容器,多節點等。限於篇幅,本文不介紹多節點容器設計模式,感興趣的同窗能夠參考書後的資料。

 

先說第一個pattern,Single Container Patten.

先從最小的組件容器開始提及,容器簡單來講,就是把應用程序和它所須要的依賴庫打成一個包。容器(Docker)的出現,極大改變了程序打包和部署的方式,更是完全改變分佈式系統設計的最基礎組件。

Container就比如OOP Java編程語言中的Object(Class),是容器分佈式系統的最基礎對象。有人作過一個簡單的類比,在java語言中,最基本的對象載體是Class,class被運行起來就是Object,而在容器系統中,最基本的對象載體是Container Image,當被運行起來後就是Container。Java對象有本身的初始化機制,Container中也有本身的Init Container機制。Java對象有銷燬機制,Container也有preStop函數來優雅的中止。

在現實的設計中,須要把一個應用拆爲多個容器來實現,這麼作的理由有三個:1. 針對資源創建邊界(不一樣的容器須要不一樣的CPU和內存,根據實際須要進行限制,並且不一樣容器間資源隔離,互不影響。2.創建團隊歸屬邊界,即一個Container有一個敏捷團隊來own,最好6到8人。3.提供興趣隔離(separation of concern)。容器的做用和職責應該知足Single Responsibility的原則,按照Domain Model Design的原則來進行設計,這樣容易理解,也容易測試、更新和部署。那麼在設計用於生產環境的容器的時候,須要設置內存和CPU的最高和最低限制,同時須要設置Liveness Probe和Readiness Probe。

在設計的時候,須要重點考慮:解耦和重用。不斷的問本身幾個問題:個人容器足夠解耦了嗎?是否還能夠拿出部分來做爲獨立的容器?這些容器是否能夠很方便的被重用到其餘地方?

再說多個容器組合成的pattern。

同窗們在學習K8S的過程當中,確定會有疑問:爲何Pod(含有一個或者多個Container)是最小的部署單元,而不能直接是容器。這裏就要涉及到K8S中一個很是精巧的設計了。Pod是一組共享生命週期,並部署在同一個節點的容器的組合,他們能夠經過共享的volume/network和IPC來進行通信。之因此不是一個單一容器,而是多個容器來完成特定功能的緣由在於:這些容器要完成的職責不一樣,根據單一職責(single responsibility)的原則,他們應該屬於不一樣的組件;其次由於職責不一樣,維護他們的team也不一樣,迭代週期也不同;最後其中一些容器是能夠被複用在其餘的環境中的。因此從「解耦」和「複用」的設計原則出發,Kubernetes經過增長一個虛擬層即POD,給系統設計帶來了極大的靈活性,同時也產生了多種設計模式。即在一個POD中除了抗流量完成業務的容器(文中稱之爲app container)外,還存在其餘的輔助容器,能夠分爲兩類:1. Init Container 2. Sidecar container。

 

Init Container Pattern

就想Java語言中Object有初始化函數同樣,Pod中的Init Container起的做用也是用來初始化。當app container須要知足一些前置條件才能啓動,例如它依賴一些外部服務db service ready才能啓動,或者須要初始化的更新文件(例如從github clone最新版本的文件)。

咱們來看一個簡單的例子,如上圖。其中myapp-container是app container,它是來執行業務邏輯的,它的啓動依賴後臺的mydb和myservice兩個服務。因此有兩個init containers,他們分別執行nslookup來檢查依賴服務是否已經啓動,若是沒有啓動,等待2秒以後再檢查,若是已經啓動,則順利啓動自身容器,而後app container再啓動。能看出這個例子中,app container利用init container來force wait,直到依賴的兩個後臺服務啓動以後再啓動。這樣app container的啓動邏輯就無需關心這兩個依賴服務是否ready。

使用Init Container的注意事項以下:1. Init container的執行順序是在pod啓動過程當中,最早執行,並且是順序執行,即一個init container執行結束後,再執行下一個init containers。他沒有readiness probe check,應該是邏輯簡單,並且執行快速的。2. Init Container也是container,它也佔CPU/內存系統資源,因此在計算資源消耗的時候,須要把它也加進去,否則調度的時候可能會有問題。

 

Sidecar pattern

下面介紹Sidecar pattern。

所謂sidecar,就是相似這種摩托車。在K8S中,sidecar container是和app container同時啓動,而且有本身的職責,並能在別的地方進行復用的容器。Sidecar container和app container之間共享磁盤/網絡/IPC等,咱們來看一個典型的例子。

App Container是一個web server,sidecar container按期從github sync代碼下來,二者經過Pod的volume來共享文件。這樣作的好處是把從github按期sync代碼的邏輯剝離出來,成爲一個能夠重用的模塊,而且能用到其餘的場合。而app container只須要單純的作web服務就好,不須要考慮sync之類的邏輯。

何時考慮使用sidecar呢? 當這兩個container須要同時部署,可是各有本身的職責,並且能夠分別去迭代和演進,並且有重用的可能性。

那麼何時不適合sidecar呢?當這兩個container有不一樣的擴容需求時候,即二者須要獨立的擴容時候,不要sidecar這種模式;另外,二者的通訊可能會帶來一些網絡的消耗,帶來必定的延遲,若是這點延遲是業務沒法接受的話,也不要使用sidecar。

 

Ambassadaor Design Pattern

它是一種特殊的sidecar,其實就是app container的一個proxy,它來接流量,而後進行處理,而後把流量轉發給app container,讓其完成真正的商業邏輯。

 

此外,ambassador pattern還可用於shard a service或者A/B 測試。另外最近新崛起的技術熱點ServiceMesh和其實現istio,都是利用sidecar的方式來作服務代理,把流量控制的邏輯下沉到基礎架構層,經過ambassador的方式很方便的實現。他們的出現將極大的改變微服務架構,可能讓其更關注業務邏輯的高效實現,而把流量控制(包括服務發現,服務限流,小流量等服務路由功能統一交給service mesh來解決。

 

Adapter Pattern

又是一種特殊的sidecar,若是但願對外輸出的內容符合下游的要求而不對app container進行修改,能夠增長一個adapter的sidecar,由它來作相似日誌轉換的事情。例如:

App container按照自身的要求生成日誌並保存到文件系統中,另外的adapter container經過共享存儲讀取該日誌,而後進行日誌轉換等工做,以便把內容輸出給下游的metrics系統。

總結:

設計模式(Design Pattern)的核心是解耦和重用;在容器和容器編排的分佈式系統中,有大量可重用的組件和pattern,其中Single container和multiple container又是最基本的組件。

 

參考資料:

 

原文連接地址:https://developer.baidu.com/topic/show/290687

相關文章
相關標籤/搜索