Docker — 云時代的程序分發方式

Docker — 云時代的程序分發方式

要說最近一年云計算業界有什么大事件?Google Compute Engine 的正式發布?Azure入華?還是AWS落地中國?留在每個人大腦中的印象可能各不相同,但要是讓筆者來排名的話那么Docker絕對應該算是第一位的。如果你之前聽說過它的話,那么也許你會說“沒錯,就是它”,因為幾乎世界各地的開發、運維都在談論著Docker;如果你還沒聽說過Docker,那么我真的建議你花上10分鐘來閱讀本文。

1. Docker簡介

1.1. 什么是Docker?

Docker是一個重新定義了程序開發測試、交付和部署過程的開放平臺。Docker也是容器技術的一種,它運行于Linux宿主機之上,每個運行的容器都是相互隔離的,也被稱為輕量級虛擬技術或容器型虛擬技術。而且它有點類似Java的編譯一次,到處運行,Docker則可以稱為構建一次,在各種平臺上運行,包括本地服務器和云主機等(Build once,run anywhere)。

容器就是集裝箱,我們的代碼都被打包到集裝箱里;Docker就是搬運工,幫你把應用運輸到世界各地,而且是超高速。

Docker是開源軟件,代碼托管在GitHub上,使用Go語言編寫。Go可以稱得上是互聯網時代專門為開發分布式、高并發系統而生的編程語言。Docker也可以說是Go語言的一個殺手級應用,而且在Docker生態圈里很多軟件也都是使用Go語言編寫的。

1.2. Docker歷史

Docker項目始于2013年3月,由當時的PaaS服務提供商dotCloud開發,dotClound也是YCombinator S10的畢業生。盡管Docker項目很年輕,到現在也只有15個月而已,然而它的發展勢頭如此之猛已經讓很多人感嘆不已了。

2013年10月dotCloud公司名字也由dotCloud, Inc.改為Docker, Inc.,集中更多的精力放到了Docker相關的研發上。

1.3. Docker的技術基石

在進入Docker的世界之前,我們先來看一下Docker實現所依賴的一些技術。

實際上Docker的出現離不開很多Linux kernel提供的功能,甚至可以說Docker在技術上并沒有什么特別重大的創新之處,利用的都是已經非常成熟的Linux技術而已,這些技術早在Solaris 10或Linux Kernel 2.6的時候就有了。可以毫不夸張的說Docker就是“站在了巨人的肩膀上”。

下面我們就先來了解一下Docker主要利用的Linux技術。

1.3.1. 容器技術

容器(Container)有時候也被稱為操作系統級虛擬化,以區別傳統的Hypervisor虛擬技術。它不對硬件進行模擬,只是作為普通進程運行于宿主機的內核之上。

在容器中運行的一般都是一個簡易版的Linux系統,有root用戶權限、init系統(采用LXC容器的情況下)、進程id、用戶id以及網絡屬性。

容器技術在云計算時代已經被大量使用。Google公司的Joe Beda在今年5月做了一次題為《Containers At Scale — At Google, the Google Cloud Platform and Beyond》?注 1?的演講,在其中提到“Everything at Google runs in a container”,每周啟動容器次數竟然多達20億次。

注 1?https://speakerdeck.com/jbeda/containers-at-scale

很多PaaS平臺都是基于容器技術實現的,比如目前最成功的PaaS平臺Heroku。此外,還有比較著名的開源PaaS平臺Cloud Foundry的Warden以及Google的Lmctfy(Let Me Contain That For You)?注 2?等。

注 2 Let Me Contain That For You,?http://github.com/google/lmctfy

1.3.2. LXC

這也是在Linux下使用比較廣泛的容器方案。基本上我們可以認為Linux containers = cgroups(資源控制) + namespaces(容器隔離)。

LXC很成熟很強大,然而它卻不好使用,比如它不方便在多臺機器間移動,不方便創建管理,不可重復操作,也不方便共享等等,相對于開發人員來說,它只是系統管理員的玩具。Docker的出現很好的解決了這些問題,它將容器技術的使用成本拉低到了一個平民價格。

1.3.3. namespaces

這是用來為容器提供進程隔離的技術,每個容器都有自己的命名空間,比如pid/net/ipc/mnt/uts等命名空間,以及為容器提供不同的hostname。namespace能保證不同的容器之間不會相互影響,每個容器都像是一個獨立運行著的OS一樣。

1.3.4. cgroups

cgroups是一個Google貢獻的項目,它主要用來對共享資源的分配、限制、審計及管理,比如它可以為每個容器分配CPU、內存以及blkio等的使用限額等。cgroups使得容器能在宿主機上能友好的相處,并公平的分配資源以及杜絕資源濫用的潛在風險。

容器技術實現方案可以用下面的圖進行簡單說明。

圖 Docker如何和Linux內核打交道

上圖中的cgroups、namespaces和apparmor等都是Linux內核提供的功能。不管是傳統的LXC還是Docker的libcontainer,都使用了Kernel的這些功能來實現容器功能。

1.3.5. 聯合文件系統

聯合文件系統是一個分層的輕量、高性能文件系統。Docker之所以這么吸引人,很大程度上在于其在鏡像管理上所做出的創新。而聯合文件系統正是構建Docker鏡像的基礎。

AUFS(AnotherUnionFS)是一個分層的基于Copy On Write技術的文件系統,支持Union Mount,就是將具有不同文件夾結構的鏡像層進行疊加掛載,讓它們看上去就像是一個文件系統那樣。

1.4. 容器技術VS虛擬機技術

容器技術和Hypervisor技術雖然不屬于同一層次的概念,但是作為具有計算能力的應用運行載體來說,它們還是有一定的共通性和競爭關系,這里作此對比完全是為了加深讀者對容器技術的理解而已。

容器技術虛擬機技術占用磁盤空間小,甚至幾十KB(鏡像層的情況)非常大,上GB啟動速度快,幾秒鐘慢,幾分鐘運行形態直接運行于宿主機的內核上,不同容器共享同一個Linux內核運行于Hypervisior上并發性一臺宿主機可以啟動成千上百個容器最多幾十個虛擬機性能接近宿主機本地進程遜于宿主機資源利用率高低

