17分鍾快速瀏覽一遍
Docker內部構建
要理解Docker內部構建,需要理解以下三種部件:
Docker鏡像(Image)
Docker容器(Container)
Docker倉庫(repository)
基本上理解了這三個概念,就理解了Docker的整個生命周期。
1)Docker鏡像(Image)
Docker鏡像就是一個只讀的模板。比如,一個鏡像可以包含一個完整的ubuntu操作系統環境,里面僅安裝了apache或用戶需要的其他應用程序。鏡像可以用來創建Docker容器。另外Docker提供了一個很簡單的機制來創建鏡像或者更新現有的鏡像,用戶甚至可以直接從其他人哪里下載一個已經做好的鏡像來直接使用。
2)Docker容器(Container)
Docker利用容器來運行應用。容器是從鏡像創建的運行實例,它可以被啟動、開始、停止、 刪除。每個容器都是相互隔離的、保證安全的平台。可以把容器看做是一個簡易版的Linux 環境(包括root用戶權限、進程空間、用戶空間和網絡空間等)和運行在其中的應用程序。
注:鏡像是只讀的,容器在啟動的時候創建一層可寫層作為最上層。
3)Docker倉庫(repository)
倉庫是集中存放鏡像文件的場所。有時候會把倉庫和倉庫注冊服務器(Registry)混為一談, 並不嚴格區分。實際上,倉庫注冊服務器上往往存放着多個倉庫,每個倉庫中又包含了多個鏡像,每個鏡像有不同的標簽(tag)。
倉庫分為公開倉庫(Public)和私有倉庫(Private)兩種形式。最大的公開倉庫是Docker Hub, 存放了數量龐大的鏡像供用戶下載。國內的公開倉庫包括Docker Pool等,可以提供大陸用戶更穩定快速的訪問。當然,用戶也可以在本地網絡內創建一個私有倉庫。當用戶創建了自己的鏡像之后就可以使用push命令將它上傳到公有或者私有倉庫,這樣下次在另外一台機器上使用這個鏡像時候,只需要從倉庫上pull下來就可以了。
注:Docker倉庫的概念跟Git類似,注冊服務器可以理解為GitHub這樣的托管服務。
Docker總架構分解
Docker對使用者來講是一個C/S模式的架構,而Docker的后端是一個非常松耦合的架構,模塊各司其職,並有機組合,支撐Docker的運行。
在此,先附上Docker總架構:

從上圖不難看出,用戶是使用Docker Client與Docker Daemon建立通信,並發送請求給后者。
而Docker Daemon作為Docker架構中的主體部分,首先提供Server的功能使其可以接受Docker Client的請求;而后Engine執行Docker內部的一系列工作,每一項工作都是以一個Job的形式的存在。
Job的運行過程中,當需要容器鏡像時,則從Docker Registry中下載鏡像,並通過鏡像管理驅動graphdriver將下載鏡像以Graph的形式存儲;當需要為Docker創建網絡環境時,通過網絡管理驅動networkdriver創建並配置Docker容器網絡環境;當需要限制Docker容器運行資源或執行用戶指令等操作時,則通過execdriver來完成。
而libcontainer是一項獨立的容器管理包,networkdriver以及execdriver都是通過libcontainer來實現具體對容器進行的操作。當執行完運行容器的命令后,一個實際的Docker容器就處於運行狀態,該容器擁有獨立的文件系統,獨立並且安全的運行環境等。
一、Docker架構內各模塊的功能與實現分析
接下來,我們將從Docker總架構圖入手,抽離出架構內各個模塊,並對各個模塊進行更為細化的架構分析與功能闡述。主要的模塊有:Docker Client、Docker Daemon、Docker Registry、Graph、Driver、libcontainer以及Docker container。
1.1)Docker Client
Docker Client是Docker架構中用戶用來和Docker Daemon建立通信的客戶端。用戶使用的可執行文件為docker,通過docker命令行工具可以發起眾多管理container的請求。
Docker Client可以通過以下三種方式和Docker Daemon建立通信:tcp://host:port,unix://path_to_socket和fd://socketfd。為了簡單起見,本文一律使用第一種方式作為講述兩者通信的原型。與此同時,與Docker Daemon建立連接並傳輸請求的時候,Docker Client可以通過設置命令行flag參數的形式設置安全傳輸層協議(TLS)的有關參數,保證傳輸的安全性。
Docker Client發送容器管理請求后,由Docker Daemon接受並處理請求,當Docker Client接收到返回的請求相應並簡單處理后,Docker Client一次完整的生命周期就結束了。當需要繼續發送容器管理請求時,用戶必須再次通過docker可執行文件創建Docker Client。
1.2)Docker Daemon
Docker Daemon是Docker架構中一個常駐在后台的系統進程,功能是:接受並處理Docker Client發送的請求。該守護進程在后台啟動了一個Server,Server負責接受Docker Client發送的請求;接受請求后,Server通過路由與分發調度,找到相應的Handler來執行請求。
Docker Daemon啟動所使用的可執行文件也為docker,與Docker Client啟動所使用的可執行文件docker相同。在docker命令執行時,通過傳入的參數來判別Docker Daemon與Docker Client。
Docker Daemon的架構,大致可以分為以下三部分:Docker Server、Engine和Job。Daemon架構如下圖。
Docker:架構分解
1.3)Docker Server
Docker Server在Docker架構中是專門服務於Docker Client的server。該server的功能是:接受並調度分發Docker Client發送的請求。Docker Server的架構如下圖。

