docker,容器,編排,和基於容器的系統設計模式

都2020年了,容器,或者說docker容器這個概念,從事互聯網行業的開發者應該都不會感到陌生。不管大廠仍是小廠的應用部署如今都首選docker容器。java

可是docker雖好,卻並不是萬能。docker自己,其實僅僅是提供了一種沙盒的機制,對不一樣應用進行隔離。鏡像是它出彩的一個設計,可讓開發者們快速部署應用。但這對大型應用管理來講,是遠遠不夠的。開發者們在乎識到這個問題後,提出了編排這個概念,從而引起的新的紛爭。。。node

本篇文章從容器的歷史開始提及,而後介紹編排領域,swarm和k8s的紛爭,最後討論基於容器的系統設計模式,這個設計模式參考自google的論文,固然是基於k8s的啦~linux

PS:容器並不是只有docker,但本篇暫略去了它們的差別,大部分狀況下這兩個詞能夠等價。程序員

從容器提及

背景

在虛擬機和雲計算較爲成熟的時候,各家公司想在雲服務器上部署應用,一般都是像部署物理機那樣使用腳本或手動部署,但因爲本地環境和雲環境不一致,每每會出現各類小問題。web

這時候有個叫Paas的項目,就是專一於解決本地環境與雲端環境不一致的問題,而且提供了應用託管的功能。簡單得說,就是在雲服務器上部署Paas對應的服務端,而後本機就能一鍵push,將本地應用部署到雲端機器。而後因爲雲服務器上,一個Paas服務端,會接收多個用戶提交的應用,因此其底層提供了一套隔離機制,爲每一個提交的應用建立一個沙盒,每一個沙盒之間彼此隔離,互不干涉。redis

看看,這個沙盒是否是和docker很相似呢?實際上,容器技術並非docker的專屬,docker只是衆多實現容器技術中的一個而已。那爲何後來docker會變得如日中天呢?仍是得從Paas提及。docker

Paas的本質就是經過一套打包(本地)-分發(雲)的機制,幫助用戶將應用分發到大規模的集羣中,容器技術只是其中比較底層的一部分而已。聽起來很完美,但問題偏偏就出如今這個打包功能上。打包功能比較繁瑣,要爲每一個應用,語言,版本都打一個包,重點是打包過程經常出現問題,極可能本地運行得好好的,打包到Paas上就出現問題,並且這種問題無跡可尋,只能經過試錯解決。換句話說,Paas確實可讓你體驗到一鍵部署的快感,但在這以前,你要先體驗打包過程的萬千痛苦數據庫

這個讓用戶痛苦萬分的打包,docker的一個小創新的卻可以解決,那就是鏡像。鏡像自己也是一種打包機制,而且這個鏡像一般包含完整的操做系統,能夠儘量還本來地環境,同時你的應用還包含在這裏面。編程

經過鏡像這種東西,你能夠方便得在本地開發,而後將鏡像上傳到雲端服務器部署,且基本不須要或者只須要少許修改就可使雲端服務器擁有和本地同樣的應用環境,而後能夠經過這個鏡像創建彼此隔離的沙盒環境,以部署本身的多個應用。windows

可是,docker雖然解決了Paas打包難的問題,但Paas本來的大規模集羣部署的能力,倒是docker的弱項,甚至docker自己並無這方面的功能。

纔有了後來提出的容器編排概念,Swarm和K8s就是圍繞這塊而起的紛爭,固然那是另一個故事了。

docker實現原理

說完了docker實現原理,接下來就來看看docker底層是如何實現沙盒隔離機制的。

提及docker,不少人都會將它與虛擬機進行比較,基本都會引用下面這張圖:

docker vs 虛擬機

其中左邊是虛擬機的結構,右邊是docker容器的結構,但這張圖其實不是那麼準確。在虛擬機中,經過Hypervisor對硬件資源進行虛擬化,在這部分硬件資源上安裝操做系統,從而可讓上層的虛擬機和底層的宿主機相互隔離。但docker是沒有這種功能的,咱們在docker容器中看到的與宿主機相互隔離的沙盒環境(文件系統,資源,進程環境等),本質上是經過Linux的Namespace機制,CGroups(Control Groups)和Chroot等功能實現的。實際上Docker依舊是運行在宿主機上的一個進程(進程組),只是經過一些障眼法讓docker覺得本身是一個獨立環境。接下來咱們簡單介紹下這部份內容。

