> 原文:http://www.infoq.com/cn/articles/docker-core-technology-preview
> 作者 肖德時
## 1\. 背景
### 1.1\. 由PaaS到Container
2013年2月,前Gluster的CEO Ben Golub和dotCloud的CEO Solomon Hykes坐在一起聊天時,Solomon談到想把dotCloud內部使用的Container容器技術單獨拿出來開源,然后圍繞這個技術開一家新公司提供技術支持。28歲的Solomon在使用python開發dotCloud的PaaS云時發現,使用 LXC(Linux Container) 技術可以打破產品發布過程中應用開發工程師和系統工程師兩者之間無法輕松協作發布產品的難題。這個Container容器技術可以把開發者從日常部署應用的繁雜工作中解脫出來,讓開發者能專心寫好程序;從系統工程師的角度來看也是一樣,他們迫切需要從各種混亂的部署文檔中解脫出來,讓系統工程師專注在應用的水平擴展、穩定發布的解決方案上。他們越深入交談,越覺得這是一次云技術的變革,緊接著在2013年3月Docker 0.1發布,拉開了基于云計算平臺發布產品方式的變革序幕。
### 1.2 Docker簡介

[Docker](http://www.docker.io/)?是 Docker.Inc 公司開源的一個基于 LXC技術之上構建的Container容器引擎,?[源代碼](https://github.com/dotcloud/docker)托管在 GitHub 上, 基于Go語言并遵從Apache2.0協議開源。 Docker在2014年6月召開DockerConf 2014技術大會吸引了IBM、Google、RedHat等業界知名公司的關注和技術支持,無論是從 GitHub 上的代碼活躍度,還是Redhat宣布在[RHEL7中正式支持Docke](http://server.cnw.com.cn/server-os/htm2014/20140616_303249.shtml)r, 都給業界一個信號,這是一項創新型的技術解決方案。 就連 Google 公司的 Compute Engine 也[支持 docker 在其之上運行](http://googlecloudplatform.blogspot.com/2013/12/google-compute-engine-is-now-generally-available.html), 國內“BAT”先鋒企業百度Baidu App Engine(BAE)平臺也是[以Docker作為其PaaS云基礎](http://blog.docker.com/2013/12/baidu-using-docker-for-its-paas/)。

Docker產生的目的就是為了解決以下問題:
1) 環境管理復雜: 從各種OS到各種中間件再到各種App,一款產品能夠成功發布,作為開發者需要關心的東西太多,且難于管理,這個問題在軟件行業中普遍存在并需要直接面對。Docker可以簡化部署多種應用實例工作,比如Web應用、后臺應用、數據庫應用、大數據應用比如Hadoop集群、消息隊列等等都可以打包成一個Image部署。如圖所示:

2) 云計算時代的到來: AWS的成功, 引導開發者將應用轉移到云上, 解決了硬件管理的問題,然而軟件配置和管理相關的問題依然存在 (AWS cloudformation是這個方向的業界標準, 樣例模板可[參考這里](https://s3-us-west-2.amazonaws.com/cloudformation-templates-us-west-2/LAMP_Single_Instance.template))。Docker的出現正好能幫助軟件開發者開闊思路,嘗試新的軟件管理方法來解決這個問題。
3) 虛擬化手段的變化: 云時代采用標配硬件來降低成本,采用虛擬化手段來滿足用戶按需分配的資源需求以及保證可用性和隔離性。然而無論是KVM還是Xen,在 Docker 看來都在浪費資源,因為用戶需要的是高效運行環境而非OS, GuestOS既浪費資源又難于管理, 更加輕量級的LXC更加靈活和快速。如圖所示:

4) LXC的便攜性: LXC在 Linux 2.6 的 Kernel 里就已經存在了,但是其設計之初并非為云計算考慮的,缺少標準化的描述手段和容器的可便攜性,決定其構建出的環境難于分發和標準化管理(相對于KVM之類image和snapshot的概念)。Docker就在這個問題上做出了實質性的創新方法。
### 1.3 Docker的Hello World
以Fedora 20作為主機為例,直接安裝docker-io:
~~~
$ sudo yum -y install docker-io
~~~
啟動docker后臺Daemon:
~~~
$ sudo systemctl start docker
~~~
跑我們第一個Hello World容器:
~~~
$ sudo docker run -i -t fedora /bin/echo hello world
Hello world
~~~
可以看到在運行命令行后的下一行會打印出經典的Hello World字符串。
## 2\. 核心技術預覽
Docker核心是一個[操作系統級虛擬化](http://en.wikipedia.org/wiki/Operating_system-level_virtualization)方法, 理解起來可能并不像VM那樣直觀。我們從虛擬化方法的四個方面:隔離性、可配額/可度量、便攜性、安全性來詳細介紹Docker的技術細節。
### 2.1\. 隔離性: Linux Namespace(ns)
每個用戶實例之間相互隔離, 互不影響。 一般的硬件虛擬化方法給出的方法是VM,而LXC給出的方法是container,更細一點講就是kernel namespace。其中pid、net、ipc、mnt、uts、user等namespace將container的進程、網絡、消息、文件系統、UTS("UNIX Time-sharing System")和用戶空間隔離開。
1)?pid namespace
不同用戶的進程就是通過pid namespace隔離開的,且不同 namespace 中可以有相同pid。所有的LXC進程在docker中的父進程為docker進程,每個lxc進程具有不同的namespace。同時由于允許嵌套,因此可以很方便的實現 Docker in Docker。
2)?net namespace
有了 pid namespace, 每個namespace中的pid能夠相互隔離,但是網絡端口還是共享host的端口。網絡隔離是通過net namespace實現的, 每個net namespace有獨立的 network devices, IP addresses, IP routing tables, /proc/net 目錄。這樣每個container的網絡就能隔離開來。docker默認采用veth的方式將container中的虛擬網卡同host上的一個docker bridge: docker0連接在一起。
3)?ipc namespace
container中進程交互還是采用linux常見的進程間交互方法(interprocess communication - IPC), 包括常見的信號量、消息隊列和共享內存。然而同 VM 不同的是,container 的進程間交互實際上還是host上具有相同pid namespace中的進程間交互,因此需要在IPC資源申請時加入namespace信息 - 每個IPC資源有一個唯一的 32 位 ID。
4)?mnt namespace
類似chroot,將一個進程放到一個特定的目錄執行。mnt namespace允許不同namespace的進程看到的文件結構不同,這樣每個 namespace 中的進程所看到的文件目錄就被隔離開了。同chroot不同,每個namespace中的container在/proc/mounts的信息只包含所在namespace的mount point。
5)?uts namespace
UTS("UNIX Time-sharing System") namespace允許每個container擁有獨立的hostname和domain name, 使其在網絡上可以被視作一個獨立的節點而非Host上的一個進程。
6)?user namespace
每個container可以有不同的 user 和 group id, 也就是說可以在container內部用container內部的用戶執行程序而非Host上的用戶。
### 2.2 可配額/可度量 - Control Groups (cgroups)
cgroups 實現了對資源的配額和度量。 cgroups 的使用非常簡單,提供類似文件的接口,在 /cgroup目錄下新建一個文件夾即可新建一個group,在此文件夾中新建task文件,并將pid寫入該文件,即可實現對該進程的資源控制。groups可以限制blkio、cpu、cpuacct、cpuset、devices、freezer、memory、net_cls、ns九大子系統的資源,以下是每個子系統的詳細說明:
1. blkio 這個子系統設置限制每個塊設備的輸入輸出控制。例如:磁盤,光盤以及usb等等。
2. cpu 這個子系統使用調度程序為cgroup任務提供cpu的訪問。
3. cpuacct 產生cgroup任務的cpu資源報告。
4. cpuset 如果是多核心的cpu,這個子系統會為cgroup任務分配單獨的cpu和內存。
5. devices 允許或拒絕cgroup任務對設備的訪問。
6. freezer 暫停和恢復cgroup任務。
7. memory 設置每個cgroup的內存限制以及產生內存資源報告。
8. net_cls 標記每個網絡包以供cgroup方便使用。
9. ns 名稱空間子系統。
以上九個子系統之間也存在著一定的關系.詳情請參閱[官方文檔](https://www.kernel.org/doc/Documentation/cgroups/)。
### 2.3 便攜性: AUFS
AUFS (AnotherUnionFS) 是一種 Union FS, 簡單來說就是支持將不同目錄掛載到同一個虛擬文件系統下(unite several directories into a single virtual filesystem)的文件系統, 更進一步的理解, AUFS支持為每一個成員目錄(類似Git Branch)設定readonly、readwrite 和 whiteout-able 權限, 同時 AUFS 里有一個類似分層的概念, 對 readonly 權限的 branch 可以邏輯上進行修改(增量地, 不影響 readonly 部分的)。通常 Union FS 有兩個用途, 一方面可以實現不借助 LVM、RAID 將多個disk掛到同一個目錄下, 另一個更常用的就是將一個 readonly 的 branch 和一個 writeable 的 branch 聯合在一起,Live CD正是基于此方法可以允許在 OS image 不變的基礎上允許用戶在其上進行一些寫操作。Docker 在 AUFS 上構建的 container image 也正是如此,接下來我們從啟動 container 中的 linux 為例來介紹 docker 對AUFS特性的運用。
典型的啟動Linux運行需要兩個FS: bootfs + rootfs:

bootfs (boot file system) 主要包含 bootloader 和 kernel, bootloader主要是引導加載kernel, 當boot成功后 kernel 被加載到內存中后 bootfs就被umount了. rootfs (root file system) 包含的就是典型 Linux 系統中的 /dev, /proc,/bin, /etc 等標準目錄和文件。
對于不同的linux發行版, bootfs基本是一致的, 但rootfs會有差別, 因此不同的發行版可以公用bootfs 如下圖:

典型的Linux在啟動后,首先將 rootfs 設置為 readonly, 進行一系列檢查, 然后將其切換為 "readwrite" 供用戶使用。在Docker中,初始化時也是將 rootfs 以readonly方式加載并檢查,然而接下來利用 union mount 的方式將一個 readwrite 文件系統掛載在 readonly 的rootfs之上,并且允許再次將下層的 FS(file system) 設定為readonly 并且向上疊加, 這樣一組readonly和一個writeable的結構構成一個container的運行時態, 每一個FS被稱作一個FS層。如下圖:

得益于AUFS的特性, 每一個對readonly層文件/目錄的修改都只會存在于上層的writeable層中。這樣由于不存在競爭, 多個container可以共享readonly的FS層。 所以Docker將readonly的FS層稱作 "image" - 對于container而言整個rootfs都是read-write的,但事實上所有的修改都寫入最上層的writeable層中, image不保存用戶狀態,只用于模板、新建和復制使用。

上層的image依賴下層的image,因此Docker中把下層的image稱作父image,沒有父image的image稱作base image。因此想要從一個image啟動一個container,Docker會先加載這個image和依賴的父images以及base image,用戶的進程運行在writeable的layer中。所有parent image中的數據信息以及 ID、網絡和lxc管理的資源限制等具體container的配置,構成一個Docker概念上的container。如下圖:

### 2.4 安全性: AppArmor, SELinux, GRSEC
安全永遠是相對的,這里有三個方面可以考慮Docker的安全特性:
1. 由kernel namespaces和cgroups實現的Linux系統固有的安全標準;
2. Docker Deamon的安全接口;
3. Linux本身的安全加固解決方案,類如AppArmor, SELinux;
由于安全屬于非常具體的技術,這里不在贅述,請直接參閱[Docker官](http://docs.docker.com/articles/security/)[方文檔](http://docs.docker.com/articles/security/)。
## 3\. 最新子項目介紹

我們再來看看Docker社區還有哪些子項目值得我們去好好研究和學習。基于這個目的,我把有趣的核心項目給大家羅列出來,讓熱心的讀者能快速跟進自己感興趣的項目:
1. Libswarm,是Solomon Hykes (Docker的CTO) 在DockerCon 2014峰會上向社區介紹的新“樂高積木”工具: 它是用來統一分布式系統的網絡接口的API。Libswarm要解決的問題是,基于Docker構建的分布式應用已經催生了多個基于Docker的服務發現(Serivce Discovery)項目,例如etcd, fleet, geard, mesos, shipyard, serf等等,每一套解決方案都有自己的通訊協議和使用方法,使用其中的任意一款都會局限在某一個特定的技術范圍內。所以Docker的CTO就想用libswarm暴露出通用的API接口給分布式系統使用,打破既定的協議限制。目前項目還在早期發展階段,值得參與。
2. Libchan,是一個底層的網絡庫,為上層 Libswarm 提供支持。相當于給Docker加上了ZeroMQ或RabbitMQ,這里自己實現網絡庫的好處是對Docker做了特別優化,更加輕量級。一般開發者不會直接用到它,大家更多的還是使用Libswarm來和容器交互。喜歡底層實現的網絡工程師可能對此感興趣,不妨一看。
3. Libcontainer,Docker技術的核心部分,單獨列出來也是因為這一塊的功能相對獨立,功能代碼的迭代升級非常快。想了解Docker最新的支持特性應該多關注這個模塊。
## 4\. 總結
Docker社區一直在面對技術挑戰,從容地給出自己的解決方案。云計算發展至今,有很多重要的問題沒有得到妥善解決,Docker正在嘗試讓主流廠商接受并應用它。至此,以上Docker技術的預覽到此告一段落,筆者也希望讀者能結合自己的實際情況,嘗試使用Docker技術。因為只有在親自體會的基礎之上,像Docker這樣的云技術才會產生更大的價值。
## 5\. 作者簡介
肖德時, Red Hat Engineering Service/HSS 內部工具組Team Leader,Nodejs開源項目nodejs-cantas Lead Developer。擅長企業內部工具的設計以及實現。開源課程Rails Starter的發起人。rubygem: lazy_high_charts的Maintainer。twitter賬號:xds2000,郵箱:xiaods@gmail.com
## 6\. 參考文獻:
1. [https://tiewei.github.io/cloud/Docker-Getting-Start/](https://tiewei.github.io/cloud/Docker-Getting-Start/)
2. [http://docs.docker.com/articles/](http://docs.docker.com/articles/)
3. [http://www.slideshare.net/shykes/docker-the-road-ahead](http://www.slideshare.net/shykes/docker-the-road-ahead)
4. [http://www.centurylinklabs.com/meet-docker-ceo-ben-golub/](http://www.centurylinklabs.com/meet-docker-ceo-ben-golub/)
5. [http://lwn.net/Articles/531114/](http://lwn.net/Articles/531114/)
6. [http://en.wikipedia.org/wiki/Aufs](http://en.wikipedia.org/wiki/Aufs)
7. [http://docs.docker.io/en/latest/terms/filesystem/](http://docs.docker.io/en/latest/terms/filesystem/)
8. [http://docs.docker.io/en/latest/terms/layer/](http://docs.docker.io/en/latest/terms/layer/)
9. [http://docs.docker.io/en/latest/terms/image/](http://docs.docker.io/en/latest/terms/image/)
10. [http://docs.docker.io/en/latest/terms/container/](http://docs.docker.io/en/latest/terms/container/)
11. [https://stackoverflow.com/questions/17989306/what-does-docker-add-to-just-plain-lxc](https://stackoverflow.com/questions/17989306/what-does-docker-add-to-just-plain-lxc)