在Docker的啟動過程中,通過包gorilla/mux,創建了一個mux.Router,提供請求的路由功能。在Golang中,gorilla/mux是一個強大的URL路由器以及調度分發器。該mux.Router中添加了眾多的路由項,每一個路由項由HTTP請求方法(PUT、POST、GET或DELETE)、URL、Handler三部分組成。
若Docker Client通過HTTP的形式訪問Docker Daemon,創建完mux.Router之后,Docker將Server的監聽地址以及mux.Router作為參數,創建一個httpSrv=http.Server{},最終執行httpSrv.Serve()為請求服務。
在Server的服務過程中,Server在listener上接受Docker Client的訪問請求,並創建一個全新的goroutine來服務該請求。在goroutine中,首先讀取請求內容,然后做解析工作,接着找到相應的路由項,隨后調用相應的Handler來處理該請求,最后Handler處理完請求之后回復該請求。
需要注意的是:Docker Server的運行在Docker的啟動過程中,是靠一個名為”serveapi”的job的運行來完成的。原則上,Docker Server的運行是眾多job中的一個,但是為了強調Docker Server的重要性以及為后續job服務的重要特性,將該”serveapi”的job單獨抽離出來分析,理解為Docker Server。
1.4)Engine
Engine是Docker架構中的運行引擎,同時也Docker運行的核心模塊。它扮演Docker container存儲倉庫的角色,並且通過執行job的方式來操縱管理這些容器。
在Engine數據結構的設計與實現過程中,有一個handler對象。該handler對象存儲的都是關於眾多特定job的handler處理訪問。舉例說明,Engine的handler對象中有一項為:{“create”: daemon.ContainerCreate,},則說明當名為”create”的job在運行時,執行的是daemon.ContainerCreate的handler。
1.5)Job
一個Job可以認為是Docker架構中Engine內部最基本的工作執行單元。Docker可以做的每一項工作,都可以抽象為一個job。例如:在容器內部運行一個進程,這是一個job;創建一個新的容器,這是一個job,從Internet上下載一個文檔,這是一個job;包括之前在Docker Server部分說過的,創建Server服務於HTTP的API,這也是一個job,等等。
Job的設計者,把Job設計得與Unix進程相仿。比如說:Job有一個名稱,有參數,有環境變量,有標准的輸入輸出,有錯誤處理,有返回狀態等。
1.6)Docker Registry
Docker Registry是一個存儲容器鏡像的倉庫。而容器鏡像是在容器被創建時,被加載用來初始化容器的文件架構與目錄。
在Docker的運行過程中,Docker Daemon會與Docker Registry通信,並實現搜索鏡像、下載鏡像、上傳鏡像三個功能,這三個功能對應的job名稱分別為”search”,”pull” 與 “push”。
其中,在Docker架構中,Docker可以使用公有的Docker Registry,即大家熟知的Docker Hub,如此一來,Docker獲取容器鏡像文件時,必須通過互聯網訪問Docker Hub;同時Docker也允許用戶構建本地私有的Docker Registry,這樣可以保證容器鏡像的獲取在內網完成。
1.7)Graph
Graph在Docker架構中扮演已下載容器鏡像的保管者,以及已下載容器鏡像之間關系的記錄者。一方面,Graph存儲着本地具有版本信息的文件系統鏡像,另一方面也通過GraphDB記錄着所有文件系統鏡像彼此之間的關系。Graph的架構如下圖。

