《容器技術係列》一1.3 Docker各模塊功能與實現分析
本節書摘來異步社區《容器技術係列》一書中的第1章 ,第1.3節,孫宏亮 著, 更多章節內容可以訪問雲棲社區“異步社區”公眾號查看。
1.3 Docker各模塊功能與實現分析
下麵我們將從Docker的總架構圖入手,抽離出架構內的各個模塊,並對各個模塊進行更為細化的架構分析與功能闡述。
1.3.1 Docker Client
Docker Client是Docker架構中用戶與Docker Daemon建立通信的客戶端。在一台安裝有Docker的機器上,用戶可以使用可執行文件docker作為Docker Client,發起眾多Docker容器的管理請求。
Docker Client可以通過以下三種方式和Docker Daemon建立通信,分別為:tcp://host:port、unix://path_to_socket和fd://socketfd。為簡單起見,本書主要使用第一種方式作為講述兩者通信的原型。通信方式確定後,DockerClient與Docker Daemon建立連接並傳輸請求時,可以通過命令行flag參數的形式,設置安全傳輸層協議(TLS)的有關參數,保證傳輸的安全性。
Docker Client發送容器管理請求後,請求由Docker Daemon接收並處理,當Docker Client接收到返回的請求響應並做簡單處理後,Docker Client一次完整的生命周期就此結束。若需要繼續發送容器管理請求,用戶必須再次通過可執行文件docker創建Docker Client,並走完以上相同的流程。
1.3.2 Docker Daemon
Docker Daemon是Docker架構中一個常駐在後台的係統進程。所謂的“運行Docker”,即代表運行Docker Daemon。總之,DockerDaemon的作用主要有以下兩方麵:
接收並處理Docker Client發送的請求。
管理所有的Docker容器。
Docker Daemon運行時,會在後台啟動一個Server,Server負責接收Docker Client發送的請求;接收請求後,Server通過路由與分發調度,找到相應的Handler來處理請求。
啟動Docker Daemon所使用的可執行文件同樣是docker,與Docker Client啟動所使用的可執行文件docker相同。既然Docker Client與Docker Daemon都可以通過docker二進製文件創建,那麼如何辨別兩者就變得非常重要。實際上,執行docker命令時,通過傳入的參數可以辨別Docker Daemon與Docker Client,如docker –d代表Docker Daemon的啟動,dockerps則代表創建Docker Client,並發送ps請求。
Docker Daemon的架構大致可以分為三部分:Docker Server、Engine和Job。Daemon的架構如圖1-2所示。
1. Docker Server
Docker Server在Docker架構中專門服務於Docker Client,它的功能是接收並調度分發Docker Client發送的請求。Docker Server的架構如圖1-3所示。
在Docker Daemon的啟動過程中,DockerServer第一個完成。Docker Server通過包gorilla/mux,創建了一個mux.Router路由器,提供請求的路由功能。在Go語言中,gorilla/mux是一個強大的URL路由器以及調度分發器。創建路由器之後,Docker Server為mux.Router中添加有效的路由項,每一個路由項由HTTP請求方法(PUT、POST、GET或DELETE)、URL和Handler三部分組成。
由於Docker Client通過HTTP協議訪問Docker Daemon,故DockerServer創建完mux.Router之後,將Server的監聽地址以及mux.Router作為參數,創建一個httpSrv=http.Server{},最終執行httpSrv.Serve()開始服務於外部請求。
在服務過程中,Docker Server在listener上接收Docker Client的訪問請求。對於每一個Docker Client請求,DockerServer均會創建一個全新的goroutine來服務。在goroutine中,Docker Server首先讀取請求內容,然後做請求解析工作,接著匹配相應的路由項,隨後調用相應的Handler來處理,最後Handler處理完請求之後給Docker Client回複響應。
需要注意的是:Docker Server在Docker的啟動過程中運行,通過一個名為“serveapi”的Job來實現。理論上,Docker Server的運行隻是眾多Job中的一個,但是為了強調Docker Server的重要性以及它為後續Job服務的重要特性,本書將“serveapi”的Job單獨抽離出來分析,理解為Docker Server。
2. Engine
Engine是Docker架構中的運行引擎,同時也是Docker運行的核心模塊。Engine存儲著大量的容器信息,同時管理著Docker大部分Job的執行。換言之,Docker中大部分任務的執行都需要Engine協助,並通過Engine匹配相應的Job完成Job的執行。
在Docker源碼中,有關Engine的數據結構定義中含有一個名為handlers的對象。該handlers對象存儲的是關於眾多特定Job各自的處理方式handler。舉例說明,Engine的handlers對象中有一項為:{"create": daemon.ContainerCreate,},則說明當執行名為“create”的Job時,執行的是daemon.ContainerCreate這個handler。
除了容器管理之外,Engine還接管Docker Daemon的某些特定任務。當Docker Daemon遭遇到自身進程需要退出的情況時,Engine還負責完成DockerDaemon退出前的所有善後工作。
3. Job
Job可以認為是Docker架構中Engine內部最基本的工作執行單元。DockerDaemon可以完成的每一項工作都會呈現為一個Job。例如,在Docker容器內部運行一個進程,這是一個Job;創建一個新的容器,這是一個Job;在網絡上下載一個文檔,這是一個Job;包括之前在Docker Server部分談及的,創建Server服務於HTTP協議的API,這也是一個Job,等等。
有關Job接口的設計,與UNIX進程非常相仿。比如說,Job有一個名稱,有運行時參數,有環境變量,有標準輸入與標準輸出,有標準錯誤,還有返回狀態等。
對於Job而言,定義完畢之後,運行才能完成Job自身真正的使命。Job的運行函數Run()則用以執行Job本身。
1.3.3 Docker Registry
Docker Registry是一個存儲容器鏡像(Docker Image)的倉庫。容器鏡像(Docker Image)是容器創建時用來初始化容器rootfs的文件係統內容。Docker Registry將大量的容器鏡像匯集在一起,並為分散的Docker Daemon提供鏡像服務。
Docker的運行過程中,有三種情況可能與Docker Registry通信,分別為搜索鏡像、下載鏡像、上傳鏡像。這三種情況所對應的Job名稱分別為search、pull和push。
不同場景下,Docker Daemon可以使用不同的Docker Registry。公有Registry與私有Regsitry就是兩種場景模式不同的Docker Registry。其中,大家熟知的Docker Hub,就是全球範圍內最大的公有Registry。Docker可以通過互聯網訪問Docker Hub,並下載容器鏡像;同時Docker也允許用戶構建本地私有Registry,使容器鏡像的獲取在內網完成。
1.3.4 Graph
Graph在Docker架構中扮演的角色是容器鏡像的保管者。不論是Docker下載的鏡像,還是Docker構建的鏡像,均由Graph統一化管理。由於Docker支持多種不同的鏡像存儲方式,如aufs、devicemapper、Btrfs等,故Graph對鏡像的存儲也會因以上種類而存在一些差異。對Docker而言,同一種類型的鏡像被稱為一個repository,如名稱為ubuntu的鏡像都同屬一個repository;而同一個repository下的鏡像則會因tag存在差異而不同,如ubuntu這個repository下有tag為12.04的鏡像,也有tag為14.04的鏡像。Docker中Graph的架構如圖1-4所示。
本書對Graph以及容器鏡像(Docker Image)的分析將以aufs為主,並在第8章進行深入分析。
1.3.5 Driver
Driver是Docker架構中的驅動模塊。通過Driver驅動,Docker可以實現對Docker容器運行環境的定製,定製的維度主要有網絡環境、存儲方式以及容器執行方式。需要注意的是,Docker運行的生命周期中,並非用戶所有的操作都是針對Docker容器的管理,同時包括用戶對Docker運行信息的獲取,還包括Docker對Graph的存儲與記錄等。因此,為了將僅與Docker容器有關的管理從Docker Daemon的所有邏輯中區分開來,Docker的創造者設計了Driver層來抽象不同類別各自的功能範疇 。
Docker Driver的實現可以分為以下三類驅動:graphdriver、networkdriver和execdriver。
graphdriver主要用於完成容器鏡像的管理,包括從遠程Docker Registry上下載鏡像並進行存儲,也包括本地構建完鏡像後的存儲。當用戶下載指定的容器鏡像時,graphdriver將容器鏡像分層存儲在本地的指定目錄下;同時當用戶需要使用指定的容器鏡像來創建容器時,graphdriver從本地鏡像存儲目錄中獲取指定的容器鏡像,並按特定規則為容器準備rootfs;另外,當用戶需要通過指定Dockerfile構建全新鏡像時,graphdriver會負責新鏡像的存儲管理。
在graphdriver的初始化過程之前,有4種文件係統或類文件係統的驅動Driver在DockerDaemon內部注冊,它們分別是aufs、btrfs、vfs和devmapper。其中,aufs、btrfs以及devmapper用於容器鏡像的管理,vfs用於容器volume的管理。Docker在初始化之時,優先通過獲取係統環境變量“DOCKER_DRIVER”來提取所使用driver的指定類型。因此,之後所有的Graph操作,都使用該driver來執行。Docker鏡像是Docker技術中非常關鍵的。2014年12月,在Linux 3.18-rc2版本中OverlayFS被合並至Linux內核主線,在Docker 1.4.0版本發布時,Docker官方宣布支持overlay這一類graphdriver,即用戶在啟動Docker Daemon時可以選擇製定graphdriver的類型為overlay。graphdriver的架構如圖1-5所示。
networkdriver的作用是完成Docker容器網絡環境的配置,其中包括Docker Daemon啟動時為Docker環境創建網橋;Docker容器創建前為其分配相應的網絡接口資源;以及為Docker容器分配IP、端口並與宿主機做NAT端口映射,設置容器防火牆策略等。networkdriver的架構如圖1-6所示。
execdriver作為Docker容器的執行驅動,負責創建容器運行時的命名空間,負責容器資源使用的統計與限製,負責容器內部進程的真正運行等。在Docker 0.9.0版本之前,execdriver隻能通過LXC驅動來實現容器的啟動管理。實際上,當時Docker通過LXC驅動調用Linux下的LXC工具管理容器的創建,並控製管理容器的生命周期。從Docker 0.9.0開始,在繼續支持LXC的情況下,Docker的execdriver默認使用native驅動,native驅動完全獨立於LXC,屬於Docker項目下第一個全新的子項目,用於容器的創建與管理。Docker 默認使用native驅動的具體體現為:Docker Daemon啟動過程中加載的ExecDriverflag參數在配置文件中已經被設為native。native這個execdriver的存在,使得Docker對Linux容器的創建與管理有了自己的解決方案。execdriver架構如圖1-7所示。
1.3.6 libcontainer
libcontainer是Docker架構中一個使用Go語言設計實現的庫,設計初衷是希望該庫可以不依靠任何依賴,直接訪問內核中與容器相關的係統調用。
正是由於libcontainer的存在,Docker可以直接調用libcontainer,而最終操作容器的namespaces、cgroups、apparmor、網絡設備以及防火牆規則等。這一係列操作的完成都不需要依賴LXC或者其他包。libcontainer架構如圖1-8所示。
另外,libcontainer提供了一整套標準的接口來滿足上層對容器管理的需求。或者說,libcontainer屏蔽了Docker上層對容器的直接管理。又由於libcontainer使用Go這種跨平台的語言開發實現,且本身又可以被上層多種不同的編程語言訪問,因此,很難說未來的Docker一定會與Linux平台緊緊捆綁在一起。Docker Daemon的邏輯完全有可能位於其他非Linux操作係統的平台上,僅僅通過libcontainer的遠程調用來實現對Docker容器的管理。另一方麵,libcontainer與Docker Daemon的鬆耦合設計,似乎讓用戶感受到了除Linux Container 之外其他的容器技術接入Docker Daemon的可能性。libcontainer承接Linux內核與Docker Daemon的同時,也讓Docker的生態在跨平台方麵充滿生機。與此同時,Microsoft在其著名雲計算平台Azure中,也添加了對Docker的支持,可見Docker的開放程度與業界的火熱度。
暫不談Docker,由於本身完善的功能以及與應用係統的鬆耦合特性,libcontainer很有可能會在眾多其他以容器為原型的平台出現,同時也很有可能催生出雲計算領域全新的項目。
1.3.7 Docker Container
Docker Container(Docker容器)是Docker架構中服務交付的最終體現形式。Docker通過DockerDaemon的管理,libcontainer的執行,最終創建Docker容器。Docker容器作為一個交付單位,功能類似於傳統意義上的虛擬機(Virtual Machine),具備資源受限、環境與外界隔離的特點。然而,實現手段卻與KVM、Xen等傳統虛擬化技術大相徑庭。
Docker容器的從無到有,涉及Docker利用到的很多技術。總而言之,用戶可以根據自己的需求,通過Docker Client向Docker Daemon發送容器的創建與啟動請求,請求中將攜帶容器的配置信息,從而達到定製相應Docker容器的目的。用戶對Docker容器的配置有以下4個基本方麵:
通過指定容器鏡像,使得Docker容器可以自定義rootfs等文件係統。
通過指定物理資源的配額,如CPU、內存等,使得Docker容器使用受限的物理資源。
通過配置容器網絡及其安全策略,使得Docker容器擁有獨立且安全的網絡環境。
通過指定容器的運行命令,使得Docker容器執行指定的任務。
最後更新:2017-06-21 15:02:03