比如開源PaaS實現軟件tsuru最初使用的是基于虛擬機的技術,創建一個應用程序需要5分鐘左右的時間,而在采用Docker之后,已經將這個時間縮短到了10秒鐘了?注 3?。

注 3 tsuru and docker by Andrews Medina?https://speakerdeck.com/andrewsmedina/tsuru-and-docker

1.5. 我們能用Docker干什么?

Docker可以應用在各種場景下,比如公司內部開發測試使用,或者作為共有或者私有PaaS平臺等。

現在PaaS平臺的發展已經非常成熟了,這里我們只羅列一些在開發中使用Docker技術可能會給我們帶來的益處。

1.5.1 在開發中

構建開發環境變得簡單

簡單包括幾個方面的意思

快速:只需?docker run?即可共享:通過Dockerfile或者Registry自動化:一切代碼化的東西都可以自動化統一:每個人的開發環境都是一模一樣的

設想我們要基于Nginx/PHP、MySQL和Redis開發,我們可以創建3個Docker鏡像保存到公司私有的Registry中去,每個開發人員使用的時候是需要執行?docker run redis?即可以享用自己獨有的Redis服務了,而且這3個容器不管從占用磁盤空間還是運行性能來說,都比虛擬機要好很多。

1.5.2. 在測試中

解決環境構建問題

有時候構建測試的環境是一項費時費力的工作,而Docker能讓這變得輕松。如果你的測試比較簡單的話,甚至直接拿開發構建的鏡像就可以開始了。

消除環境不一致導致的問題

“在我的機器上運行的好好的,怎么到你那里就不行了?”,我想超過半數的程序員都曾經說過類似的話。如果對導致這一問題的原因進行統計的話,我想排在第一位的應該非“環境不一致”莫屬了,這包括操作系統和軟件的版本、環境變量、文件路徑等。

使用Docker的話你再也不用為此煩惱了。因為你交付的東西不光是你的代碼、配置文件、數據庫定義,還包括你的應用程序運行的環境:OS加上各種中間件、類庫 + 你的應用程序。

1.5.3. 部署和運維

基于容器的部署和自動化

Docker定義了重新打包程序的方法。

Docker容器 + 用戶應用 = 部署單位(構件)

Docker可以看作是用代碼編寫出來的國際集裝箱,它可以把任何應用及相關依賴項打包成一個輕量、可移植(Portable)、自包涵的容器。

以前部署代碼都是代碼級別的,有了Docker,則可以進行容器級別的部署。這樣帶來的最大的好處就是開發者本地測試、CI服務器測試、測試人員測試,以及生產環境運行的都可以是同一個Docker鏡像。

快速進行橫向擴展

Docker容器的啟動速度很快,可以瞬間啟動大量容器,所以在非常適合在業務高峰期進行橫向擴展。這比傳統的啟動EC2實例或者物理機可要快多了。

天生的和云計算技術相結合

當然,由于Docker具有很好的移植性,所以它更強大的地方還在于和云環境結合使用。

Docker容器是可移植,或者說跨平臺。將來的應用部署可能是在本地進行打包(成Docker鏡像)然后傳送到云端運行,至于是AWS還是GCE這不是問題,Docker都能在其上運行。這樣不僅能在一定程度上解決vendor-lockin的問題,同時也使得在不同的云服務提供商之間遷移也變得簡單。尤其是未來在使用多云(multi-cloud)環境的時候,這將非常便利。

筆者認為基于IaaS + 容器技術的應用交付、部署方式將來一定會成為一種流行的方式。

進行Blue-green部署

「Blue-green deployment」這個詞最初出現在《Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation 》一書,后經ThoughtWorks的Martin Fowler發揚光大?注 4?。

注 4?http://martinfowler.com/bliki/BlueGreenDeployment.html

Blue-green deployment方法其實很簡單,就是保持兩套一樣的生產環境,而實際上只有一套環境真正的對外提供服務(圖中綠色環境),而另一套環境則處于待機狀態(圖中藍色)。部署的時候,我們會先上線到藍色環境中,如果測試沒有問題了,再將路由切換到新的服務上。

Blue-green部署能帶來如下好處。

最小化停機時間快速回滾hot standby

而未來的開發和部署和可能就會像下面這樣進行了。

① 開發人員將代碼push到Git倉庫② CI工具通過webhook得到最新代碼,構建Docker鏡像并啟動容器進行測試。③ 測試通過后將鏡像打標簽后push到私有鏡像Registry④ CI工具通知CD工具⑤ CD工具通過Mesos/Marathon等進行基于容器的部署⑥ 測試沒有問題后進行容器的切換(即Blue-green切換)

2. Docker架構解析

2.1. Docker整體結構

Docker是一個構建、發布、運行分布式應用的平臺(見下圖),Docker平臺由Docker Engine(運行環境 + 打包工具)、Docker Hub(API + 生態系統)兩部分組成。

圖 Docker平臺

從圖中我們可以看到,Docker的底層是各種Linux OS以及云計算基礎設施,而上層則是各種應用程序和管理工具,每層之間都是通過API來通信的。

Docker引擎

Docker引擎是一組開源軟件,位于Docker平臺的核心位置。它提供了容器運行時以及打包、管理等工具。

Docker引擎可以直觀理解為就是在某一臺機器上運行的Docker程序,實際上它是一個C/S結構的軟件,有一個后臺守護進程在運行,每次我們運行?docker?命令的時候實際上都是通過RESTful Remote API來和守護進程進行交互的,即使是在同一臺機器上也是如此。

Docker Hub

Docker Hub是一個云端的分布式應用服務,它專注于內容、協作和工作流。Docker Hub除了可以托管、下載、查找Docker鏡像之外,還提供了包括更管理、團隊協作、生命周期流程自動化等功能,以及對第三方工具和服務的集成。