其中,GraphDB是一個構建在SQLite之上的小型圖數據庫,實現了節點的命名以及節點之間關聯關系的記錄。它僅僅實現了大多數圖數據庫所擁有的一個小的子集,但是提供了簡單的接口表示節點之間的關系。
同時在Graph的本地目錄中,關於每一個的容器鏡像,具體存儲的信息有:該容器鏡像的元數據,容器鏡像的大小信息,以及該容器鏡像所代表的具體rootfs。
1.8)Driver
Driver是Docker架構中的驅動模塊。通過Driver驅動,Docker可以實現對Docker容器執行環境的定制。由於Docker運行的生命周期中,並非用戶所有的操作都是針對Docker容器的管理,另外還有關於Docker運行信息的獲取,Graph的存儲與記錄等。因此,為了將Docker容器的管理從Docker Daemon內部業務邏輯中區分開來,設計了Driver層驅動來接管所有這部分請求。
在Docker Driver的實現中,可以分為以下三類驅動:graphdriver、networkdriver和execdriver。
graphdriver主要用於完成容器鏡像的管理,包括存儲與獲取。即當用戶需要下載指定的容器鏡像時,graphdriver將容器鏡像存儲在本地的指定目錄;同時當用戶需要使用指定的容器鏡像來創建容器的rootfs時,graphdriver從本地鏡像存儲目錄中獲取指定的容器鏡像。
在graphdriver的初始化過程之前,有4種文件系統或類文件系統在其內部注冊,它們分別是aufs、btrfs、vfs和devmapper。而Docker在初始化之時,通過獲取系統環境變量”DOCKER_DRIVER”來提取所使用driver的指定類型。而之后所有的graph操作,都使用該driver來執行。
graphdriver的架構如圖

networkdriver的用途是完成Docker容器網絡環境的配置,其中包括Docker啟動時為Docker環境創建網橋;Docker容器創建時為其創建專屬虛擬網卡設備;以及為Docker容器分配IP、端口並與宿主機做端口映射,設置容器防火牆策略等。networkdriver的架構如下圖。

execdriver作為Docker容器的執行驅動,負責創建容器運行命名空間,負責容器資源使用的統計與限制,負責容器內部進程的真正運行等。在execdriver的實現過程中,原先可以使用LXC驅動調用LXC的接口,來操縱容器的配置以及生命周期,而現在execdriver默認使用native驅動,不依賴於LXC。具體體現在Daemon啟動過程中加載的ExecDriverflag參數,該參數在配置文件已經被設為”native”。這可以認為是Docker在1.2版本上一個很大的改變,或者說Docker實現跨平台的一個先兆。execdriver架構如下圖:

1.9)libcontainer
libcontainer是Docker架構中一個使用Go語言設計實現的庫,設計初衷是希望該庫可以不依靠任何依賴,直接訪問內核中與容器相關的API。
正是由於libcontainer的存在,Docker可以直接調用libcontainer,而最終操縱容器的namespace、cgroups、apparmor、網絡設備以及防火牆規則等。這一系列操作的完成都不需要依賴LXC或者其他包。libcontainer架構如下圖:

另外,libcontainer提供了一整套標准的接口來滿足上層對容器管理的需求。或者說,libcontainer屏蔽了Docker上層對容器的直接管理。又由於libcontainer使用Go這種跨平台的語言開發實現,且本身又可以被上層多種不同的編程語言訪問,因此很難說,未來的Docker就一定會緊緊地和Linux捆綁在一起。而於此同時,Microsoft在其著名雲計算平台Azure中,也添加了對Docker的支持,可見Docker的開放程度與業界的火熱度。
暫不談Docker,由於libcontainer的功能以及其本身與系統的松耦合特性,很有可能會在其他以容器為原型的平台出現,同時也很有可能催生出雲計算領域全新的項目。
1.10)Docker container
Docker container(Docker容器)是Docker架構中服務交付的最終體現形式。
Docker按照用戶的需求與指令,訂制相應的Docker容器:
用戶通過指定容器鏡像,使得Docker容器可以自定義rootfs等文件系統; 用戶通過指定計算資源的配額,使得Docker容器使用指定的計算資源; 用戶通過配置網絡及其安全策略,使得Docker容器擁有獨立且安全的網絡環境; 用戶通過指定運行的命令,使得Docker容器執行指定的工作。

對於整個Docker的介紹先到這里,真正去理解Docker架構還需要多去實踐Docker才行。
