讓博客Docker化,輕松上手Docker
Docker是一個有趣的技術,在過去的兩年已經(jīng)從一個想法變成了全世界的機構(gòu)都在采用來部署應用的技術。在今天的文章中我將會討論如何通過將一個現(xiàn)有應用Docker化來上手Docker。那這里選取的現(xiàn)有應用就是我的博客。
什么是Docker
在我們開始學習Docker的基礎知識之前讓我們首先理解什么是Docker,并且為什么它那么流行。Docker是一個操作系統(tǒng)容器管理工具,通過將應用打包到操作系統(tǒng)容器里面,從而讓你能輕松管理和部署應用。
容器 vs. 虛擬機
容器可能不如虛擬機一樣為人所熟知,但是它們是另外的一種提供操作系統(tǒng)虛擬化的方法。然而,他們與標準的虛擬機有很大的差異。
標準的虛擬機通常包含一個完整的操作系統(tǒng),OS軟件包,最后包含一兩個應用。它是通過一個向虛擬機提供了硬件虛擬化的Hypervisor來實現(xiàn)的,允許單個服務器運行很多獨立的被當做虛擬游客(virtual guest)的操作系統(tǒng)。
而容器與虛擬機的類似之處在于它們允許單個服務器運行多個操作環(huán)境(operating environment),然而這些環(huán)境不卻是完整的操作系統(tǒng)。容器通常只包含必要的OS軟件包和應用。他們通常不包含一個完整的操作系統(tǒng)或者硬件虛擬化。這也意味著比之虛擬機,容器的額外開銷(overhead)更小。
容器和虛擬機通常被視為不能共生的技術,然而這通常是一個誤解。虛擬機面向物理服務器,提供可以能與其他虛擬機一起共享這些物理資源的,功能完善的操作環(huán)境。容器通常是用來通過對單一主機的一個進程進行隔離,來保證被隔離的進程無法與處于同一個系統(tǒng)的其他進程進行互動。實際上,比起完全的虛擬機,容器與BSD的Jail,
chroot的進程更加類似。
在容器的基礎上Docker提供了什么
Docker自身并不是一個容器的運行時環(huán)境;實際上Docker實際上是對容器技術不可知的(container technology agnostic),并且為了支持Solaris Zones和BSD Jails花了不少功夫。Docker提供的是一種容器管理,打包和部署的方法。盡管這種類型的功能已經(jīng)某一種程度地存在于虛擬機中,但在傳統(tǒng)上,它們并不是為了絕大多數(shù)的容器方案而生的,而那些已經(jīng)存在的,卻又不如Docker一樣容易使用且功能完善。
現(xiàn)在我們知道了Docker是什么,讓我們開始通過安裝Docker并且部署一個公共的預先構(gòu)建好的容器來學習Docker是如何工作的。
從安裝開始
因為Docker不會默認安裝好,第一步就是安裝Docker軟件包;因為我們實例中使用的操作系統(tǒng)是Ubuntu 14.04,我們將會使用Apt包管理工具:
apt-getinstalldocker.ioReadingpackagelists...Done
Buildingdependencytree
Readingstateinformation...Done
Thefollowingextrapackageswillbeinstalled:
aufs-toolscgroup-litegitgit-manliberror-perl
Suggestedpackages:
btrfs-toolsdebootstraplxcrinsegit-daemon-rungit-daemon-sysvinitgit-doc
git-elgit-emailgit-guigitkgitwebgit-archgit-bzrgit-cvsgit-mediawiki
git-svn
ThefollowingNEWpackageswillbeinstalled:
aufs-toolscgroup-litedocker.iogitgit-manliberror-perl
0upgraded,6newlyinstalled,0toremoveand0notupgraded.
Needtoget7,553kBofarchives.
Afterthisoperation,46.6MBofadditionaldiskspacewillbeused.
Doyouwanttocontinue?[Y/n]y
要檢查是否有容器運行我們可以執(zhí)行docker命令,然后使用ps命令選項:
dockerpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
docker命令的ps功能類似于Linux的ps命令。它能顯示可用的Dokcer容器和他們的當前狀態(tài)。因為我們還沒有啟動任何的Docker容器,命令顯示沒有任何的正在運行的容器。
部署一個預先構(gòu)建好的nginx Docker容器
Docker的一個我最喜歡的特性是其可有讓你用類似yum或者apt-get部署一個軟件包一樣的方式來部署一個預先構(gòu)建好容器的能力。為了更好的說明這一點,讓我們來部署一個預先構(gòu)建好的運行nginx服務器的容器。我們可以通過執(zhí)行docker命令,但是這一次,我們使用的是run命令選項。
#dockerrun-dnginx
Unabletofindimage'nginx'locally
Pullingrepositorynginx
5c82215b03d1:Downloadcomplete
e2a4fb18da48:Downloadcomplete
58016a5acc80:Downloadcomplete
657abfa43d82:Downloadcomplete
dcb2fe003d16:Downloadcomplete
c79a417d7c6f:Downloadcomplete
abb90243122c:Downloadcomplete
d6137c9e2964:Downloadcomplete
85e566ddc7ef:Downloadcomplete
69f100eb42b5:Downloadcomplete
cd720b803060:Downloadcomplete
7cc81e9a118a:Downloadcomplete
docker命令的run功能告訴Docker來找到一個指定的Docker鏡像,并且啟動一個運行該鏡像的容器。默認情況下,Docker容器會在前臺運行,意味這當你執(zhí)行docker run你的shell會綁定到這個容器的console和在容器里面運行的進程。為了在將這個Docker容器在后臺啟動,我包含了一個-d(detach,脫離)的標志。
現(xiàn)在再次運行
docker ps,我們可以看到正在運行的nginx容器:
dockerpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
f6d31ab01fc9nginx:latestnginx-g'daemonoff4secondsagoUp3seconds443/tcp,80/tcpdesperate_lalande
在上面的輸出中,我們可以看到運行中的容器叫desperate_lalande,并且該容器是從鏡像nginx:latest構(gòu)建而來。
Docker鏡像
鏡像是Docker的核心特性之一,并且與虛擬機的鏡像很類似。類似之處在于,一個Docker鏡像是一個保存好并且打包好的容器。然而Docker,并不止步于鏡像創(chuàng)建。Docker也包含了通過Docker倉庫分發(fā)這些鏡像的能力,這個概念與軟件包倉庫類似。正是這個能力讓Docker可以如同用yum來部署一個軟件包一樣來部署容器。為了更好的理解這如何工作的,讓我們再看看docker run的輸出:
#dockerrun-dnginx
Unabletofindimage'nginx'locally
第一條消息我們可以看到docker不能在本地找到一個名為nginx的鏡像。我們之所以看到這個消息是因為當我們執(zhí)行docker run的時候我們告訴Docker啟動一個容器,一個基于名為nginx鏡像的容器。因為Docker正在啟動一個基于特定鏡像的容器,它需要首先找到這個鏡像。在檢查遠端的倉庫之前,Docker首先檢查是否本地已經(jīng)存在有一個該特定名稱的鏡像。
因為我們的系統(tǒng)是全新的,沒有一個名為nginx的Docker鏡像,這意味著Docker需要在Docker倉庫里面下載:
Pullingrepositorynginx
5c82215b03d1:Downloadcomplete
e2a4fb18da48:Downloadcomplete
58016a5acc80:Downloadcomplete
657abfa43d82:Downloadcomplete
dcb2fe003d16:Downloadcomplete
c79a417d7c6f:Downloadcomplete
abb90243122c:Downloadcomplete
d6137c9e2964:Downloadcomplete
85e566ddc7ef:Downloadcomplete
69f100eb42b5:Downloadcomplete
cd720b803060:Downloadcomplete
7cc81e9a118a:Downloadcomplete
這正如輸出的第二部分所顯示的一樣。默認情況下,Docker使用Docker Hub倉庫,這是由Docker公司運行的倉庫服務。
如Github一樣,Docker Hub對于公有的倉庫免費,但是對于私有倉庫需要付費。然而,你也可以部署你自己的Docker倉庫,實際上這只是運行一下
docker run registry這么簡單。在這篇文章中,我們不會部署一個自己的注冊表服務(registry service)。
停止和移除鏡像
在我們開始構(gòu)建一個自己的Docker容器之前,讓我們首先清理我們的Docker環(huán)境。我們需要停止之前啟動的容器并且移除它。
要啟動一個Docker容器我們執(zhí)行
docker命令并且使用run命令選項,要停止這個已啟動的鏡像我們只需要執(zhí)行docker命令并使用kill選項并指定該容器的名稱。
#dockerkilldesperate_lalande
desperate_lalande
如果我們再次執(zhí)行docker ps我們看到容器沒有運行了。
#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
然而在這一刻,我們僅僅停止了這個容器,盡管它沒有運行了,但是它還是存在的。默認情況下docker ps只會顯示運行中的容器,如果我們添加了-a(all,所有)標志,它就會顯示所有不論運行與否的容器。
#dockerps-a
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
f6d31ab01fc95c82215b03d1nginx-g'daemonoff4weeksagoExited(-1)Aboutaminuteagodesperate_lalande
要完全的移除該容器,我們可以使用docker命令并且使用rm命令選項。
#dockerrmdesperate_lalande
desperate_lalande
盡管這個容器已經(jīng)被移除了;我們?nèi)匀豢梢噪S時使用nginx鏡像。我們想要重新運行docker run -d nginx,容器將會立即=啟動而不需要再次拉取名叫nginx的鏡像。這是因為Docker已經(jīng)在本地保存了一個備份。
要查看所有本地的鏡像我們可以是運行docker命令并且使用
image選項。
#dockerimages
REPOSITORYTAGIMAGEIDCREATEDVIRTUALSIZE
nginxlatest9fab4090484a5daysago132.8MB
構(gòu)建我們自己的自定義鏡像
到目前為止我們已經(jīng)使用了一些基礎的Docker命令來啟動,停止和移除一個普通的預選構(gòu)建好的鏡像。而為了Docker化這個博客,我們將要構(gòu)建我們自己的Docker鏡像,這意味要創(chuàng)建一個Dockerfile。
在絕大多數(shù)的虛擬機環(huán)境中,假如你想要創(chuàng)建一個機器的鏡像,你需要首先創(chuàng)建一個虛擬機,然后安裝好操作系統(tǒng),然后安裝好應用程序,最后將其轉(zhuǎn)化成一個模板或者鏡像。然而,對于Docker來說,這些步驟都可以通過Dockerfile進行自動化。一個Dockerfile是一個可以向Docker提供構(gòu)建指令的方式。在這一節(jié)中,我們將要創(chuàng)建一個可以用來部署本博客的自定義Dockerfile。
理解應用
在我們開始創(chuàng)建Dockerfile之前,我們需要首先理解要部署這個博客我們必需什么。
這個博客自身實際上是通過一個自己編寫的名為
hamerkop(注:錘頭鸛的意思)的靜態(tài)網(wǎng)站生成器生成的一個靜態(tài)的HTML網(wǎng)頁。這個生成器非常的簡單,是我專門為這個博客編寫的,剛好夠用。這個博客的所有的代碼和源文件都能在公有的Github倉庫中訪問到。為了部署這個博客我們只需要從Github拿到該倉庫的內(nèi)容,然后安裝Python和一些Python的模塊,并且執(zhí)行hamerkop應用。要對外服務這些生成的內(nèi)容我們需要使用nginx;這意味著我們也需要安裝好nginx。
到目前為止,我們的Dockerfile應該足夠簡單,但是就這些也足夠讓我們學到不少
Dockerfile語法。首先讓我們克隆Github倉庫代碼,然后用最喜愛的編輯器來創(chuàng)建一個Dockerfile;我這里使用vi。
#gitclonehttps://github.com/madflojo/blog.git
Cloninginto'blog'...
remote:Countingobjects:622,done.
remote:Total622(delta0),reused0(delta0),pack-reused622
Receivingobjects:100%(622/622),14.80MiB|1.06MiB/s,done.
Resolvingdeltas:100%(242/242),done.
Checkingconnectivity...done.
>cdblog/
>viDockerfile
用FROM繼承一個Docker鏡像
Dockerfile的第一條指令是FROM指令。這用來將一個已經(jīng)存在的Docker鏡像指定為基礎鏡像。這基本上為我們提供了繼承另一個Docker鏡像的方法。在我們這個場景中,我們將會從我們之前用到的nginx鏡像開始,如果我們想要從一個最原始的空白狀態(tài)(* blank slate*)開始,我們可以通過指定ubuntu:latest使用Ubuntu鏡像。
FROMnginx:latest
MAINTAINERBenjaminCane
除了FROM指令之外,我還包含了一個MAINTAINER指令,其是用來顯示Dockerfile的作者的。
因為Docker支持使用
#來作為評論的標示,我將會使用這種語法來解釋Dockerfile的各個部分。
運行一個測試構(gòu)建
因為我們繼承了nginx Docker鏡像,我們當前的Dockerfil也繼承了所有用來構(gòu)建該nginx鏡像的所有指令。這意味著即使在這一刻,我們已經(jīng)能夠從這個Dockerfile中構(gòu)建出一個Docker鏡像并且用這個鏡像運行出一個容器。生成的鏡像基本上跟nginx鏡像一樣,讓我們現(xiàn)在就開始來對這個Dockerfile進行構(gòu)建,之后還有幾次構(gòu)建過,通過這些實踐來幫助解釋Docker的構(gòu)建過程。
為了開始從一個Dockerfile中開始一個構(gòu)建,我們可以簡單地執(zhí)行
docker命令并且使用build命令選項。
#dockerbuild-tblog/root/blog
SendingbuildcontexttoDockerdaemon23.6MB
SendingbuildcontexttoDockerdaemon
Step0:FROMnginx:latest
--->9fab4090484a
Step1:MAINTAINERBenjaminCane
--->Runninginc97f36450343
--->60a44f78d194
Removingintermediatecontainerc97f36450343
Successfullybuilt60a44f78d194
在上面的例子中,我們使用-t(tag,標簽)標志來將這個鏡像貼上名為"blog"的標簽。這基本上讓我們可以對鏡像進行命名。假如不為鏡像指定一個標簽,這個鏡像就只能通過一個由Dokcer指定的鏡像ID(Image ID)來調(diào)用。在這個場景下,這鏡像ID是60a44f78d194,正如我們在docker命令的成功構(gòu)建消息中看到的一樣。
除了
-t標志,我也指定了/root/blog目錄。這個目錄就是“構(gòu)建目錄(build directory)”,這個目錄包含了Dockerfile和其他必要的構(gòu)建這個容器的文件。
現(xiàn)在我們已經(jīng)完成了一個成功的構(gòu)建,讓我們開始對這個鏡像進行定制化。
使用RUN來執(zhí)行apt-get
這個用來生成HTML頁面的靜態(tài)網(wǎng)站生成器是使用Python來編寫的,因此在Dockerfile中第一個自定義的任務是安裝Python。要安裝Python包,我們需要用到Apt包管理器,這意味著我們需要在Dockerfile中說明需要執(zhí)行apt-get update和apt-get install python-dev;我們可以通過RUN指令來完成這一點。
#Dockerfilethatgeneratesaninstanceofhttp://bencane.com
FROMnginx:latest
MAINTAINERBenjaminCaneInstallpythonandpipRUNapt-getupdate
RUNapt-getinstall-ypython-devpython-pip
在上面我們僅僅是運行RUN指令來告訴Docker當其構(gòu)建這個鏡像的時候,它需要執(zhí)行指定的apt-get命令。然而有意思的部分是,這些命令只會在這個容器的情景(context)中才會執(zhí)行。這意味著python-dev和python-pip只被安裝到了容器中,并沒有安裝到主機中。或者用更簡單的話來說,在容器中,pip是可以執(zhí)行的,但是出了容器之外,pip命令是找不到的。
同樣重要的一點是,Docker的構(gòu)建過程中是不接受用戶的輸入的。這意味這所有由RUN指令來執(zhí)行的命令必須不藉由用戶輸入而完成。這給構(gòu)建過程增加了一點復雜度,因為很多應用是需要用戶輸入的。在我們這個場景中,
RUN所有執(zhí)行的命令都不需要用戶輸入。
安裝Python模塊
現(xiàn)在Python安裝好了我們需要安裝一些Python模塊。要在Docker之外做這件事情,我們通常是使用pip命令并且引用在博客的倉庫中的一個名叫requirements.txt文件。在之前的一個步驟里,我們使用了git命令將博客的Github倉庫克隆到/root/blog目錄下;這個目錄也同時是我們創(chuàng)建Dockerfile的地方。這很重要,因為這意味著Git倉庫的內(nèi)容能被Docker在構(gòu)建過程中訪問到。
當執(zhí)行構(gòu)建的時候,Docker會將構(gòu)建的情景(context)設置為一個指定的構(gòu)建目錄。這意味該文件夾任何的文件以及子目錄中的文件都能被構(gòu)建過程所使用,而處于該目錄之外(處于構(gòu)建情景之外的),是不能被訪問到的。
要安裝必需的Python模塊,我們需要將
requirements.txt文件從構(gòu)建目錄拷貝到容器之中去。我們可以在Dockerfile中使用COPY指令。
#Dockerfilethatgeneratesaninstanceofhttp://bencane.com
FROMnginx:latestMAINTAINERBenjaminCaneInstallpythonandpipRUNapt-getupdate
RUNapt-getinstall-ypython-devpython-pipCreateadirectoryforrequiredfilesRUNmkdir-p/build/AddrequirementsfileandrunpipCOPYrequirements.txt/build/
RUNpipinstall-r/build/requirements.txt
在Dockerfile中我們添加了3個指令。第一個指令使用RUN在容器中創(chuàng)建了一個/build目錄。這個目錄將用來拷貝用來生成靜態(tài)HTML頁面所需的任何文件。第二個指令是`COPY指令,用來將requirements.txt從構(gòu)建目錄拷貝到容器中的/build目錄。第三個使用了RUN指令,用來執(zhí)行pip命令;這會安裝所有在requirements.txt文件中指定的模塊。
COPY是一個在構(gòu)建定制化的鏡像時需要理解的很重要的指令。沒有在Dockerfile文件中指定復制文件,Docke鏡像就不會包含這個requirements.txt文件。在Docker容器一切都是被隔離的情況下,除非在Dockerfile中特別指定過,容器是不大可能包含所需的依賴的。
重新運行一個構(gòu)建
現(xiàn)在我們有了一些可以讓Docker執(zhí)行的定制化的任務了,讓我們來試著再一次對這個blog鏡像進行構(gòu)建。
#dockerbuild-tblog/root/blog
SendingbuildcontexttoDockerdaemon19.52MB
SendingbuildcontexttoDockerdaemon
Step0:FROMnginx:latest
--->9fab4090484a
Step1:MAINTAINERBenjaminCane
--->Usingcache
--->8e0f1899d1eb
Step2:RUNapt-getupdate
--->Usingcache
--->78b36ef1a1a2
Step3:RUNapt-getinstall-ypython-devpython-pip
--->Usingcache
--->ef4f9382658a
Step4:RUNmkdir-p/build/
--->Runninginbde05cf1e8fe
--->f4b66e09fa61
Removingintermediatecontainerbde05cf1e8fe
Step5:COPYrequirements.txt/build/
--->cef11c3fb97c
Removingintermediatecontainer9aa8ff43f4b0
Step6:RUNpipinstall-r/build/requirements.txt
--->Runninginc50b15ddd8b1
Downloading/unpackingjinja2(from-r/build/requirements.txt(line1))
Downloading/unpackingPyYaml(from-r/build/requirements.txt(line2))
Successfullyinstalledjinja2PyYamlmistunemarkdownMarkupSafe
Cleaningup...
--->abab55c20962
Removingintermediatecontainerc50b15ddd8b1
Successfullybuiltabab55c20962
從上面的構(gòu)建輸出我們可以看到構(gòu)建成功了,但是我們也可以看到另外一個有意思的消息;---> Using cache(使用緩存)。這個消息告訴我們的是,Docker能夠在構(gòu)建過程中使用他的構(gòu)建緩存。
Docker構(gòu)建緩存
當Docker構(gòu)建一個鏡像的時候,它不會僅僅構(gòu)建一個單一的鏡像;它實際上在整個構(gòu)建過程中會構(gòu)建出多個鏡像。實際上我們可以從以上的輸出看到,在每一步之后,Docker都創(chuàng)建了一個新的鏡像。
Step5:COPYrequirements.txt/build/
--->cef11c3fb97c
上面片段中的最后一行,實際上是Docker在告訴我們創(chuàng)建了一個新的鏡像,它通過輸出鏡像ID來告訴我們這一點;cef11c3fb97c。這個策略的一個有用之處在于Docker能夠使用這些鏡像作為后續(xù)構(gòu)建步驟的緩存。這很有用,因為它能讓Docker加快相同容器的新構(gòu)建的構(gòu)建過程。如果仔細我們看上面的例子,我們可以發(fā)現(xiàn)Docker能夠使用一個已經(jīng)緩存了的鏡像,而不是重新安裝python-dev和python-pip包。然而,因為Docker無法找到一個執(zhí)行過mkdir命令的構(gòu)建,這之后每一個后續(xù)的步驟都執(zhí)行了。
Docker的構(gòu)建緩存是一個饋贈也是一個詛咒;這么說的原因是否要使用緩存或者重新執(zhí)行指令這個決定是在一個很狹窄的范圍做出的。比如,如果如果有對
requirements.txt文件的更改,Docker會在構(gòu)建過程中檢測到這個改動然后從那一點重新開始。然而執(zhí)行apt-get命令卻情況不同。如果提供Python包的Apt倉庫包含了一個python-pip包更新的版本;Docker無法檢測到這個變化,然后簡單地使用緩存。這意味著可能我們安裝了軟件包的一個較老的版本。盡管這個對于python-pip軟件包來說這不是什么問題,如果安裝包緩存了一個包含已知漏洞的軟件包,那么就是一個問題。
介于這個原因,周期性的重新構(gòu)建鏡像并且不使用Docker的緩存是有用的。你可以在執(zhí)行一個Docker構(gòu)建的時候指定
--no-cache=True來禁用緩存。
部署blog的其余部分
當Python軟件包和模塊都安裝好后,現(xiàn)在我們應該拷貝必需的應用文件,然后運行hamerkop應用了。要完成這一步我們可以簡單地使用更多的COPY和RUN指令。
FROMnginx:latest
MAINTAINERBenjaminCane安裝python和pipRUNapt-getupdate
RUNapt-getinstall-ypython-devpython-pip創(chuàng)建一個文件夾放置必需文件RUNmkdir-p/build/添加依賴文件然后運行pipCOPYrequirements.txt/build/
RUNpipinstall-r/build/requirements.txt添加博客代碼和必需文件COPYstatic/build/static
COPYtemplates/build/templates
COPYhamerkop/build/
COPYconfig.yml/build/
COPYarticles/build/articles運行生成器RUN/build/hamerkop-c/build/config.yml
現(xiàn)在我們補上了其余的構(gòu)建指令,讓我們來再來一次構(gòu)建并且驗證是否鏡像能夠構(gòu)建成功。
#dockerbuild-tblog/root/blog/
SendingbuildcontexttoDockerdaemon19.52MB
SendingbuildcontexttoDockerdaemon
Step0:FROMnginx:latest
--->9fab4090484a
Step1:MAINTAINERBenjaminCane
--->Usingcache
--->8e0f1899d1eb
Step2:RUNapt-getupdate
--->Usingcache
--->78b36ef1a1a2
Step3:RUNapt-getinstall-ypython-devpython-pip
--->Usingcache
--->ef4f9382658a
Step4:RUNmkdir-p/build/
--->Usingcache
--->f4b66e09fa61
Step5:COPYrequirements.txt/build/
--->Usingcache
--->cef11c3fb97c
Step6:RUNpipinstall-r/build/requirements.txt
--->Usingcache
--->abab55c20962
Step7:COPYstatic/build/static
--->15cb91531038
Removingintermediatecontainerd478b42b7906
Step8:COPYtemplates/build/templates
--->ecded5d1a52e
Removingintermediatecontainerac2390607e9f
Step9:COPYhamerkop/build/
--->59efd1ca1771
Removingintermediatecontainerb5fbf7e817b7
Step10:COPYconfig.yml/build/
--->bfa3db6c05b7
Removingintermediatecontainer1aebef300933
Step11:COPYarticles/build/articles
--->6b61cc9dde27
Removingintermediatecontainerbe78d0eb1213
Step12:RUN/build/hamerkop-c/build/config.yml
--->Runninginfbc0b5e574c5
Successfullycreatedfile/usr/share/nginx/html//2011/06/25/checking-the-number-of-lwp-threads-in-linux
Successfullycreatedfile/usr/share/nginx/html//2011/06/checking-the-number-of-lwp-threads-in-linux
Successfullycreatedfile/usr/share/nginx/html//archive.html
Successfullycreatedfile/usr/share/nginx/html//sitemap.xml
--->3b25263113e1
Removingintermediatecontainerfbc0b5e574c5
Successfullybuilt3b25263113e1
運行一個定制化的容器在成功構(gòu)建后,我們現(xiàn)在可以通過運行docker命令并且使用run選項來啟我們的定制化的容器,就如我們之前運行nginx容易類似。
#dockerrun-d-p80:80--name=blogblog
5f6c7a2217dcdc0da8af05225c4d1294e3e6bb28a41ea898a1c63fb821989ba1
與先前一樣,-d(detach,脫離)標志是用來告訴Docker在后臺運行容器。然后我們這里也使用兩個新的標志。第一個標志是--name,這是用來給容器一個用戶指定的名稱。在之前的例子里,我們沒有指定名稱,因此Docker隨機生成了一個名稱。第二個新出現(xiàn)的標志是-p,這個標志能讓用戶來將一個端口從主機機器映射到容器中的一個端口。
我們使用的nginx基礎鏡像暴露了80端口來提供HTTP服務。默認情況下,與Docker容器內(nèi)部綁定的端口并沒有與主機系統(tǒng)綁定。為了讓外部的系統(tǒng)訪問容器內(nèi)部暴露的端口,這些端口必須通過使用
-p標志從主機端口映射到容器端口。假如我們想要端口從主機的8080端口,映射到容器中的80端口,我們可以通過使用這種語法-p 8080:80。
從上面的命令中,看起來我們的容器已經(jīng)啟動成功了。我們可以通過運行執(zhí)行
docker ps來驗證。
#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
d264c7ef92bdblog:latestnginx-g'daemonoff3secondsagoUp3seconds443/tcp,0.0.0.0:80->80/tcpblog
總結(jié)
到這里,我們已經(jīng)有了一個運行中的自定制的Docker容器。盡管這個文章中我們觸及到了不少的Dockerfile指令,我們還未能討論所有的指令。要獲取一個完整的指令列表你可以查看Docker的手冊頁面,那里很好的解釋了每個指令。
另外一個很好的資源是
Docker最佳實踐頁面,包含了不少的構(gòu)建自定制Dockerfile最佳實踐。有些指點十分有用,比如策略性地在Dockerfile中安排指令的順序。在上面的例子中,我們的Dockerfile用到的COPY指令,被放在了最后。這么做的原因是,articles目錄會頻繁更改。最好將可能經(jīng)常變動的指令放到最后,這樣可以優(yōu)化Docker的緩存步驟。
在這個文章中我覆蓋了如何從一個預先構(gòu)建好的容器開始,如何構(gòu)建然后部署一個自定制的容器。盡管Docker要學習的內(nèi)容還有很多,希望這篇文章會助你邁出第一步。