Docker Engine有點像一個C/S結構的軟件,系統中有一個后臺守護進程,每次我們運行?docker?命令的時候實際上都是通過RESTful的Remote API來和守護進程進行交互的。

2.2. Docker鏡像(image)

2.2.1. Docker鏡像

Docker鏡像是Docker系統中的構建模塊(Build Component),是啟動一個Docker容器的基礎。

我們可以通過一個官方提供的示意圖來幫助我們來理解一下鏡像的概念。

Docker鏡像位于bootfs之上,實際上bootfs在系統啟動后會被卸載的。Docker鏡像(Images)是分層的,這得益于其采用的聯合文件系統,前面我們已經介紹過了。鏡像是有繼承(父子)關系的,每一層鏡像的下面一層稱為父鏡像,沒有父鏡像的稱為基礎鏡像(Base Iamge,其實叫做Root Image可能更確切,不過這可能容易和rootfs混淆)。

2.2.2. 鏡像倉庫

我們可以將Docker鏡像倉庫理解為Git倉庫。Dcoker鏡像倉庫分為遠程和本地,本地的概念好理解,而一般來說遠程倉庫就是Registry,包括官方的或者自建的私有Registry;我們通過?docker pull?和?docker push?命令在本地和遠程之間進行鏡像傳輸。

Docker鏡像的命名規則和GitHub也很像。比如我們自己創建的倉庫名稱都是類似?liubin/redis?這樣格式的,前面的?liubin?是用戶名或namespace,后面是倉庫名。

不過我們前面已經看到運行的ubuntu鏡像的時候是倉庫名就是?ubuntu?,而不帶用戶名前綴,這是表明它是由官方制作的,或者由官方認可的第三方制作的鏡像。我們可以認為官方倉庫提供的鏡像都是安全的、最新的,所以也可以放心使用。

2.3. Docker容器(Container)

容器是一個基于Docker鏡像創建、包含為了運行某一特定程序的所有需要的OS、軟件、配置文件和數據,是一個可移植的運行單元。在宿主機來看,它只不過是一個簡單的用戶進程而已。

容器啟動的時候,Docker會在鏡像最上層掛載一個read-write的文件系統,即上圖中標記為writable的Container層,容器將跑在這個文件系統上。這層可寫的文件系統是容器中才有的概念,如果我們對此容器進行commit操作,那么該層文件系統則會被提交為一個新的只讀的鏡像層,并位于鏡像層的最上面的。

我們可以認為Docker鏡像是“靜”的".exe"文件,只在“硬盤”上;而容器是“動”的,是在“內存中”的,要想啟動一個容器,需要先把".exe"裝載到內存。

鏡像和容器具有如下的轉換關系:

鏡像 ->?docker run?-> 容器容器 ->?docker commit?-> 鏡像

有時候我們經常會將兩個名稱混用,不過這并不會影響我們的理解。

2.4. Docker Registry

Docker Registry是Docker架構中的分發模塊,它用來存儲Docker鏡像,我們可以將它理解為GitHub。

Docker Hub是一個官方的Docker Registry,也是Docker鏡像的默認存儲位置。

當然從安全管理的角度上來說,我們可能更愿意在自己公司內部托管一個私有的Docker Registry,這可以通過使用Docker官方提供的Registry?注 5?軟件實現。

注 5 Docker Registry?https://github.com/dotcloud/docker-registry

運行私有Registry非常簡單,這也是一個典型的Docker風格的應用發布例子。

docker run –p 5000:5000 registry

3. 使用Docker

3.1. 初識容器

3.1.1. 創建并啟動容器

這里我們假定各位讀者已經在自己的機器上安裝好了Docker。Docker主要的命令就是?docker?了,它的參數很多,關于它的具體使用方法,可以參考官方的文檔?注 6?,這里我們只簡單的介紹其中一些常用的用法。

注 6?https://docs.docker.com/reference/commandline/cli/?和?https://docs.docker.com/reference/run/

啟動一個容器很簡單,我們只需要運行?docker run?命令就可以了?注 6?。

注 6 為了方便區分,本文中運行命令的時候如果提示符為?$?,表示實在宿主機(Ubuntu)中,如果是?#?,則表示是在Docker容器中

$ sudo docker run -i -t ubuntu /bin/bash

Unable to find image 'ubuntu' locally

Pulling repository ubuntu

e54ca5efa2e9: Pulling dependent layers

... 省略 ...

6c37f792ddac: Download complete

... 省略 ...

root@81874a4a6d2e:/#

docker run?命令會啟動一個容器。參數?ubuntu?指定了我們需要運行的鏡像名稱,后面的?bash?則指定了要運行的命令,注意這個命令是容器中的命令,而不是宿主機中的命令。參數?-i?用來為容器打開標準輸入以和宿主機進行交互,?-t?則會為容器分配一個終端。

在第一次啟動某鏡像的時候,如果我們本地還沒有這個鏡像,則Docker會先從遠程倉庫(Docker Hub)將容器的鏡像下載下來,下載完成之后才會啟動容器。

注意Docker里有一個很重要的概念就是容器ID或者鏡像ID,比如這個例子里的?e54ca5efa2e9?。這個ID是一個容器或者鏡像的唯一標識,它的長度為64位,不過很多時候都可以簡寫為12位,這也和Git很像。

3.1.2. 讓Docker容器在后臺運行

這時候我們可以使用?-d?參數來通過守護模式啟動一個容器,這樣容器將會在后臺一直運行下去。這非常適合運行服務類程序。如果需要,我們可以再通過?docker attach?命令連接到運行中的容器。

3.1.3. 常用命令

docker ps

docker ps?用來查看正在運行中的容器。

從下面的輸出結果我們可以看出該容器狀態(STATUS列)為已經停止執行,且沒有錯誤(Exited后面的狀態碼)。

$ sudo docker ps -a

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

60bab6f881e5 ubuntu:latest /bin/bash 14 minutes ago Exited (0) 5 seconds ago agitated_hopper

docker ps?命令的常用參數(及組合)如下。