若是在一個docker容器裏面,使用ps命令查看進程,可能只會看到以下的輸出:

/ # ps
PID  USER   TIME COMMAND
  1 root   0:00 /bin/bash
  10 root   0:00 ps

在容器中執行ps,只會看到1號進程/bin/bash和10號進程ps。前面有說到,docker容器自己只是Linux中的一個進程(組),也就是說在宿主機上,這個/bin/bash的pid多是100或1000,那爲何在docker裏面看到的這個/bin/bash進程的pid是1呢?答案是linux提供的Namespace機制,將/bin/bash這個進程的進程空間隔離開了

具體的作法呢,就是在建立進程的時候添加一個可選的參數,好比下面這樣:

int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);

那樣後,建立的線程就會有一個新的命名空間,在這個命名空間中,它的pid就是1,固然在宿主機的真實環境中,它的pid仍是原來的值。上面的這個例子,其實只是pid Namespace(進程命名空間),除此以外,還有network Namespace(網絡命名空間),mount Namespace(文件命名空間,就是將整個容器的根目錄root掛載到一個新的目錄中,而後在其中放入內核文件看起來就像一個新的系統了)等,用以將整個容器和實際宿主機隔離開來。而這其實也就是容器基礎的基礎實現了。

可是,上述各類Namespace其實還不夠,還有一個比較大的問題,那就是系統資源的隔離,好比要控制一個容器的CPU資源使用率,內存佔用等,不然一個容器就吃盡系統資源,其餘容器怎麼辦。

而Linux實現資源隔離的方法就是Cgroups,具體的使用方法就很少介紹。Cgroups主要是提供文件接口,即經過修改 /sys/fs/cgroup/下面的文件信息,好比給出pid,CPU使用時間限制等就能限制一個容器所使用的資源。

因此,docker自己只是linux中的一個進程,經過Namespace和cgroup將它隔離成一個個單獨的沙盒。明白這點,就會明白docker的一些特性,好比說太過依賴內核的程序在docker上可能執行會出問題,好比沒法在低版本的宿主機上安裝高本版的docker等,由於本質上仍是執行在宿主機的內核上。

對了,還有mac和windows系統,這些是怎麼實現的呢?很簡單,它們的docker都是創建在虛擬化的linux上的,因此其實仍是linux。

說完容器,接下來就開始介紹編排了。

編排之爭

這裏咱們主要會介紹編排這個概念,以及從這個概念起引起的docker swarm和k8s的紛爭。

docker自己只是提供打包-部署的功能,它並無提供分佈式集羣(大規模集羣)管理的功能,這實際上是本來Paas項目的主要領域。而編排纔是容器技術的核心魅力所在,沒有編排,容器就只是一個沙箱工具。因此從docker成熟之後,你會發現它的主要發力點是在編排,也就是swarm項目上,不過這個docker的親兒子,swarm編排工具,卻敗給了橫空出世的k8s。

爲何會這樣?

先說說什麼是容器的編排,說簡單些就是對(docker)容器的配置,運行時候的行爲的管理

那麼docker swarm是怎麼進行編排的呢?這其實還涉及到另外一個項目,docker-compose,這兩個項目與docker Machine合稱爲docker三劍客(怎麼聽起來有點low)。

前面說到編排就是對容器的配置和運行行爲進行管理,那麼很天然的想法就是將這些配置和行爲的定義都寫到一個配置文件裏面,好比用戶須要運行容器A,容器B,容器C。那麼咱們能夠將這幾個容器相關的配置和關聯,好比網絡,磁盤,啓動副本,出錯行爲等配置,還有容器間的協做方式(啓動順序等)都寫到一個配置文件。最後經過一條命令,加載並執行這個配置文件,就可以實現容器的編排了

swarm作的事情很簡單,有時候簡單不必定是好事,由於那意味着難以知足業界複雜的需求。好比它在處理有狀態服務上的無力,又好比它難以處理多個服務間複雜的關係(處理服務的順序是不夠的)。這時候,脫胎於Borg的kubernetes(k8s)出如今人們的面前。它身上,沉澱着google數十年的經驗,能夠說它就是那個站在巨人肩膀上的寵兒。那麼相比於swarm,它的優點到底在哪裏呢?

