Docker 是最近在雲計算領域出現的新技術。目前,Docker 和以其爲表明的容器技術的熱度已經改過了以前的 OpenStack。Docker 以及其所表明的容器技術的流行,即便由於軟件技術的進步,更是因爲其符合雲計算對軟件領域所帶來新思想。在現在的互聯網和企業應用開發領域,微服務和 DevOps 是兩個思想頗爲深刻人心。而 Docker 技術的出現和其對整個容器技術及其生態圈發展的促進,解決了這個微服務和 DevOps 這兩個思想實踐中的不少難題,使得前面兩種思想大規模地實現成爲了可能。因此,咱們有必要地深刻了解一下 Docker 這個技術,看看它會對雲計算時代的軟件開發產生什麼樣的影響。java
本文將介紹以下內容:linux
至於 Docker 的用法,不在這裏作介紹。Docker 的入門使用,能夠前往 Docker 官網。各類高級用法、技巧、經驗,能夠前往技術網站 CSDN、InfoQ 和 CoreOS、Centurylink Labs 等這些使用 Docker 的雲計算廠商的網站。docker
容器技術有時會被稱爲輕量化虛擬技術。但不一樣於基於 Hypervisor 的傳統虛擬化技術,容器技術並不會虛擬硬件。容器自己和容器內的進程都是運行在宿主 Linux 系統的內核之上。但與直接運行的進程不一樣,運行在容器內的進程會被隔離和約束。從而以直接運行的高效實現了虛擬技術的大部分效果。ubuntu
容器技術並非一個新鮮事物,早在1979年出現的在 Unix 系統中的 chroot 即是容器技術的雛形。而隨後出現的 BSD Jail、Solaris Containers 和 OpenVZ 都算是容器技術的先驅。但容器技術開始普及倒是在2007年,這一年 Google 貢獻出 cgroups,而且從 2.6.4 開始,Linux 內核包含了這一組件,隨後容器技術開始逐漸普及。但容器技術真正大放異彩則要等到2013年 Docker 0.10 版本發佈。數組
在 Docker 出現以前,不只 Google 大量使用容器技術,國內的如淘寶也使用容器技術搭建了本身的應用平臺。影響力最大的開源 PaaS 解決方案 CloudFoundry,也在使用本身的容器解決方案 Warden。而 Docker 發佈以後,因其極有可能成爲將來企業應用、互聯網應用和雲計算應用的開發、部署的中心角色,因此獲得了幾乎全部的業界大佬的追捧,Google、VMware、微軟、RedHat 等等都已全力推進 Docker 技術的發展。同時,圍繞 Docker,出現了一系列以 CoreOS 爲表明的新技術。安全
Docker 的出現並不是創造了一個新的容器技術,而是在 LXC (LinuX Container)<sup>注1</sup>、cgroups、namespaces 技術之上所構建的一種技術:服務器
docker run [params] [image] [command (optional)]
Dockerfile
和 docker commit
兩種方式構建鏡像,而且提供了 Docker image registry 機制以保存和分發鏡像打一個比方,集裝箱(容器)對於遠洋運輸(應用運行)來講十分重要。集裝箱(容器)能保護貨物(應用),讓其不會相互碰撞(應用衝突)而損壞,也能保障當一些危險貨物發生規模不大的爆炸(應用崩潰)時不會波及其它貨物(應用)可是把貨物(應用)裝載在集裝箱(容器)中並非一件簡單的事情。而出色的碼頭工人(Docker)的出現解決了這一問題。它(Docker)使得貨物裝載到集裝箱(容器)這一過程變得垂手可得。對於遠洋運輸(應用運行)而言,用多艘小貨輪(虛擬機)代替原來的大貨輪(實體機)也能保證貨物(應用)彼此之間的安全,可是和集裝箱(容器)比,成本太高,但適合運輸某些重要貨物(應用)。網絡
Docker 主要有 Docker Hub 和 Docker 引擎組成。前者是Docker 官方提供的容器鏡像倉庫;後者運行在宿主機上,可分爲服務器端和客戶端兩部分。服務器端負責構建、運行和分發 Docker 容器等重要工做,客戶端負責接收用戶的命令和服務程序進行通訊。oracle
除了這兩部分,Dockerfile 也是不得不提的,它雖然不能算做一個獨立的組件,可是倒是 Docker 中很重要的部分。經過 Dockerfile,技術人員能夠建立本身的 Docker 容器鏡像。Dockerfile 起到了鏈接開發與運維的橋樑的做用,很是符合如今 DevOps 的潮流。運維
Docker Hub 是 Docker 官方所提供的一個鏡像倉庫。在運行 Docker 容器或構建本身的容器鏡像時,都會直接或間接地使用到 Docker Hub 中的鏡像。
Docker Engine 承載了 Docker 容器在宿主機上運行啓停、Docker 鏡像的構建等功能等功能。是咱們接觸最多的組件。接下來簡單介紹一下 Docker 常見的命令:
run
運行一個容器,若是鏡像不存在則先下載。經常使用參數有 -d
、-t
、-i
等pull
下載容器鏡像start/stop
啓動/中止一個 Docker 容器rm
刪除一個容器rmi
刪除一個容器鏡像commit
將容器中的修改提交至鏡像中logs
顯示容器運行的控制檯輸出build
從 Dockerfile 構建一個鏡像inspect
顯示容器運行參數,經過輸入一個 JSON 格式的值來顯示相應的結果images
顯示當前宿主機上的全部鏡像經過編寫 Dockerfile,咱們能夠構建本身的鏡像。看一個 Dockerfile 的例子:
FROM dockerfile/java:oracle-java8 MAINTAINER Lifan Yang <yanglifan@gmail.com> ADD device.jar /device.jar EXPOSE 8080 ENTRYPOINT java -jar /device.jar
FROM
指令的意思是說你的鏡像是基於一個什麼鏡像。dockerfile/java:oracle-java8
是一個鏡像的名字,它也是有一個基礎鏡像狗狗見。其實它也是基於例如 Ubuntu、CentOS 這樣的 Base 鏡像。關於 Base 鏡像的製做方法,Docker 官網上有介紹,須要專門的工具,這裏再也不做介紹。MAINTAINER
指令是可選的。ADD
指令是用來將一個文件或目錄添加到 Docker 鏡像中,前面是源文件,後面是目標文件。源文件必須使用相對路徑。EXPOSE
指令用來容器間暴露端口,其指定的端口也會被 -P
參數映射給宿主機的一個隨機端口上。ENTRYPOINT
能夠用來指定運行 Docker 容器時,在容器中執行的命令是什麼。若是須要運行多個命令,能夠經過 Supervisor 來執行。
除了這些指令,還有一些經常使用的指令。例如,用於在構建過程當中執行命令的 CMD
指令;用於在容器中設置環境變量的 ENV
指令。詳細請見 Dockerfile Reference
接下來要介紹 Docker 所使用的幾個重要技術:namespaces、cgroups、LXC 和 AUFS。
Linux 容器經過 Kernel 的 namespaces 技術,爲一個或一組進程建立獨立的 pid
、net
等 namespaces,從而與其它進程相互隔離。下面將介紹 namespaces 都會對哪些資源進行分組控制以實現相互隔離:
pid
namespace不一樣容器中進程是經過 pid namespace 隔離開的,且不一樣容器中能夠有相同 pid。具備如下特徵:
/proc
包含正在運行的進程,所以在容器中的 /proc
目錄只能看到本身 namespace 中的進程net
namespace若是僅僅隔離了進程空間,仍是會有問題。好比若是你在多個容器中運行 Apache 服務器,那隻能有一個服務器使用 80 端口。對此你有兩個選擇,讓每一個 Apache 服務器使用不一樣的端口,或者隔離網絡空間。
net
namespace 使得每一個容器都有本身的 lo
loopback 接口。同時,還有一個一般被命名爲 eth0
的網絡接口,經過這個接口,容器能夠和 host 或其它容器進行通訊。eth0
interface 會被分配一個 172.17.0.XXX 的 IP 地址,容器之間能夠經過這個 IP 地址相互通訊。
**圖3:**容器內部 ifconfig
命令查看的結果
同時,容器的這個 eth0
網卡在 Host 中的名字是一個相似 vethdfb7
的略顯古怪的名字。這個網卡會和 docker0
網卡橋接在一塊兒。
ipc
namespaceipc
namepace 對於不熟悉 Unix 的人(包括以前的我)吸引力不是很大。畢竟,如今進程之間的通訊多數是經過網絡實現的。但實際上,Unix ipc
有着十分普遍的應用,好比管道就是 ipc
的一種。對 ipc
就不作過多介紹了,總之 Linux Kernel namespaces 可讓不一樣容器的 ipc
相互隔離。
mnt
namespace如名所示,mnt
namespace 是處理掛載點的。mnt
namespace 可使不一樣容器擁有不一樣的掛載的文件系統和 root 目錄。在一個 mnt
namespace 掛載的文件系統只能被同一個 namespace 裏的進程所見。
uts
namespaceuts
namespace 用於控制 hostname 的隔離。
有了以上幾種隔離,一個容器就能夠對外展示出一個獨立計算機的能力,而且不一樣容器內的資源在操做系統層面實現了隔離。 然而不一樣 namespace 之間資源仍是相互競爭的,仍然須要相似 ulimit 來管理每一個容器所能使用的資源 - Docker 採用的是 cgroup。
參考文獻 1 http://blog.dotcloud.com/under-the-hood-linux-kernels-on-dotcloud-part
namespaces 對進程分組以實現資源隔離,但這隔離仍是不夠的。一個進程能夠經過佔用過分的硬件資源的方式去影響另外一個分組中的進程。因此,要想實現完善的資源隔離,不只要對資源分組,還要能對這一組內的進程所使用的資源進行約束。cgroups 就是用來實現這個目的的。cgroups 的全稱是 Control Groups,在 2003 年由 Google 的工程師實現,在 2007 年加入 Linux Kernel。cgroups 可限制進程對 CPU、內存、塊存儲和網絡的使用。這裏不對 cgroups 的使用方法做介紹,感興趣的同窗能夠參考以下:
雖然 cgroups 提供了對 IO 資源使用的約束的功能,但 Docker 目前(1.3)還沒有提供支持。
cgroups 能夠控制進程所能使用的內存和 swap 空間的大小。經過 -m
參數,Docker 能夠限制容器所能使用的最大內存數:
docker run -m 128m -d container_img cmd_name
Docker 能夠經過 cgroups 限制容器所能使用的 CPU 資源。在執行 docker run
命令時能夠經過參數 --cpu-shares
(-c
)、--cpuset
對 CPU 作出限制。
--cpu-shares
(-c
)-c
設置的是一個相對值,這個值將影響到此容器內的進程所能使用的 CPU 時間片。新運行的 Docker 容器默認使用 1024,對一個單獨的 Docker 的容器來講,這個值沒有任何意義。當啓動兩個 Docker 容器的時候,這兩個容器將平分 CPU 時間片。若是兩個容器,一個不指定 -c
,另外一個設置爲 -c 512
,那前者將使用大體 2/3 的計算能力,另外一個將使用大體 1/3 的計算能力。可是 -c
對容器所能使用 CPU 的運行頻率等沒有任何影響。
--cpuset
可以讓你指定 Docker 容器中的進程運行在第幾塊 CPU 上。後面跟一個數組或用逗號分隔的多個數字 0,1,2
。好比下列命令將會使你的容器運行在第一個 CPU 核心上。
docker run -i -t --cpuset 0 ubuntu:14.04
注:cgroups 使用的是一種僞文件系統形式的接口。這個僞文件系統實際存在於內存中,但映射在目錄中。用戶經過在這個目錄中寫入文件來對 cgroups 進行操做。
注:最新的 Docker 使用 BTRFS 替代 AUFS,但所要實現的功能相同 AUFS 的全稱是 Another Union File System。AUFS(包括其它 UFS)的一個重要能力是能使兩個目錄結構合二爲一。這有什麼用呢?Docker 的鏡像都是有多個層組成的,最上層是一個可讀寫的,而下面的層則是隻讀的。經過 AUFS 的目錄融合的能力,實現了既可隨意讀寫,又保證了下層的內容安全的目的。見下圖能夠有一個形象的認識。下圖中的 bootfs 層包含了 Linux Kernel。在其上是某個特定的 Linux 發行版本的不一樣於 Kernel 的文件層,在下圖中是 Debian。再往上有包含 emacs 和 Apache 的兩個層。這些層在容器運行時都是隻讀的。最上面就是容器運行時可讀寫的層了。上面的層能夠只讀訪問下面的層裏的文件。這樣的層次結構能夠經過 Dockerfile 來建立。
那這樣的一個結構有什麼樣的好處呢?主要有下面幾點:
節省磁盤存儲空間是由於不一樣的容器鏡像之間能夠共享相同的層。例如,兩個不一樣的 Java 應用的容器鏡像,它們都是基於 Ubuntu 14.04 和 JDK7。那在同一臺 Host 中,這兩個鏡像就會使用相同的 Ubuntu 14.04 和 JDK7 的層。
節省內存空間是由於 Linux 爲了加快磁盤訪問,會將一些磁盤上的文件加載到內存中。因此,節省磁盤空間的同時也就能夠間接地解釋內存的使用。
一樣,可共享的鏡像層能加快部署速度。由於相同的層不用被重複下載部署。
上傳可讀寫的層能夠對下面的只讀層中的文件作任意修改,但這實際上是 copy-on-write,因此,這種修改對下面的層實際上是安全的。
Docker 再好,單靠 Docker 自身是沒法知足互聯網和企業應用的各類複雜需求的。好在 Docker 的出現帶動了一些列技術的發展,造成了一個龐大的生態圈。這個生態圈中的產品可大體分爲以下幾類:
以 Google Kubernets 和 Apache Mesos 爲表明。主要解決基於容器組成分佈式集羣應用的管理工做,例如對容器的運行狀態的監控、容器自動化的故障恢復、基於容器的應用的擴容和縮容、服務發現。
以 CoreOS 和 Redhat Atomic 爲表明。它們拋棄了 Linux 上面傳統的包管理機制,而使用 Docker 做爲應用的運行平臺。同時精簡系統。CoreOS 還引入了 Ectd、Fleet 等組件以更好地支持分佈式系統。
PaaS 平臺不是什麼新鮮的概念,但卻一直處於發育不良的狀態。Docker 的出現給 PaaS 的發展帶來了新的機遇,Docker 使得 PaaS 應用的部署有了統一的格式。以 Flynn 和 Deis 爲表明的新的 PaaS 技術平臺都是以 Docker 爲基礎的。
現在的一臺服務器能夠輕鬆應付幾百上千的 Docker 容器同時運行在其中。能夠想見,在一個服務器集羣中的 Docker 容器會有多少。若是對這麼多的 Docker 容器所使用的網絡進行組織管理便成爲新的挑戰。在這個領域主要有 Pipework、Weave 和 Flannel 等技術
像 Puppet、Ansible 這樣的配置管理工具早在 Docker 出現以前就已被普遍使用,但 Docker 的出現給這些技術帶來了新的變化。是否能更好地支持對 Docker 容器集羣的配置管理決定了這些技術從此的發展。
本文簡單介紹了 Docker 出現的背景、意義,Docker 的組成和背後的技術以及其所帶動的生態圈。但做爲一個新出現並在快速發展的基礎性的技術,一兩篇文章顯然只能讓人有一個最基本的認識。同時,任何技術也都有其兩面性,Docker 做爲一個新技術在實踐中也存在這個很是多的問題。即使在國外,Docker 的應用也是出於起步階段。因此還有很長的路要走。可是從最近一年多的發展看,Docker 無疑是一個很是有生命力的技術,一定會在從此的一段時間內成爲一個熱門、主流的技術。
雖然 Docker 的出現更多地是改變了服務器應用的部署和運維。但做爲開發者來講,部署和運維的模型會對開發也產生很重大的影響,並且 Docker 的出現能使開發人員更好地參與到運維中來,這將促使應用更快、更好地迭代和發佈。
更重要地是,雲計算毫無疑問是將來互聯網和企業應用的發展方向,而 Docker 和其所表明的容器技術將在將來一段時間內成爲雲計算的基礎性技術。從這個角度來說,Docker 是咱們每個從業人員必須瞭解甚至熟練掌握的一門技術。