-a?: 查看所有容器,包括已經停止運行的。-l?: 查看剛剛啟動的容器。-q?: 只顯示容器ID-l?-q?: 則可以返回剛啟動的容器ID。

docker stop/start/restart

docker stop?用來停止運行中的容器,同時你還可以用?docker start?來重新啟動一個已經停止的容器。

docker restart?可以重啟一個運行中的容器。這就相當于對一個容器先進行?stop?再?start?。

3.2. 深入了解Docker鏡像

在對Docker容器有一個簡單的感性認識之后,我們再來深入了解一下Docker鏡像的概念。

Docker鏡像實際上就是一個tarball,它是一個能完整運行的OS系統,這非常像OS或VM鏡像。它里面有基礎OS、各種軟件包及類庫等。我們啟動一個容器,相當于是啟動了一個“基礎OS”。

3.2.1. 標簽(Tag)

我們還可以為鏡像打標簽,這也和Git非常相似。其實你也可能在前面留意到了,?docker images?的輸出中有一列就是TAG的。我們在執行?docker build?或者?docker commit?的時候都可以同時為倉庫名稱指定一個TAG,格式為?user_name/repo_name:tag?,如果沒有指定這個TAG,則默認為?latest?。

3.2.2. 常見鏡像操作

這里我們再介紹一下對鏡像常見的一些操作。

查看本地鏡像列表

docker images?命令用來列出當前系統中的所有本地鏡像,即我們已經通過?docker run?或者?docker pull?下載下來的鏡像,鏡像文件保存在本地的?/var/lib/docker?文件夾下。

下載鏡像到本地

只需要運行?docker pull?命令即可,命令非常簡單,問題在于你的網路速度和連通性。

刪除鏡像

docker rmi?用來從本地倉庫中刪除一個不再需要的鏡像,即"rm image"的縮寫。

3.3. 構建鏡像

我們可以創建自己的Docker鏡像,在我們的日常工作中會經常進行鏡像構建操作。構建Docker鏡像非常簡單,而且方法也有幾種。

3.3.1. 手工創建

這個方法最簡單直接的方法。其流程是啟動一個容器,在里面進行一些列安裝、配置操作,然后運行?docker commit?命令來將容器commit為一個新鏡像。

$ sudo docker run -t -i ubuntu bash

root@c4be1df52810:/# apt-get update

root@c4be1df52810:/# apt-get -y install redis-server

root@c4be1df52810:/# exit

通過下面的命令得到剛才容器的ID號并進行commit操作。

$ sudo docker ps -q -l

c4be1df52810

$ sudo docker commit -m="manually created image" -a="bin liu <liubin0329@gmail.com>" -run='{"CMD":["/usr/bin/redis-server"], "PortSpecs": ["6379"]}' c4be1df52810 liubin/redis:manually

Warning: '-run' is deprecated, it will be removed soon. See usage.

744ce29b2fcf0ad7ad8b2a89c874db51376c3fdd65d1f4f0c6f233b72f8c3400

注意上面的警告信息,在?docker commit?命令指定?-run?選項已經不被推薦了,這里為了說明這個例子而故意使用了這個選項。建議創建鏡像還是使用Dockerfile的方式,即能將創建過程代碼化、透明化,還能進行版本化。

再次運行?docker images?命令,就應該能看到我們剛才通過?docker commit?命令創建的鏡像了(鏡像ID為744ce29b2fcf?,鏡像名為?liubin/redis:manually?)。

3.3.2. 使用Dockerfile文件

使用Dockerfile構建Docker鏡像

這是一個官方推薦的方法,即將構建鏡像的過程代碼化,比如要安裝什么軟件,拷貝什么文件,進行什么樣的配置等都用代碼進行描述,然后運行?docker build?命令來創建鏡像文件。官方的自動構建即是基于保存在GitHub等代碼托管服務上的Dockerfile進行的。Dockerfile即是具體的用于構建的配置文件名,也是這類文件的類型名稱。

使用Dockerfile構建Docker鏡像非常簡單,我們只需要創建一個名為?Dockerfile?的文件,并編寫相應的安裝、配置腳本就可以了。我們還是以上面安裝Redis服務為例,看看如何使用Dockerfile構建一個鏡像。

首先,創建一個?redis?文件夾(文件夾名任意,無任何限制),并進入該文件夾,然后創建一個?Dockerfile?文件。這個文件的文件名是固定的,其內容如下。

FROM ubuntu

MAINTAINER bin liu <liubin0329@gmail.com>

RUN apt-get update

RUN apt-get -y install redis-server

EXPOSE 6379

ENTRYPOINT ["/usr/bin/redis-server"]

Dockerfile文件的語法非常簡單,每一行都是一條指令,注釋則以?#?開頭。每條指令都是“指令名稱 參數”的形式,指令名稱一般都是大寫。比如?FROM?指令表明了我們的鏡像的基礎鏡像(嚴格來說叫父鏡像,我們的所有操作都將以此鏡像為基礎),這里是?ubuntu?,但實際上它可以是存在的任何鏡像,比如?liubin/ruby?。?RUN?指令則用來在構建過程中執行各種命令、腳本,比如這里是?apt-get?命令,你也可以指定一個很復雜很長的腳本文件路徑。AUFS有42層文件系統的限制?注 7?,這時候我們可以通過在?RUN?指令中執行多條命令,即?cmd1?&&?cmd2?&&cmd3?&&?...?這種形式就可以可避免該問題了。?EXPOSE?表示此鏡像將對外提供 端口的服務。?ENTRYPOINT?則指定了啟動該鏡像時的默認運行程序。

注 7?https://github.com/dotcloud/docker/issues/1171

具體的Dockerfile語法在官方網站?注 8?有詳細說明,相信花個10分鐘就能通讀一遍,這里唯一比較容易混淆的就是ENTRYPOINT?和?CMD?指令了,關于它們的區別,還是留作每位讀者自己的課題去研究一下吧。

注 8?https://docs.docker.com/reference/builder/

Dockerfile準備好了之后,運行?docker build?命令即可構建鏡像了。

