Docker是一種在Linux容器里運(yùn)行應(yīng)用的開源工具,一種輕量級(jí)的虛擬機(jī)。除了運(yùn)行應(yīng)用,Docker還提供了一些工具,借助Docker Index或自己托管的Docker注冊(cè)表對(duì)進(jìn)行了集裝箱化處理的應(yīng)用進(jìn)行分發(fā),從而簡(jiǎn)化復(fù)雜應(yīng)用的部署過(guò)程。
我將在本文介紹如今在部署復(fù)雜系統(tǒng)時(shí)公司所面臨的挑戰(zhàn),Docker怎樣有效地解決這個(gè)問(wèn)題,以及Docker的其他用例。
服務(wù)器應(yīng)用的部署已經(jīng)越來(lái)越復(fù)雜了。把幾個(gè)Perl腳本拷貝到正確目錄就完成服務(wù)器應(yīng)用的安裝,這種時(shí)代已經(jīng)一去不復(fù)返了。如今的軟件有很多類型的需求:
我們來(lái)看一個(gè)相對(duì)簡(jiǎn)單的應(yīng)用的部署:Wordpress。Wordpress的安裝通常要求:
在服務(wù)器上部署、運(yùn)行這樣一個(gè)系統(tǒng),我們可能會(huì)遇到下面的問(wèn)題和挑戰(zhàn):
那我們應(yīng)該如何解決這些問(wèn)題呢?
我們決定在單獨(dú)的虛擬機(jī)上運(yùn)行獨(dú)立的應(yīng)用,例如Amazon的EC2,大部分問(wèn)題這時(shí)會(huì)迎刃而解:
完美!
不過(guò)……我們有個(gè)新問(wèn)題:虛擬機(jī)在兩個(gè)方面比較昂貴:
我們能做得更好嗎?
進(jìn)入Docker的世界吧。
Docker是由公共PaaS提供商dotCloud的人發(fā)起的開源項(xiàng)目,于去年初發(fā)起。從技術(shù)角度來(lái)說(shuō),Docker(主要用Go語(yǔ)言編寫)試圖簡(jiǎn)化兩種已有技術(shù)的使用:
Docker可以安裝在任何支持AUFS和內(nèi)核版本大于等于3.8的Linux系統(tǒng)上。但從概念上來(lái)說(shuō)它并不依賴于這些技術(shù),以后也可以和類似的技術(shù)一起運(yùn)行,例如Solaris的Zones或BSD jails,并將ZFS作為文件系統(tǒng)。不過(guò)目前只能選擇Linux 3.8+和AUFS。
那Docker為什么有意思呢?
讓我們回到前面的部署、操作問(wèn)題列表,看看Docker是怎么解決的:
假設(shè)你已經(jīng)安裝了Docker。要在Ubuntu容器中運(yùn)行bash,只要執(zhí)行:
docker run -t -i ubuntu /bin/bash
根據(jù)“ubuntu”鏡像的下載情況,Docker會(huì)選擇下載或者使用本地可用的拷貝,然后在Ubuntu容器里運(yùn)行/bin/bash。接著你就能在容器里執(zhí)行幾乎所有典型的Ubuntu操作,比如安裝新的包。
我們來(lái)安裝個(gè)“hello”:
$ docker run -t -i ubuntu /bin/bashroot@78b96377e546:/# apt-get install helloReading package lists... DoneBuilding dependency tree... DoneThe following NEW packages will be installed: hello0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.Need to get 26.1 kB of archives.After this operation, 102 kB of additional disk space will be used.Get:1 http://archive.ubuntu.com/ubuntu/ precise/main hello amd64 2.7-2 [26.1 kB]Fetched 26.1 kB in 0s (390 kB/s)debconf: delaying package configuration, since apt-utils is not installedSelecting previously unselected package hello.(Reading database ... 7545 files and directories currently installed.)Unpacking hello (from .../archives/hello_2.7-2_amd64.deb) ...Setting up hello (2.7-2) ...root@78b96377e546:/# helloHello, world!
現(xiàn)在退出,然后再運(yùn)行一次相同的Docker命令:
root@78b96377e546:/# exitexit$ docker run -t -i ubuntu /bin/bashroot@e5e9cde16021:/# hellobash: hello: command not found
怎么了?我們美麗的hello命令哪兒去了?事實(shí)上我們剛剛根據(jù)干凈的Ubuntu鏡像啟動(dòng)了一個(gè)新的容器。要繼續(xù)先前那個(gè),我們必須把它提交到倉(cāng)庫(kù)中。我們退出這個(gè)容器,看看先前啟動(dòng)容器的ID是什么:
$ docker ps -aID IMAGE COMMAND CREATED STATUS PORTSe5e9cde16021 ubuntu:12.04 /bin/bash About a minute ago Exit 12778b96377e546 ubuntu:12.04 /bin/bash 2 minutes ago Exit 0
docker ps命令能列出當(dāng)前運(yùn)行的容器,docker ps -a還會(huì)顯示已經(jīng)退出的容器。每個(gè)容器都有一個(gè)唯一的ID,類似于Git提交哈希值。命令也列出了容器基于的鏡像、運(yùn)行的命令、創(chuàng)建時(shí)間、當(dāng)前狀態(tài),以及容器暴露的端口和與主機(jī)端口之間的映射。
上面那個(gè)是我們第二次啟動(dòng)的容器,不包含“hello”;下面那個(gè)是我們想重用的,所以我們提交一下,再創(chuàng)建一個(gè)新的容器:
$ docker commit 78b96377e546 zefhemel/ubuntu356e4d516681$ docker run -t -i zefhemel/ubuntu /bin/bashroot@0d7898bbf8cd:/# helloHello, world!
我用容器ID把容器提交到了倉(cāng)庫(kù)中。倉(cāng)庫(kù)類似于Git倉(cāng)庫(kù),包含一或多個(gè)打了標(biāo)簽的鏡像。如果像我一樣沒有指定標(biāo)簽名稱,標(biāo)簽會(huì)被命名為“l(fā)atest”。運(yùn)行docker images命令可以查看本地安裝的所有鏡像。
Docker提供了一些基礎(chǔ)鏡像(比如ubuntu和centos),你也可以創(chuàng)建自己的鏡像。用戶倉(cāng)庫(kù)的命名模型和Github的類似:Docker用戶名后面跟一個(gè)斜線,然后再跟倉(cāng)庫(kù)名稱。
前面創(chuàng)建Docker鏡像的方式并不是特別正規(guī),你可以試試。更簡(jiǎn)潔的方式是使用Dockerfile。
Dockerfile是個(gè)簡(jiǎn)單的文本文件,介紹了如何從基礎(chǔ)鏡像構(gòu)建鏡像。我在Github上提供了幾個(gè)Dockerfile。下面的文件用來(lái)運(yùn)行、安裝SSH服務(wù)器:
FROM ubuntuRUN apt-get updateRUN apt-get install -y openssh-serverRUN mkdir /var/run/sshdRUN echo "root:root" | chpasswdEXPOSE 22
上面的內(nèi)容一目了然。FROM命令定義了基礎(chǔ)鏡像,基礎(chǔ)鏡像可以是官方的,也可以是我們剛剛創(chuàng)建的zefhemel/ubuntu。RUN命令用來(lái)配置鏡像。在這里,我們更新了APT包倉(cāng)庫(kù),安裝了openssh-server,創(chuàng)建了一個(gè)目錄,然后給我們的root賬戶設(shè)置了一個(gè)再簡(jiǎn)單不過(guò)的密碼。EXPOSE命令會(huì)向外暴露22端口(SSH端口)。接下來(lái)看看如何構(gòu)建并實(shí)例化這個(gè)Dockerfile。
第一步是構(gòu)建一個(gè)鏡像。在包含Dockerfile的目錄下運(yùn)行:
$ docker bui ld -t zefhemel/ssh .
這會(huì)創(chuàng)建一個(gè)zefhemel/ssh倉(cāng)庫(kù),包含我們新的SSH鏡像。如果創(chuàng)建成功,就能進(jìn)行實(shí)例化了:
$ docker run -d zefhemel/ssh /usr/sbin/sshd -D
和前面的命令不一樣。-d表示會(huì)在后臺(tái)運(yùn)行容器,而不是運(yùn)行bash,所以我們用前臺(tái)模式(用-D參數(shù)指定)運(yùn)行了sshd守護(hù)進(jìn)程。
讓我們檢查運(yùn)行中的容器,看看命令做了些什么:
$ docker psID IMAGE COMMAND CREATED STATUS PORTS23ee5acf5c91 zefhemel/ssh:latest /usr/sbin/sshd -D 3 seconds ago Up 2 seconds 49154->22
可以看到我們的容器啟動(dòng)著。PORTS頭下的內(nèi)容比較有意思。由于我們EXPOSE了22端口,這個(gè)端口現(xiàn)在映射到了主機(jī)系統(tǒng)的一個(gè)端口(這里是49154)。讓我們看看它能否運(yùn)行。
$ ssh root@localhost -p 49154The authenticity of host '[localhost]:49154 ([127.0.0.1]:49154)' can't be established.ECDSA key fingerprint is f3:cc:c1:0b:e9:e4:49:f2:98:9a:af:3b:30:59:77:35.Are you sure you want to continue connecting (yes/no)? yesWarning: Permanently added '[localhost]:49154' (ECDSA) to the list of known hosts.root@localhost's password: Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.8.0-27-generic x86_64) * Documentation: https://help.ubuntu.com/ The programs included with the Ubuntu system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. root@23ee5acf5c91:~#
再次成功了!現(xiàn)在有了一個(gè)運(yùn)行的SSH服務(wù)器,我們能登錄它。在有人猜出密碼并攻擊容器之前,讓我們先從SSH退出,殺掉容器。
$ docker kill 23ee5acf5c91
如你所見,容器的22端口映射到了49154端口,但這是完全隨機(jī)的。要把它映射到特定端口,運(yùn)行命令時(shí)傳入-p參數(shù):
docker run -p 2222:22 -d zefhemel/ssh /usr/sbin/sshd -D
現(xiàn)在,如果2222端口可用,我們的端口就會(huì)映射到2222上。我們?cè)贒ockerfile的結(jié)尾再添加一行內(nèi)容,以便我們的鏡像對(duì)用戶更加友好:
CMD /usr/sbin/sshd -D
CMD表示構(gòu)建鏡像時(shí)并不會(huì)運(yùn)行命令,實(shí)例化時(shí)才運(yùn)行。所以不傳遞其它參數(shù)時(shí)就會(huì)執(zhí)行/usr/sbin/sshd -D。然后我們可以直接運(yùn)行:
docker run -p 2222:22 -d zefhemel/ssh
得到的結(jié)果和前面一樣。要發(fā)布新創(chuàng)建的鏡像,只要運(yùn)行docker push就可以了:
docker push zefhemel/ssh
登錄之后,鏡像就可用了,用先前的docker run命令就能執(zhí)行命令。
讓我們回到Wordpress的例子。怎樣在容器里用Docker運(yùn)行Wordpress呢?要構(gòu)建一個(gè)Wordpress鏡像,我們要?jiǎng)?chuàng)建一個(gè)Dockerfile:
幸運(yùn)的是,很多人已經(jīng)成功了,比如John Fink的GitHub庫(kù)就包括創(chuàng)建這樣一個(gè)Wordpress鏡像需要的所有內(nèi)容。
除了用可靠、可重復(fù)的方式簡(jiǎn)化復(fù)雜應(yīng)用的部署,Docker還有很多用途。下面是一些有趣的Docker用法和項(xiàng)目:
盡管Docker有助于系統(tǒng)的可靠部署,但它本身并不是個(gè)完全成熟的部署系統(tǒng)。它操作的是容器里運(yùn)行的應(yīng)用。哪個(gè)容器安裝在哪個(gè)服務(wù)器上,以及如何啟動(dòng)它們,則超出了Docker的范圍。
同樣的,Docker也不處理跨多個(gè)容器(可能在多個(gè)物理服務(wù)器上,也可能在多個(gè)VM上)運(yùn)行的應(yīng)用。要讓容器互相通信,需要某些發(fā)現(xiàn)機(jī)制,來(lái)找出哪些IP和端口上的其他應(yīng)用可用。這和跨常規(guī)虛擬機(jī)的服務(wù)發(fā)現(xiàn)非常相似。etcd等工具,或者其他的服務(wù)發(fā)現(xiàn)機(jī)制都能用來(lái)解決這個(gè)問(wèn)題。
雖然本文描述的所有內(nèi)容用原始的LXC、cgroups和AUFS也可能實(shí)現(xiàn),但實(shí)現(xiàn)起來(lái)絕對(duì)沒有那么容易或簡(jiǎn)單。Docker提供了一種簡(jiǎn)單的方式將復(fù)雜應(yīng)用打包到容器中,而且能輕松版本化、可靠分發(fā)。進(jìn)而讓輕量級(jí)的Linux容器和真正的虛擬機(jī)一樣靈活、強(qiáng)大,但成本更低、方式更為便捷。即便Vagrant VirtualBox VM在Macbook Pro上,使用運(yùn)行在其中的Docker創(chuàng)建的Docker鏡像也能很好地運(yùn)行在EC2、Rackspace Cloud或物理硬件上,反之亦然。
Docker可以從它的網(wǎng)站上獲取,并免費(fèi)使用。交互式的入門指南很不錯(cuò)。項(xiàng)目的路線圖指出,第一個(gè)生產(chǎn)就緒的版本是2013年10月發(fā)布的0.8版本,不過(guò)此前大家已經(jīng)在生產(chǎn)環(huán)境里使用Docker了。
【編輯推薦】
聯(lián)系客服