答案在他的設計上,這個提及來得詳細介紹k8s才能說明白。從設計上說,k8s總體是基於API設計,即總體架構中涉及的組件均可插拔,以容器舉例,在k8s中,容器是可替換的,只要知足對應的接口設計的標準便可,docker是其中一種方案,而其餘容器技術也是可選方案。和容器相似的還有網絡插件,volume插件等等。有關k8s的詳細內容這裏很少介紹,有興趣的童鞋能夠參考如下文檔:

即整個設計是以集羣管理爲核心,總體架構都是鬆散的,可插拔的。而swarm則是以docker爲核心,二者設計就存在本質上的區別。

然後在容器的基礎上,k8s添加了另外一層的封裝,即Pod,所謂Pod,是一組相同或功能相似的容器所組成的組(task group)。爲何要有Pod呢?還記得容器的本質是什麼嗎,是操做系統中的一組進程,從某種程度上來講,從進程這個層次來進行管理,有些繁雜了。好比Linux都有進程組這個概念管理相同或彼此聯繫的一些進程(好比一個功能由多個進程協做完成,這多個進程構成一個進程組)。

在容器編排中,每每多個容器間也會有相似進程和進程組的關係(這裏就不舉例了,不少分佈式組件都有這種狀況),因此須要一個更高層次的抽象來幫助咱們對容器進行管理。在k8s中,承擔相似進程組的就是Pod,Pod是一種邏輯上的概念,同時Pod也是k8s中最小的調度單位,同組Pod中的容器都是共享volumns。

Pods

有了Pod,也就是組這個概念後,就可以更加方便對不一樣服務進行管理。可是它還有個更重要的意義,那就是基於容器的系統設計模式。

基於容器的分佈式系統設計之道

讓咱們回到1980年,假設你是一個寫慣了C的程序員,你接觸到一個名叫面向對象的編程概念,你會怎麼看待這個東西呢?可以想象這個東西在30年後會佔據編程領域的大半壁江山嗎?

而現在,docker容器(或者說Pod)就是一種相似OOP的東西,核心都是經過模塊化封裝,將不一樣的東西相互隔離,讓它們相互配合,完成某些事情

從這個角度,或許就能明白爲何前面說到的,容器價值不高,真正有價值的是編排。由於咱們一樣不會以爲一個java object有多大價值,OOP的編程思想,及其衍生的設計模式纔是精髓。

那麼從分佈式系統的設計模式的角度來講,容器能夠有多少種分類呢?和分佈式系統的搭建模式相似,有三種。

  • 單容器模式(single-container patterns for container management)
  • 單節點協做模式(single-node patterns of closely cooperating containers)
  • 和多節點協做模式(multi-node patterns)

PS:這部份內容多參考自google的Design patterns for container-based distributed systems,想看原味論文的童鞋請戳最下方的連接。

單容器模式和單節點協做模式看起來類似,但實際是徹底不一樣的東西。

單容器模式,簡單說就是在傳統Docker的基礎上(傳統docker的行爲比較簡單,只有run(),pause(),stop()),提供更加豐富的功能和生命週期的管理。說得更簡單點,使用k8s管理單個docker服務。

咱們主要介紹單節點協做模式和多節點協做模式。

單節點協做模式

單節點協做模式,簡單說就是在一個分佈式的容器服務環境中,經過一個單節點的服務輔助進行管理的這類模式。在這種模式中,須要依賴於k8s中,Pod這個概念的抽象,Pod即task group,一組相同或相似服務的容器的集合。

主要有如下幾種設計模式。

Sidecar pattern(邊車模式)

邊車,這個詞可能不少人沒聽過(包括我瞭解這個東西以前)。咱們先來貼一下邊車的圖,

邊車就是摩托車旁邊的那個小車,在某些環境下(比賽,我猜的),旁邊車上的人能夠給車手遞水、食物等操做。

邊車模式也是相似的,即在主服務(的容器,main container)身邊提供一個輔助容器,幫助主服務作一些髒活累活。

好比一個web應用,它會將日誌信息寫入到磁盤中,這時候咱們就能夠新增長一個日誌採集的邊車,協助web服務完成日誌採集的工做。就像下面這樣:

仍是挺好理解的,這樣的好處,相信瞭解過設計模式的童鞋隨隨便便就能列舉幾個,不過這裏仍是從容器的角度詳細介紹下:

  1. 容器是資源分配的一個單元。將邊車服務分離後,能夠更加靈活地經過cgroup配置資源,或者一些動態調節資源的操做(好比忙時給web服務更多資源而邊車更少資源)。
  2. 容器是最小的打包單位。有助於不一樣服務的責任劃分和測試。
  3. 容器能夠是複用的單位,好比能夠將日誌服務用語其餘服務。
  4. 提供了錯誤邊界,可使系統能夠正常降級,好比日誌服務出錯,不會致使web服務出錯。
  5. 容器是最小的部署單位,能夠爲每一個服務升級,和回滾。但這也多是缺點,由於服務一多難以管理。

由於分離因此多了這些好處,聽起來仍是蠻誘人的~

Ambassador pattern(外交官模式)

外交官模式,提供一個容器做爲代理與主服務(main container)通訊。
就至關於在通訊口出作多一層代理,好比主服務覺得是與一個本地redis通訊,但實際上代理會真正與一個redis集羣交互。

外交官模式的好處是,讓主服務與外部組件之間相互隔離。只經過代理的話,那麼外部組件能夠無縫進行替換,而這一切主服務都是無感知的。而後是方便測試和複用,其實就是服務之間解耦的好處啦,和上面邊車模式是有點相似的。

Adapter pattern(適配器模式)

前面說的兩種模式,主要是爲了幫助主服務(main coninter)更專一於本身的職責。而適配器模式則是爲了方便其餘組件。

舉個例子,假設你有多個服務(web,數據庫,緩存服務等),而後須要一個監控監控這幾個組件是否正常。正常狀況下,須要讓監控系統獲取不一樣服務之間的指標信息,而後才能進行監控。

但這樣的問題是,若是增長或減小服務,那麼對監控系統來講會很麻煩。適配器模式可以解決這種困擾。

若是多個服務,web,數據庫,緩存等都提供一個統一的對外接口,那麼咱們就可以使用一個適配器容器,統一獲取這些服務的指標信息,而後由監控系統經過這個適配器容器統一獲取全部的指標信息。以下圖所示。

OK,那麼以上就是單節點協做狀況下的三種設計模式,下面再看看多節點的協做模式。

多節點協做模式

這部份內容會比較簡單一些,這裏就不花太多篇幅進行講述。

除了單節點上的協做容器,模塊化容器還使構建協做的多節點分佈式應用程序變得更加容易。不過這部份內容聽起來很高大上,但實際上是很好理解的東西。

好比分佈式領域的zookeeper,你們應該都不陌生,在論文中,這種多個節點提供領導者選舉的模式,被稱爲領導者選舉模式。一樣的,kafka這類消息隊列,被稱之爲工做隊列模式(rk queue pattern)。而最後一種,則是相似spark的,master worker計算模式,即將一個計算任務分佈到多個其餘計算節點的這種方式,稱之爲Scatter/gather pattern模式。

列舉的幾種模式都是經過多個節點協做,而且經過暴露接口提供對外服務。不過其實基本就是常見的使用容器搭建分佈式服務的方式,若是使用過docker來搭建hadoop這一套東西,那麼對所謂的多節點協做模式確定不會陌生。

想一想也是,若是真的將分佈式系統當作一個工程項目,那麼這些多節點的部署模式確實須要一個名分。這能夠算是一個典型的實踐先於理論,理論總結實踐的例子吧。(不過我仍是以爲這部份內容有水論文的嫌疑)

那麼關於容器的分佈式系統設計的內容就先到這吧,有興趣看原論文的童鞋能夠翻到最下。

小結

OK,本文主要介紹了docker容器的發家歷史,而後介紹容器編排的重要性,並簡單說了爲何swarm會在編排的戰爭中輸給了k8s。最後則從容器編排這個概念延伸到基於容器技術的設計模式,三種模式中,單節點協做模式算是比較新穎,仍是有些啓發價值的。

以上~

參考文章:

Design patterns for container-based distributed systems

kubernetes設計理念

Docker 核心技術與實現原理

An Introduction to Docker and Analysis of its Performance

相關文章
相關標籤/搜索