$ sudo docker build -t liubin/redis:dockerfile .

這里?-t?表示為構建好的鏡像設置一個倉庫名稱和Tag(如果省略Tag的話則默認使用?latest?)。最后的一個?.表示?Dockerfile?文件的所在路徑,由于我們是在同一文件夾下運行?docker build?命令,所以使用了?.?。

由于篇幅所限,這里我們就省略了?docker build?命令的輸出。不過如果你親自動手執行?docker build?命令的話,那么從它的輸出應該很容易理解,Dockerfile里的每一條指令,都對應著構建過程中的每一步,而且每一步都會生成一個新的類似容器的哈希值一樣的鏡像層ID。也正是這些層,使得鏡像能共享很多信息,并且能進行版本管理、繼承和分支關系管理等。這除了能節省大量磁盤空間之外,還能在構建鏡像的時候通過使用已經構建過的層(即緩存)來大大加快了鏡像構建的速度。比如在我們在使用Dockerfile進行構建鏡像時,如果在某一步出錯了,那么實際上之前步驟的操作已經被提交了,修改Dockerfile后再次進行構建的話,Docker足夠聰明到則會從出錯的地方開始重新構建,因為前面的指令執行結構都已經被緩存了。

如果你使用?docker history?命令來查看該鏡像的歷史信息,你會發現它的輸出和?docker build?的記錄是相匹配的,每一條Dockerfile中的指令都會創建一個鏡像層。此命令還能查看每個鏡像層所占空間大小,即?SIZE?列的內容。比如本例中?MAINTAINER?這樣指令,實際上它只是關于鏡像的元數據,并不占用額外的磁盤空間,所以它的層大小為0字節。而?RUN apt-get?-y install redis-server?創建的層則會在鏡像中增加文件,所以是需要占用磁盤空間的。

自動構建(Automated Builds)

Docker Hub的目的之一就是要成為應用程序交換的中轉站,它還支持自動構建功能。自動構建的Dockerfile可以托管在GitHub或者Bitbucket上,當我們將代碼提交并push到托管倉庫的時候,Docker Hub會自動通過webhook來啟動鏡像構建任務。

配置自動構建很簡單,只需要在Docker Hub中綁定GitHub或者Bitbucket賬號就可以了,如何具體操作這里不做詳細說明了。

3.3.3. 使用Packer

Packer?注 10?是一個通過配置文件創建一致機器鏡像(identical machine images)的非常方便的工具。Packer同樣出自Vagrant的作者Mitchell Hashimoto之手。它支持虛擬機VirtualBox和VMWare等虛擬機軟件,以及Amazon EC2、DigitalOcean、GCE以及OpenStack等云平臺,最新版的Packer也增加了對Docker的支持。

注 10?http://www.packer.io/

Packer的使用也比較簡單,這里我們就舉例說明了,讀者可以自己試一下。

3.4. 發布鏡像

如果你愿意,還可以將在本地制作鏡像push到Docker Hub上和其他人分享你的工作成果。

首先你要有一個Docker Hub賬號并已經為登錄狀態,這樣才能往Docker Hub上push鏡像文件。注冊Docker Hub賬號只能通過網站注冊?注 11?,這里我們假設各位讀者已經擁有Docker Hub了賬號。

注 11?https://hub.docker.com/

登錄Docker Hub通過?docker login?命令。

登錄成功后,我們就可以push鏡像了。注意這里我們沒有指定Tag,Docker知道如何去做。

$ sudo docker push liubin/redis

我們前面說過,鏡像文件是分層的,很多鏡像文件可以共用很多層。比如我們這次往服務器push鏡像的時候,實際push的只有一層(?744ce29b2fcf?)而已,這是因為我們的鏡像文件是基于?ubuntu?這個base鏡像創建的,而ubuntu?鏡像早已經在遠程倉庫中了。

我們在層?744ce29b2fcf?中對應的操作是?bash?命令,并在容器中安裝了Redis。而這次修改只有不到6M的容量增加,而如果只是修改配置文件的話,那么一次push操作可能只需要耗費幾K的網絡帶寬而已。

4. DockerCon14總結

首屆Docker大會(DockerCon14)于當地時間6月9日~6月10日在舊金山舉行。相對于計劃中的500個參會名額,最終有超過900人報名,并提交了超過150個演講申請。

關于這次Docker大會的更多信息可以參考其官方網站:?http://www.dockercon.com/。

4.1. Docker官方發布的產品和服務

4.1.1. Docker 1.0的發布及商業支持

在這次大會上最重要的事情莫過于Docker 1.0的發布了。Docker 1.0已經可以在Red Hat、Debian、Ubuntu、Fedora、SuSE等主流Linux系統下運行,在功能、穩定性以及軟件質量上都已經達到了企業使用的標準,文檔也更加系統、完善。并且提供了Docker Hub云服務,方便開發者和企業進行應用分發。最重要的是Docker, Inc.還宣布了對Docker的商業支持,尤其是對Docker 1.0版本的長期支持。此外,Docker, Inc.還會提供Docker相關的培訓、咨詢等工作。

4.1.2. Docker Engine + Docker Hub

同時從1.0開始,Docker的架構也發生了較大的變化。Docker已經從單一的軟件轉變為了一個構建、發布、運行分布式應用的平臺。

新的Docker平臺由Docker Engine(運行環境 + 打包工具)、Docker Hub(API + 生態系統)兩部分組成。

Docker引擎

Docker引擎是一組開源軟件,位于Docker平臺的核心位置。它提供了容器運行時以及打包、管理等工具。

Docker Hub

Docker Hub是一個云端的分布式應用服務,它專注于內容、協作和工作流。

Docker Hub可以看作是原來Docker index服務的升級版。Docker Hub除了可以托管Docker鏡像之外,還提供了包括更管理、團隊協作、生命周期流程自動化等功能,以及對第三方工具和服務的集成。

在Docker, Inc.看來,典型的基于Docker Hub的軟件開發生命周期為:在本地基于Docker引擎開發 -> 打包應用程序 -> 將應用程序push到Docker Hub -> 從Docker Hub上下載此應用鏡像并運行。它將鏡像構建的任務交給Dev,將鏡像部署的任務交給Ops。

