導讀 | Docker是一個開源的應用容器引擎,讓開發者能夠打包他們的應用以及依賴包到一個可移植的容器中,而後發佈到任何流行的Linux機器上,也能夠實現虛擬化。本文立足於新手,從容器和虛擬機兩個大的概念入手,由淺入深,由宏轉微,爲咱們解析了Docker的方方面面。來吧朋友們,理解它,熱愛它,而後更好地使用它。 |
做爲程序員或者技術人員,你們確定據說過Docker的鼎鼎大名——這款工具可以幫助咱們高效打包、發佈及運行承載着應用程序的「容器」系統。其發展如火如荼——從開發者到運維人員,每一個人都在關注着這位技術新貴。即便是像谷歌、VMware與Amazon這樣的技術巨擘也在構建相關服務爲其提供支持。java
不管你們是否有意當即使用Docker,咱們都應當對其基礎概念加以瞭解,並明確區分容器與虛擬機之間的差別所在。目前網絡上已經存在大量對兩者關聯與區別的闡述性文章,不過在今天的綜述中,咱們將立足於新手對其加以剖析。node
下面首先聊聊虛擬機與容器到底是什麼?python
容器與虛擬機到底是什麼?linux
容器與虛擬機擁有着相似的使命:對應用程序及其關聯性進行隔離,從而構建起一套可以隨處運行的自容納單元。android
此外,容器與虛擬機還擺脫了對物理硬件的需求,容許咱們更爲高效地使用計算資源,從而提高能源效率與成本效益。git
容器與虛擬機之間的核心差別在於其架構方法。下面一塊兒進行深刻了解。程序員
虛擬機web
虛擬機在本質上就是在模擬一臺真實的計算機設備,同時遵循一樣的程序執行方式。虛擬機可以利用「虛擬機管理程序」運行在物理設備之上。反過來,虛擬機管理程序則可運行在主機設備或者「裸機」之上。docker
下面用更直白的表達來講明:數據庫
虛擬機管理程序可表現爲軟件、固件或者硬件,並做爲虛擬機的運行基礎。虛擬機管理程序自己運行在物理計算機之上,咱們也將這種底層硬件稱爲「主機設備」。主機設備爲虛擬機提供資源,包括內存與CPU。這些資源由不一樣虛擬機共享,並根據須要進行隨意分配。所以若是一套虛擬機運行有需求大量資源的高強度應用程序,那麼咱們能夠在同一主機設備上爲其分配遠高於其它虛擬機的資源配額。
運行在主機設備上的虛擬機(固然,須要配合虛擬機管理程序)一般被稱爲一套「客戶機」。這套客戶機容納有應用程序及其運行所必需的各種組件(例如系統二進制文件及庫)。它同時還包含有完整的虛擬硬件堆棧,其中包括虛擬網絡適配器、存儲以及CPU——這意味着它也擁有本身的完整訪客操做系統。着眼於內部,這套客戶機自成體系並擁有專用資源。而從外部來看,這套虛擬機使用的則是由主機設備提供的共享資源。
如上所述,客戶機能夠運行主機虛擬機管理程序或者裸機虛擬機管理程序。兩者之間存在着多種重要區別。
首先,主機虛擬機管理程序運行在主機設備的操做系統之上。舉例來講,一臺運行有OS X的計算機能夠在操做系統之上安裝虛擬機(例如VirtualBox或者VMware Workstation 8)。該虛擬機並不會直接訪問硬件,所以其須要經由主機操做系統(也就是Mac OS X)實現資源獲取。
主機虛擬機管理程序的優點在於,其基本擺脫了對底層硬件的要求。該主機操做系統負責提供硬件驅動程序,而非由虛擬機管理程序自身提供,所以咱們認爲其具有更理想的「硬件兼容性」。在另外一方面,這種介於硬件與虛擬機管理程序之間的額外層會帶來更多資源消耗,進而下降虛擬機性能表現。
裸機虛擬機管理程序則將虛擬機直接安裝並運行在主機設備硬件之上以改善性能表現。因爲其直接接入底層硬件,所以咱們再也不須要主機操做系統做爲輔助。在這種狀況下,咱們能夠直接在硬件上安裝虛擬機管理程序並將其做爲操做系統。與主機虛擬機管理程序不一樣,裸機虛擬機管理程序擁有本身的設備驅動程序及接口,從而直接支持各種I/O、處理或者操做系統特定任務相關組件。這種方式可以帶來更理想的性能水平、可擴展性以及穩定性。但代價是其硬件兼容性比較有限,由於虛擬機管理程序只能包含一部分設備驅動程序。
說到這裏,你們可能提出疑問:爲何咱們非得在虛擬機與主機設備之間添加「虛擬機管理程序」呢?
這個嘛,由於虛擬機自己擁有一套虛擬操做系統,而虛擬機管理程序則負責爲虛擬機提供平臺以管理並運行這套訪客操做系統。如此一來,主機計算機就可以爲運行於其上的各虛擬機分配共享資源了。
虛擬機原理示意圖
如你們所見,虛擬機會將虛擬硬件、內核(即操做系統)以及用戶空間打包在新虛擬機當中。
容器
與提供硬件虛擬化機制的虛擬機不一樣,容器經過對「用戶空間」的抽象化處理提供操做系統層級的虛擬化機制。經過對容器進行分解,你們將能夠很是清晰地理解其中含義。
出於各類考量與需求,容器在外觀上與虛擬機很是類似。舉例來講,兩者皆擁有專有處理空間、可以做爲root執行命令、提供專有網絡接口與IP地址、容許定製化路由及iptable規則,且可啓動文件系統等等。
容器與虛擬機間的最大區別在於,各容器系統共享主機系統的內核。
容器原理示意圖
以上示意圖顯示了容器如何對用戶空間進行打包,而不像虛擬機那樣一樣對內核或者虛擬硬件進行打包。每套容器都擁有本身的隔離化用戶空間,從而使得多套容器可以運行在同一主機系統之上。咱們能夠看到所有操做系統層級的架構均可實現跨容器共享。唯一須要獨立構建的就是二進制文件與庫。正由於如此,容器才擁有極爲出色的輕量化特性。
Docker的功能定位
Docker爲基於Linux容器的開源項目,其利用Linux內核中的各項功能——例如命名空間與控制組——以在操做系統之上建立容器。
容器概念並非什麼新鮮事物; 谷歌公司多年來一直在使用本身開發的容器技術。其它Linux容器技術方案還包括Solaris Zones、BSD jails以及LXC,且其都已經擁有多年的發展歷史。
那麼爲何Docker的出現會快速吸引到技術業界的注意?
最後但一樣重要的是,Docker鯨魚實在是太惹人喜好了~
Docker基本概念
如今咱們對Docker有了宏觀印象,下面具體對其組件進行解讀:
Docker Engine
Docker Engine屬於Docker的運行層。這是一套輕量化運行時及工具組合,負責管理容器、鏡像、構建 等等。它以原生方式運行在Linux系統之上,並由如下元素構成:
Docker Daemon,運行在主機計算機之上。
Docker Client,負責與Docker Daemon通訊以執行命令。
REST API,用於同Docker Daemon遠程交互。
Docker Client
Docker Client正是咱們做爲最終用戶的通訊對象。咱們能夠將其視爲Docker的UI。
咱們進行的一切操做都將直接接入Docker Client,再由其將指令傳遞至Docker Daemon。
Docker Daemon
Docker Daemon直接將執行命令發送至Docker Client——例如構建、運行以及分發等等。Docker Daemon運行在主機設備之上,但做爲用戶,咱們永遠不會直接與該Daemon進行通訊。Docker Client也能夠運行在主機設備上,但並不是必需。它亦可以運行在另外一臺設備上,並與運行在目標主機上的Docker Daemon進行遠程通訊。
Dockerfile
Dockerfile是咱們編寫指令以構建Docker鏡像的載體。這些指令包括:
RUN apt-get y install some-package:安裝某軟件包
EXPOSE 8000: 開放端口
ENV ANT_HOME /usr/local/apache-ant:傳遞環境變量
相似的指令還有不少。一旦設置了Dockerfile,你們就能夠利用docker build命令來構建鏡像了。下面來看Dockerfile示例:
# Start with ubuntu 14.04 FROM ubuntu:14.04MAINTAINER preethi kasireddy iam.preethi.k@gmail.com# For SSH access and port redirection ENV ROOTPASSWORD sample# Turn off prompts during installations ENV DEBIAN_FRONTEND noninteractive RUN echo "debconf shared/accepted-oracle-license-v1-1 select true" | debconf-set-selections RUN echo "debconf shared/accepted-oracle-license-v1-1 seen true" | debconf-set-selections# Update packages RUN apt-get -y update# Install system tools / libraries RUN apt-get -y install python3-software-properties \ software-properties-common \ bzip2 \ ssh \ net-tools \ vim \ curl \ expect \ git \ nano \ wget \ build-essential \ dialog \ make \ build-essential \ checkinstall \ bridge-utils \ virt-viewer \ python-pip \ python-setuptools \ python-dev # Install Node, npm RUN curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash - RUN apt-get install -y nodejs # Add oracle-jdk7 to repositories RUN add-apt-repository ppa:webupd8team/java # Make sure the package repository is up to date RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list # Update apt RUN apt-get -y update # Install oracle-jdk7 RUN apt-get -y install oracle-java7-installer # Export JAVA_HOME variable ENV JAVA_HOME /usr/lib/jvm/java-7-oracle # Run sshd RUN apt-get install -y openssh-server RUN mkdir /var/run/sshd RUN echo "root:$ROOTPASSWORD" | chpasswd RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config # SSH login fix. Otherwise user is kicked off after login RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd # Expose Node.js app port EXPOSE 8000 # Create tap-to-android app directory RUN mkdir -p /usr/src/my-app WORKDIR /usr/src/my-app # Install app dependencies COPY . /usr/src/my-app RUN npm install # Add entrypoint ADD entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] CMD ["npm", "start"]
示例Dockerfile
Docker鏡像
鏡像屬於只讀模板,你們能夠藉此配合Dockerfile中的編寫指令集進行容器構建。鏡像定義了打包的應用程序以及其相關依賴。這些依賴就好像是其啓動時須要運行的進程。
Docker鏡像利用Dockerfile實現構建。Dockerfile中的每條指令都會在鏡像中添加一個新的「層」,這些層則表現爲鏡像文件系統中的一個分區——咱們能夠對其進行添加或者替換。層概念正是Docker輕量化的基礎。Docker利用一套Union File System創建起這套強大的結構:
Union File System
Docker利用Union File System以構建鏡像。你們能夠將Union File System視爲一套可堆疊文件系統,這意味着各文件系統中的文件與目錄(在Docker中被稱爲分支)能夠透明方式覆蓋並構成單一文件系統。
覆蓋分支內的各目錄內容擁有一樣的路徑,並將被視爲單一合併目錄,這就避免了須要爲每一個層分別建立副本的麻煩。相反,這些目錄可調用特定指針以指向一樣的資源; 當特定層須要進行修改時,其會分別建立修改前與修改後的本地副本。經過這種方式,文件系統表現出可寫入特性,但其實際上並未接受任何寫入操做。(換言之,這是一套‘寫入即複製’系統。)
分層系統擁有兩大核心優點:
無重複數據: 分層機制使得咱們在使用鏡像建立並運行新容器時無需複製完整的文件集,從而保證Docker容器實例擁有良好的運行速度與低廉的資源成本。
層隔離: 變動操做速度很快——當咱們對鏡像進行變動時,Docker只須要對進行了變動的層進行廣播。
Volumes
Volumes屬於容器中的「數據」部分,會在容器建立時進行初始化。Volumes機制容許咱們持久保留並共享容器數據。數據卷 獨立於默認Union File System以外,且做爲普通目錄及文件存在於主機文件系統當中。所以即便是在對容器進行銷燬、更新或者重構時,數據卷仍然不受影響。當咱們須要對某一數據捲進行更新時,直接加以變動便可。(做爲另外一項優點,數據卷還可以在不一樣容器之間進行共享與複用。)
Docker容器
如上所述,Docker容器將一款應用程序的軟件打包在單一環境當中,同時包含所有運行必需的要素。其中包括操做系統、應用程序代碼、運行時、系統工具、系統庫等等。Docker容器由Docker鏡像構建而成。因爲鏡像存在只讀屬性,所以Docker會在鏡像在只讀文件系統之上添加一套讀取-寫入文件系統以實現容器建立。
另外,在建立容器時,Docker還會建立一套網絡接口以幫助容器同本地主機通訊、對接可用IP地址並執行用戶在定義鏡像時所執行的進程以運行應用程序。
在成功建立了一套容器以後,咱們隨後能夠將其運行在任何環境當中,而沒必要再作任何變動。
「容器」揭祕
打開容器,咱們會發現其中包含大量活動組件。容器的實現機制一直令我感到驚訝而好奇,特別是考慮到容器不存在任何抽象的基礎設施邊界。在閱讀了大量材料以後,我將結合本身的理解爲你們進行講解:)
「容器」這一術語其實只是個抽象概念,用於表述多種不一樣的相關特性,而其與原詞「container」的另外一含義「集裝箱」有着千絲萬縷的聯繫。下面就來一塊兒瞭解:
命名空間
命名空間爲容器提供對應的底層Linux系統視圖,即限制容器的查看與訪問範疇。當咱們運行一套容器時,Docker會建立多個命名空間供特定容器使用。
Docker會在內核中使用多種不一樣類型的命名空間,例如:
Docker將這些命名空間結合起來以隔離並建立容器。下面要講的則是控制組。
控制組
控制組(也被稱爲cgroups)屬於Linux內核中的一項功能,用於對一組進程的資源使用量(包括CPU、內存、磁盤I/O以及網絡等)進行隔離、排序與計數。這意味着cgroup可以確保Docker容器只使用其必需的資源——並在必要狀況下設置其所能使用的資源上限。另外,cgroups還可以確保單一容器不至於佔用太多資源並致使總體系統陷入癱瘓。
最後要說明的是Union文件系統:
隔離化Union File System
咱們在以前的Docker鏡像章節中已經解釋過了:)
這就是關於Docker容器的所有內容了(固然,實現細節纔是最麻煩的環節——例如如何管理不一樣組件間的交互)。
Docker的將來:Docker與虛擬機將並生共存
儘管Docker已經開始逐步成爲主流,但我認爲它不太可能對虛擬機形成真正的威脅。容器將繼續發展壯大,但虛擬機也仍然擁有適合本身的生存空間。
舉例來講,若是咱們須要在多臺服務器上運行多款應用程序,那麼最理想的辦法就是使用虛擬機。在另外一方面,若是咱們須要運行同一應用程序的多套副本,那麼Docker則擁有更多具體優點。
另外,儘管容器容許咱們將應用程序拆分紅多個功能組件,但這種分散化趨勢意味着咱們須要管理更多功能部件,即帶來更爲複雜的控制與協調任務。
安全同時也是Docker容器須要解決的一大難題——因爲各容器共享同一套內核,所以不一樣容器之間的屏障很是薄弱。與只須要調用主機虛擬機管理程序的虛擬機方案不一樣,Docker容器須要向主機內核發出系統調用,而這會帶來更龐大的攻擊面。出於安全性的考量,不少開發人員可能更傾向於選擇虛擬機——其由抽象化硬件進行隔離,從而顯著提高了彼此交互的難度。
固然,安全性與管理等難題必將隨着容器在生產環境下的進一步普及而獲得解決。就目前來說,關於容器與虛擬機孰優孰劣的議題已經成爲開發人員與運維人員間的平常爭論素材。
結束語
但願經過此文能讓你們掌握Docker、虛擬機的基本概念和區別。容器時代即將到來!
免費提供最新Linux技術教程書籍,爲開源技術愛好者努力作得更多更好:http://www.linuxprobe.com/