docker:架構拆解

docker:架構拆解
 近期很多朋友都在學習docker,筆者也是在慢慢學習中。當大家在終端飛快的敲擊docker的這些命令時:
  docker ps
  docker run
  docker image
  docker pull
  ......

 大家有沒有思考過,當使用這些命令時究竟發生了什麼?它是一個怎樣的執行流程、它的架構又是怎樣呢?一起來了解一下吧。

Docker架構

 Docker使用了傳統的cs架構模式(cilent-server),架構圖如下圖所示。用戶通過Docker client與Docker daemon建立通信,並將請求發送給後者。而Docker的後端是鬆耦合結構,不同模塊各司其職,有機組合,完成用戶的請求。

docker:架構拆解

 從上圖中可以看出,Docker daemon是Docker架構中的主要用戶接口。首先,它提供了API Server用於接收來自於Docker client的請求,其後根據不同的請求分發給Docker daemon的不同模塊執行相應的工作,其中對容器運行時、volume、鏡像以及網絡方面的具體實現放在daemon以外的模塊或項目中(其中libcontainer、libnetwork已經成爲單獨項目,獨立於docker項目。而volimedriver、distribution、registry、layer、image、reference只是相對獨立的代碼模塊)。值得注意的是,Docker一直致力於將其進一步解耦,削減Docker daemon的功能,熟悉Docker早期版本的朋友在對照上面的架構圖時一定要注意到了這種變化。

 Docker通過driver模塊來實現對Docker容器執行環境的創建和管理:

  • 當需要創建Docker容器時,可通過鏡像管理(image management)部分的distribution和registry模塊從Docker registry中下載鏡像,並通過鏡像管理的image、refernce和layer存儲鏡像的元數據,通過鏡像存儲驅動graphdriver將鏡像文件存儲於具體的文件中;

  • 當需要爲容器創建數據卷volume時,通過volume模塊來調用某個具體的volumedriver,來創建一個數據卷並負責後續的掛載操作;

  • 當需要限制Docker容器運行資源或執行用戶指令等操作時, 則通過execdriver來完成。libcontainer是對cgroups和namespace的二次封裝,execdriver是通過libcontainer來實現對容器的具體管理,包括利用UTS、IPC、PID、Netwrok、Mount、User等namespace實現容器之間的資源隔離和利用cgoups實現對容器的資源限制。

 當運行容器的命令執行完畢後,一個實際的容器就處於運行狀態,該容器擁有獨立的文件系統、相對安全且相互隔離的運行環境。Docker 1.9版本以後,volume、network的生命週期都是獨立於容器的,與容器一樣是Docker中的一等公民,Docker用戶可以單獨增刪改查volume或network,然後在創建容器的時候根據需要配置給容器。

  下面對各個模塊的功能進行介紹一下。

Docker daemon

 Docker daemon是Docker最核心的後臺進程,它負責響應來自Docker client的請求,然後將這些請求翻譯成系統調用完成容器管理操作。該進程會在後臺啓動一個API Server,負責接收由Docker client發送的請求;接收到的請求將通過Docker daemon分發調度,再由具體的函數來執行請求。

Docker client

 Docker client是一個泛稱,用來向Docker daemon發起請求,執行相應的容器管理操作。它既可以是命令行工具docker,也可以是任何遵循了Docker API的客戶端。目前,社區中維護着的Docker client種類非常豐富,涵蓋了包括C#、 Java、Go、Ruby、JavaScript等常用語言,甚至還有使用Angular庫編寫的WebUI格式的客戶端,足以滿足大多數據用戶的需求。

image management

 Docker通過distribution、registry、layer、image、reference等模塊實現了Docker鏡像的管理,我們將這些模塊統稱爲鏡像管理(image management)。在Docker 1.10以前的版本中,這一功能通過graph組件來完成的。下面簡單介紹一下:

  • distribution負責與Docker registry交互,上傳下載鏡像以及存儲v2 registry有關的元數據。
  • registry模塊負責與Docker registry有關的身份驗證、鏡像查找、鏡像驗證以及管理registry mirror等交互操作。
  • image模塊負責與鏡像元數據有關的存儲、查找,鏡像層的索引、查找以及鏡像tar包有關的導入、導出等操作。
  • reference負責存儲本地所有鏡像的repository和tag名,並維護與鏡像ID之間的映射關係。
  • layer模塊負責與鏡像層和容器層元數據有關的增刪查改,並負責將鏡像層的增刪查改操作映射到實際存儲鏡像層文件系統的graphdriver模塊。

execdriver、volumedriver、graphdriver

 前面提到,Docker daemon負責將用戶請求轉譯成系統調用,進而創建和管理容器。而在具體實現過程中,爲了將這些系統調用抽象成爲統一的操作接口方便調用者使用,Docker把這些操作分成了容器執行驅動、volume存儲驅動、鏡像存儲驅動這3種,分別對應execdriver、volumedriver、graphdriver:

  • execdriver是對Linux系統的namespaces、cgroups、apparmor、SELinux等容器運行所需的系統操作進行的一層二次封裝,其本質作用類似於LXC,但是功能要更全面。這也就是爲什麼LXC會作爲execdriver的一種實現而存在。當然,execdriver最主要的實現,也是現在的默認實現,是Docker官方編寫的libcontainer庫。
  • volumedriver是volume數據卷存儲操作的最終執行者,負責volume的增刪改查,屏蔽不同驅動實現的區別,爲上層調用者提供一個統一的接口。Docker中作爲默認實現的volumedriver是local,默認將文件存儲於Docker根目錄下的volume文件夾裏。其他的volumedriver均是通過外部插件實現的。
  • graphdriver是所有與容器鏡像相關操作的最終執行者。graphdriver會在Docker工作目錄下維護一組與鏡像層對應的目錄,並記下鏡像層之間的關係以及與具體的graphdriver實現相關的元數據。這樣,用戶對鏡像的操作最終會被映射成對應這些目錄文件以及元數據的增刪改查,從而屏蔽掉不同文件存儲實現對於上層調用者的影響。在Linux環境下,目前Docker已經支持graphdriver包括aufs、btrfs、zfs、overlay和vfs。

network

 在Docker 1.9版本以前,網絡是通過networkdriver模塊以及libcontainer庫完成的,現在這部分功能已經分離成一個libnetwork庫獨立維護了,可參考 https://github.com/docker/libnetwork 。libnetwork抽象出了一個容器網絡模型(Container Network Model,CNM),並給調用者提供了一個抽象接口,其目標不權限於Docker容器。CNM模型對真實的容器網絡抽象出了沙盒(sandbox)、端點(endpoint)、網絡(network)這3種對象,由具體網絡驅動(包括內置的Bridge、Host、None和overlay驅動以及通過插件配置的外部驅動)操作對象,並通過網絡控制器提供一個統一接口供調用者管理網絡。網絡驅動負責實現具體的操作,包括創建容器通信所需的網絡,容器的network namespace,這個網絡所需的虛擬網卡,分配通信所需的IP,服務訪問的端口和容器與宿主機之間的端口映射,設置hosts、resolv.conf、iptables等。

喜歡我的文章,請點擊最上方右角處的《關注》支持一下!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章