4.1.3. 新組件

Docker Engine也有了一些新的變化,而部分功能實際上早在Docker 0.9就開始提供了。如果你還在運行Docker 0.8及其以前的版本的話,那么還是及早升級的比較好。

libswarm

libswarm是一個"toolkit for composing network services”。它定義了標準接口用于管理和編配一個分布式系統,并提供了一致的API。libswarm打算支持各種編配系統,雖然它看上去更像個高層接口封裝的API而已。

libcaontainer

libcontainer是一個容器的參考實現,它通過Go語言實現來使用Linux的命名空間等技術,而不需要額外的外部依賴。

實際上在Docker 0.9的時候這個模塊就已經分離出來了,到了1.0的時候,此模塊成為了獨立項目并且可以單獨使用。并且從0.9版本的時候開始Docker就已經開始就采用libcontainer來代替LXC作為默認的容器實現方式了,LXC變成了可選項之一。

libchan

libchan現在是Docker的標準通信層,被稱為網絡上的go channel,普通的Go channel只能運行在單機上,而libchan可以跨Unix socket或純TCP/TLS/HTTP2/SPDY/Websocket等運行。使用libchan,可以非常方便的進行任意結構的消息傳遞、實時雙工異步通信、并發編程及同步等。

最后我們再從下面的這張圖,更形象的認識一下這三個工具的作用及關系。

4.2. 大公司的熱情

如果看一下演講嘉賓列表?注 13?,你一定會感嘆這陣容太豪華了。不錯,很多演講嘉賓都來自大型互聯網公司,比如Facebook、Twitter、Google、Heroku、Yelp以及Group等,很多還都是VP、CTO等高級別的管理人員,可見這次大會規格之高,分量之重。并且他們中的很多人還都進入到了Docker治理委員會。

注 13?http://www.dockercon.com/speakers.html

4.2.1. Google

前面我們已經介紹了Google公司內部的服務都是跑在容器之中的,Google對Docker也表現出了相當濃厚的興趣。除了他們負責基礎設施的VP Eric Brewer進行了主題為《Robust Containers》的演講之外,他們還介紹了自己開源容器管理軟件Kubernetes和對容器資源進行監控的cAdvisor。

4.2.2. Red Hat

Red Hat Enterprise Linux 7版將內置Docker,雖然版本還是0.11,不過很快就會升級的。另外Atomic項目也是Red Hat主導開發的。

4.3. 其它感受

其他一些筆者認為比較有意思的就是使用基于Mesos工具群來對容器進行集群管理了。比如Twitter和Groupon都做了使用Mesos + Aurora/Marathon + ZooKeeper在數據中心進行資源分配和管理的分享;甚至在Twitter看來,數據中心也可以看做是一臺計算機,Mesos就是這臺計算機的OS。

另外就像我們前面在Docker使用場景中介紹過的那樣,很多公司都在使用Docker進行持續集成。

5. Docker現狀及展望

在本節我們將會站在一個開放的角度和更高的層次來審視一下Docker的現狀,包括其問題點,以及對Docker將來的可能性做一些膚淺的推測。

5.1. 生態系統

Docker的發展離不開其生態系統?注 14?,我們學習Docker也同樣需對其生態系統有所了解。我們可以從下面三點來審視一下Docker當前的發展狀況。

注 14 關于Docker的生態環境,大家也可以參考網上有人制作的一份思維導圖。http://www.mindmeister.com/389671722/docker-ecosystem

5.1.1. 廠商支持

前面我們已經說過了,包括RedHat等在內的Linux發行商以及Google、AWS、Rackspace等云服務提供商都表示對Docker非常濃厚的興趣,甚至已經進行了非常深入的實踐。從這一點上來說,Docker有非常好的政治背景。

5.1.2. 開源項目

圍繞Docker的開源項目就更多了,主要有以下幾類,我們將挑選出一些比較有意思且開發較活躍的項目進行簡單介紹。

PaaS平臺

PaaS平臺大多基于容器技術,Docker天生就適合做PaaS。

Flynn

Flynn是一個高度模塊化的下一代開源PaaS實現。Flynn分為兩層,Layer 0是底層,也叫資源層,基于Google的Omega論文?注 15?開發,這一層也包括服務發現。Layer 1則用來進行部署、管理應用程序。Flynn目前開發比較活躍,是一個值得關注的開源項目,而且今年夏天很可能就會發布1.0的版本了。

注 15?http://eurosys2013.tudos.org/wp-content/uploads/2013/paper/Schwarzkopf.pdf

https://flynn.io/

Deis

Deis是一個支持共有和私有PaaS的開源實現。它支持運行使用Ruby, Python, Node.js, Java, PHP和Go等語言進行應用開發,并能部署到AWS, Rackspace和DigitalOcean等云上。

http://deis.io/

CI/CD(持續集成/持續部署)

由于Docker的沙箱性、創建速度快等特性,它與生俱來也適合進行CI/CD。很多基于Docker的CI/CD開源方案和服務如雨后春筍般的涌現出來。

Drone

開源的支持各種語言的CI工具,并且提供了CI/CD服務Drone.io

https://drone.io/

Strider CD

開源的CI/CD方案,集成GitHub。

http://stridercd.com/

私有倉庫托管(Registry)/容器托管

這類服務主要進行私有倉庫的托管,根據用戶的托管倉庫數量收費。Doccker Hub也提供私有倉庫的收費套餐。

Quay

Quay除了能托管私有鏡像之外,還能和GitHub集成,使用Dockerfile進行鏡像構建。

https://quay.io/

Shippable

Shippable支持Github和Bitbucket,并且提供100%免費的服務,包括私有倉庫。

https://www.shippable.com/

Orchard

Orchard也是一個和StackDock類似的Docker托管服務,它提供了便捷的命令行工具來運行各種Docker命令。同時它也提供免費的私有Registry服務,前面介紹的Fig工具就是此公司開發的。

https://www.orchardup.com/

筆者認為傳統的云計算服務提供商除了在云主機上提供對容器的支持之外,說不定將來還會提供專門托管容器的服務。

開發管理工具

軟件工程師天生就是閑不住和想盡一切辦法要提高自己效率的一群人。這里我們簡單介紹兩個方便進行Docker開發的工具。

Shipyard

Shipyard是一個Docker鏡像和容器管理工具,除了基本的鏡像構建,容器啟動等功能之外,它還具有在瀏覽器中attach到容器的功能,并通過hipache?16?來進行容器之間的連接。同時它也支持跨節點的Docker管理和容器Metrics采集。

注 16 Hipache: a distributed HTTP and websocket proxy?https://github.com/dotcloud/hipache

https://github.com/shipyard/shipyard

Fig

Fig是一個為了提高基于Docker開發的效率而創建的工具,它通過一個配置文件來管理多個Docker容器,非常適合組合使用多個容器進行開發的場景。

http://orchardup.github.io/fig/index.html

5.1.3. 社區

Docker開發社區非常活躍,除了35名全職員工(外加一只烏龜)之外,還有450名左右的外部代碼貢獻者。到目前Docker Hub已經擁有超過16000多個應用,在GitHub上也有超過7000個Docker相關的項目,其中不乏很多受關注度非常高的項目。

在Twitter上,科技媒體上以及個人Blog上,每天都能看到很多關于Docker的內容。

線下社區活動也在蓬勃展開中。在世界范圍內除了南極洲,Docker Meetup已經遍布35個國家100多個城市,北京在今年3月8日舉行了國內第一次的Docker Meetup,當時有超過40人報名參加。而且第二次北京Docker Meetup將在七月中舉行,目前正在緊鑼密鼓的籌備之中。

5.2. 運用中的問題點

雖然Docker很火,有時候我們也需要反過來看看它還有哪些不令我們滿意的地方,或者說在使用上還存有疑慮。當然這里的問題都是筆者個人主觀看法,只是非常片面的一部分,各位讀者一定要帶著批判性的思維去理解它。

5.2.1. Debug、調優

查看日志可能是最簡單直接的方式了。當然也有很多人都會在Docker容器中運行一個SSHD服務,然后通過SSH登錄到容器中去,不過不建議使用這種方法。

官方推薦使用nsenter?注 17?工具來完成類似的工作,通過它可以進入到指定的namespace中并控制一個容器。

注 17?https://github.com/jpetazzo/nsenter

5.2.2. 數據管理

這里所說的數據包括數據庫文件,Log,用戶上傳的文件等。

在容器中要想處理數據文件,可能最簡單的方式就是通過共享卷標來實現,即?docker run?-v?。但是隨之帶來的問題是既然是文件,都存在備份問題,如何備份?用ftp或者在容器和宿主機之間共享文件夾的方式?而且隨著容器數量的增多,對共享卷標的管理也勢必會更復雜。

筆者認為理想的解決方法就是使用云服務,比如數據庫使用RDS,文件使用S3。如果不想使用云服務,則可以考慮自己通過FastDFS等實現自己的“云存儲”。Log則通過fluentd/logstash進行集計再用Graphite/Kibana等進行可視化。

5.2.3. 如何和配置管理工具配合使用

到底在容器時代,還需不需要傳統的Puppet或Chef這樣的配置管理工具?當然,從配置管理工具的角度來說,他們都不會放棄對Docker的支持,比如Puppet就已經增加了對Docker(安裝、管理鏡像和容器)的支持。

但隨著不可變基礎設施的普及?注 18?,冪等性將不再重要,因為我們的容器只需要配置一次。要對容器做出修改,可能只需要修改Dockerfile/manifest/recipe文件重新Provisioning即可。而且也不需要在容器內部安裝任何agent,這樣的話類似Ansible這樣純SSH的配置管理工具比較適合對Docker進行配置。甚至還可能出現專門為Docker的更簡單的配置管理工具。

注 18 筆者個人偏見而已

5.2.4. 安全性

是軟件就會存在bug,包括安全漏洞,Docker也不例外。就在今年6月份,Docker剛爆出了一個容器逸出的漏洞?注 19?。不管是Hypervisor技術還是容器技術,安全問題始終都是一個不可避免的話題,雖然它們出問題的幾率要比中間件軟件(Apache,Nginx、Tomcat)和軟件框架(Struts、Rails)等的概率要小很多。

注 19?http://blog.docker.com/category/security-2/

事后Docker, Inc.還是比較積極的面對了這件事,除了及時披露詳細情況之外,還著重強調了他們的安全政策。

5.2.5. 有狀態和無狀態容器

在不可變基礎設施(Immutable Infrastructure)里,一切都可以分為有狀態(stateful)的和無狀態(stateless)的,容器也不例外。容器似乎更適合跑無狀態的服務,然而業內對如何分別對待這兩種服務還沒有太好的最佳實踐。

5.3. 對Docker展望

最后再容筆者斗膽對Docker的將來做一些展望。除了Docker本身自己會蓬勃發展之外,圍繞Docker的生態圈必將更加成熟和強大。

5.3.1. 集群管理(Orchestration)和服務發現(Service Discovery)

相對于對單臺機器進行Provisioning而言,云環境下則需要對多臺機器進行Orchestration。Orchestration這個詞翻譯過來就是編排、編配的意思,我們也可以理解為集群管理。它主要由兩部分工作組成:

監控服務器,發現變化(軟硬件異常、網絡異常、正常變更等)根據監視事件采取相應的行動。

服務發現

在松耦合的分布式環境下,應用程序不一定跑在同一臺機上,甚至是跨越數據中心的。這時候服務發現就顯得格外重要了。

Zookeeper

Chubby?注 20?可以稱得上是很多服務發現、集群管理軟件的鼻祖了,比如Zookeeper?注 21?,這些軟件都提供數據存儲、leader選舉、元數據存儲、分布式鎖、事件監聽(或watch,監視)等功能。

注 20?http://research.google.com/archive/chubby.html

注 21?http://zookeeper.apache.org/

etcd

etcd?注 22?很新也很輕量,安裝很簡單,配置也不復雜,所以非常適合入門。etcd存儲的是key-value格式的數據。

etcd是CoreOS的一個組件。同時CoreOS提供了一個基于公有云的服務發現服務discovery.etcd.io。

注 22?https://github.com/coreos/etcd

此外,我們還可以有Skydns/Skydock?注 23?、Discoverd?注 24?等選擇。

注 23 基于DNS的服務發現。?https://github.com/crosbymichael/skydock

注 24 Flynn的一個組件,它目前是基于etcd的,但是也可以擴展諸如Zookeeper等分布式存儲機制。https://github.com/flynn/discoverd

集群管理

圍繞Docker使用場景的開源集群管理軟件有很多,比如Geard、Fleet、Consul及Serf等,這些軟件都是隨著Docker應運而生的;此外還有很多老牌的集群管理軟件,比如Mesos等也可以很好的結合Docker使用。

Serf和Consul

Serf?注 25?是一個基于Gossip協議去中心的服務器發現和集群管理工具,它非常輕量,高可用并具備容錯機制。

注 25?http://www.serfdom.io/

Consul?注 26?是一個服務發現和集群配置共享的軟件,除了K/V store功能之外,它還支持跨數據中心及容錯功能,并能進行服務健康監測。

注 26?http://www.consul.io/

這兩個軟件都Vagrant作者所在公司HashiCorp?注 27?發布的產品,這個公司也值得大家關注。

注 27?http://www.hashicorp.com/products

Apache Mesos & Marathon & deimos & etc.

Mesos用于對多個節點的資源進行管理,它將多臺服務器作為一臺“虛擬機”看待,并在這臺虛擬機上分配資源,用戶通過使用framework進行資源管理。Marathon是一個Mesos的framework,用來啟動、管理需要長時間運行的任務。deimos則是一個為Mesos準備的Docker插件。

其它工具

Cloud Foundry在5月份發布的Docker版的BOSH工具,有興趣的讀者可以參考一下Decker?注 28?項目。

注 28 Decker = Docker + Cloud Foundry.?http://www.cloudcredo.com/decker-docker-cloud-foundry/

另外Clocker?注 29?這個項目也比較有意思,它基于Apache Brooklyn(目前還在孵化器中),能在多云環境下基于Docker容器進行應用部署。這個項目的擴展性很好,非常方便自己定制。不過項目還太年輕,要想使用的話恐怕還需要些時日。

注 29?https://github.com/brooklyncentral/clocker

5.3.2. 和OS的深度結合

在Fedora上使用的systemd?注 30?就已經提供了集成容器和虛擬機的功能。

注 30 systemd是用來替代Linux中init系統的系統軟件,目前已經在Fedora/RHEL等中采用

Docker除了能在各種主流Linux上使用之外,還出現了有專為運行Docker容器而定制的OS了,比如CoreOS?注 31?,RedHat的Atomic?注 32?。

注 31?https://coreos.com/?,在6月末剛剛宣布獲得了八百萬美元的A輪融資注 32?http://www.projectatomic.io/

CoreOS

CoreOS是一個精簡版的Linux,可以運行在既有硬件或者云上,它也是一個最近備受關注的項目。CoreOS不提供類似yum或者apt類似的包管理工具,你不需要在CoreOS中安裝軟件,而是讓程序都在Docker容器中去運行。CoreOS使用systemd和fleet來對容器進行管理,通過etcd進行服務發現和配置信息共享。

Atomic

Project Atomic是最近才發布的一個項目,它也是一個瘦身版的Linux,只包含systemd/geard?注 33?/rpm-OSTree以及Docker組件,專門用來部署和管理Docker容器。它能在接近硬件裸機級別上高性能的運行大量容器,而且它還是基于SELinux的,在安全上也有保障。

注 33?http://openshift.github.io/geard/

5.3.3. Container技術規范化和兼容性

就在DockerCon14開始的前一天,Flynn發布了Pinkerton,一個支持在其它容器中使用Docker鏡像的技術。

而另一方面,我們知道除了LXC,Docker之外,還有很多其它容器技術,比如Zones,jail和LMCTFY等,那么試想這么多的容器之上,是否有統一接口、互相兼容或者在容器上加一層封裝的可能性呢?比如讓一種容器的鏡像,能運行到其它容器中?Docker容器已經能互相連接了,會不會異構的容器之間也能進行某種交互呢?

6. 總結

Docker雖然入門和使用起來非常簡單,但整個生態系統還是挺龐大的,而且其底層技術也都很復雜,由于篇幅有限及筆者學識不精,也只能說一些皮毛之事,最多只能算是拋塊磚而已;而且筆者也有一種意猶未盡的感覺,但是由于篇幅所限,不能說到面面俱到,更多的內容,還請各位讀者自己去深入挖掘。

總之筆者認為Docker還是非常有趣的一個東西,值得大家花些時間體驗一下,相信在各位的工作中多多少少都能用的上Docker。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 轉載自 http://blog.opskumu.com/docker.html 一、Docker 簡介 Docke...
    極客圈閱讀 10,532評論 0 120
  • Docker — 云時代的程序分發方式 - 簡書 http://www.lxweimin.com/p/7745d8d...
    葡萄喃喃囈語閱讀 429評論 0 0
  • 0. 前言 docker是什么?docker是用GO語言開發的應用容器引擎,基于容器化,沙箱機制的應用部署技術。可...
    sessionboy閱讀 3,877評論 2 49
  • 五、Docker 端口映射 無論如何,這些 ip 是基于本地系統的并且容器的端口非本地主機是訪問不到的。此外,除了...
    R_X閱讀 1,792評論 0 7
  • 電視機里傻笑著的大三男孩,輕聲哼唱著《小幸運》,突然就想起了生命中那些稍縱即逝的一點點歡欣。用了上午的時間匆匆忙忙...
    morlindeer閱讀 379評論 0 1