Docker学习总结(6)——通过 Docker 化一个博客网站来开启我们的 Docker 之旅
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
通過 Docker 化一個(gè)博客網(wǎng)站來開啟我們的 Docker 之旅
這篇文章包含 Docker 的基本概念,以及如何通過創(chuàng)建一個(gè)定制的 Dockerfile 來 Docker 化Dockerize一個(gè)應(yīng)用。
Docker 是一個(gè)過去兩年來從某個(gè) idea 中孕育而生的有趣技術(shù),公司組織們用它在世界上每個(gè)角落來部署應(yīng)用。在今天的文章中,我將講述如何通過“Docker 化Dockerize”一個(gè)現(xiàn)有的應(yīng)用,來開始我們的 Docker 之旅。這里提到的應(yīng)用指的就是這個(gè)博客!
什么是 Docker?
當(dāng)我們開始學(xué)習(xí) Docker 基本概念時(shí),讓我們先去搞清楚什么是 Docker 以及它為什么這么流行。Docker 是一個(gè)操作系統(tǒng)容器管理工具,它通過將應(yīng)用打包在操作系統(tǒng)容器中,來方便我們管理和部署應(yīng)用。
容器 vs. 虛擬機(jī)
容器和虛擬機(jī)并不完全相似,它是另外一種提供操作系統(tǒng)虛擬化的方式。它和標(biāo)準(zhǔn)的虛擬機(jī)還是有所不同。
標(biāo)準(zhǔn)的虛擬機(jī)一般會(huì)包括一個(gè)完整的操作系統(tǒng)、操作系統(tǒng)軟件包、最后還有一至兩個(gè)應(yīng)用。這都得益于為虛擬機(jī)提供硬件虛擬化的管理程序。這樣一來,一個(gè)單一的服務(wù)器就可以將許多獨(dú)立的操作系統(tǒng)作為虛擬客戶機(jī)運(yùn)行了。
容器和虛擬機(jī)很相似,它們都支持在單一的服務(wù)器上運(yùn)行多個(gè)操作環(huán)境,只是,在容器中,這些環(huán)境并不是一個(gè)個(gè)完整的操作系統(tǒng)。容器一般只包含必要的操作系統(tǒng)軟件包和一些應(yīng)用。它們通常不會(huì)包含一個(gè)完整的操作系統(tǒng)或者硬件的虛擬化。這也意味著容器比傳統(tǒng)的虛擬機(jī)開銷更少。
容器和虛擬機(jī)常被誤認(rèn)為是兩種對(duì)立的技術(shù)。虛擬機(jī)采用一個(gè)物理服務(wù)器來提供全功能的操作環(huán)境,該環(huán)境會(huì)和其余虛擬機(jī)一起共享這些物理資源。容器一般用來隔離一個(gè)單一主機(jī)上運(yùn)行的應(yīng)用進(jìn)程,以保證隔離后的進(jìn)程之間不能相互影響。事實(shí)上,容器和?BSD Jails以及chroot進(jìn)程的相似度,超過了和完整虛擬機(jī)的相似度。
Docker 在容器之上提供了什么
Docker 本身不是一個(gè)容器運(yùn)行環(huán)境,事實(shí)上,只是一個(gè)與具體實(shí)現(xiàn)無關(guān)的容器技術(shù),Docker 正在努力支持 Solaris Zones[1] 和BSD Jails[2]。Docker 提供了一種管理、打包和部署容器的方式。雖然一定程度上,虛擬機(jī)多多少少擁有這些類似的功能,但虛擬機(jī)并沒有完整擁有絕大多數(shù)的容器功能,即使擁有,這些功能用起來都并沒有 Docker 來的方便或那么完整。
現(xiàn)在,我們應(yīng)該知道 Docker 是什么了,然后,我們將從安裝 Docker,并部署一個(gè)公開的預(yù)構(gòu)建好的容器開始,學(xué)習(xí) Docker 是如何工作的。
從安裝開始
默認(rèn)情況下,Docker 并不會(huì)自動(dòng)被安裝在您的計(jì)算機(jī)中,所以,第一步就是安裝 Docker 軟件包;我們的教學(xué)機(jī)器系統(tǒng)是 Ubuntu 14.0.4,所以,我們將使用 Apt 軟件包管理器,來執(zhí)行安裝操作。
# apt-get install docker.io
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
aufs-tools cgroup-lite git git-man liberror-perl
Suggested packages:
btrfs-tools debootstrap lxc rinse git-daemon-run git-daemon-sysvinit git-doc
git-el git-email git-gui gitk gitweb git-arch git-bzr git-cvs git-mediawiki
git-svn
The following NEW packages will be installed:
aufs-tools cgroup-lite docker.io git git-man liberror-perl
0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
Need to get 7,553 kB of archives.
After this operation, 46.6 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
為了檢查當(dāng)前是否有容器運(yùn)行,我們可以執(zhí)行docker命令,加上ps選項(xiàng)
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
docker命令中的ps功能類似于 Linux 的ps命令。它將顯示可找到的 Docker 容器及其狀態(tài)。由于我們并沒有啟動(dòng)任何 Docker 容器,所以命令沒有顯示任何正在運(yùn)行的容器。
部署一個(gè)預(yù)構(gòu)建好的 nginx Docker 容器
我比較喜歡的 Docker 特性之一就是 Docker 部署預(yù)先構(gòu)建好的容器的方式,就像yum和apt-get部署包一樣。為了更好地解釋,我們來部署一個(gè)運(yùn)行著 nginx web 服務(wù)器的預(yù)構(gòu)建容器。我們可以繼續(xù)使用docker命令,這次選擇run選項(xiàng)。
# docker run -d nginx
Unable to find image 'nginx' locally
Pulling repository nginx
5c82215b03d1: Download complete
e2a4fb18da48: Download complete
58016a5acc80: Download complete
657abfa43d82: Download complete
dcb2fe003d16: Download complete
c79a417d7c6f: Download complete
abb90243122c: Download complete
d6137c9e2964: Download complete
85e566ddc7ef: Download complete
69f100eb42b5: Download complete
cd720b803060: Download complete
7cc81e9a118a: Download complete
docker命令的run選項(xiàng),用來通知 Docker 去尋找一個(gè)指定的 Docker 鏡像,然后啟動(dòng)運(yùn)行著該鏡像的容器。默認(rèn)情況下,Docker 容器運(yùn)行在前臺(tái),這意味著當(dāng)你運(yùn)行docker run命令的時(shí)候,你的 shell 會(huì)被綁定到容器的控制臺(tái)以及運(yùn)行在容器中的進(jìn)程。為了能在后臺(tái)運(yùn)行該 Docker 容器,我們使用了-d(detach)標(biāo)志。
再次運(yùn)行docker ps命令,可以看到 nginx 容器正在運(yùn)行。
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f6d31ab01fc9 nginx:latest nginx -g 'daemon off 4 seconds ago Up 3 seconds 443/tcp, 80/tcp desperate_lalande
從上面的輸出信息中,我們可以看到正在運(yùn)行的名為desperate_lalande的容器,它是由nginx:latest image(LCTT 譯注: nginx 最新版本的鏡像)構(gòu)建而來得。
Docker 鏡像
鏡像是 Docker 的核心特征之一,類似于虛擬機(jī)鏡像。和虛擬機(jī)鏡像一樣,Docker 鏡像是一個(gè)被保存并打包的容器。當(dāng)然,Docker 不只是創(chuàng)建鏡像,它還可以通過 Docker 倉庫發(fā)布這些鏡像,Docker 倉庫和軟件包倉庫的概念差不多,它讓 Docker 能夠模仿yum部署軟件包的方式來部署鏡像。為了更好地理解這是怎么工作的,我們來回顧docker run執(zhí)行后的輸出。
# docker run -d nginx
Unable to find image 'nginx' locally
我們可以看到第一條信息是,Docker 不能在本地找到名叫 nginx 的鏡像。這是因?yàn)楫?dāng)我們執(zhí)行docker run命令時(shí),告訴 Docker 運(yùn)行一個(gè)基于 nginx 鏡像的容器。既然 Docker 要啟動(dòng)一個(gè)基于特定鏡像的容器,那么 Docker 首先需要找到那個(gè)指定鏡像。在檢查遠(yuǎn)程倉庫之前,Docker 首先檢查本地是否存在指定名稱的本地鏡像。
因?yàn)橄到y(tǒng)是嶄新的,不存在 nginx 鏡像,Docker 將選擇從 Docker 倉庫下載之。
Pulling repository nginx
5c82215b03d1: Download complete
e2a4fb18da48: Download complete
58016a5acc80: Download complete
657abfa43d82: Download complete
dcb2fe003d16: Download complete
c79a417d7c6f: Download complete
abb90243122c: Download complete
d6137c9e2964: Download complete
85e566ddc7ef: Download complete
69f100eb42b5: Download complete
cd720b803060: Download complete
7cc81e9a118a: Download complete
這就是第二部分輸出信息顯示給我們的內(nèi)容。默認(rèn)情況下,Docker 會(huì)使用 Docker Hub[3] 倉庫,該倉庫由 Docker 公司維護(hù)。
和 Github 一樣,在 Docker Hub 創(chuàng)建公共倉庫是免費(fèi)的,私人倉庫就需要繳納費(fèi)用了。當(dāng)然,部署你自己的 Docker 倉庫也是可以的,事實(shí)上只需要簡單地運(yùn)行docker run registry命令就行了。但在這篇文章中,我們的重點(diǎn)將不是講解如何部署一個(gè)定制的注冊(cè)服務(wù)。
關(guān)閉并移除容器
在我們繼續(xù)構(gòu)建定制容器之前,我們先清理一下 Docker 環(huán)境,我們將關(guān)閉先前的容器,并移除它。
我們利用docker命令和run選項(xiàng)運(yùn)行一個(gè)容器,所以,為了停止同一個(gè)容器,我們簡單地在執(zhí)行docker命令時(shí),使用kill選項(xiàng),并指定容器名。
# docker kill desperate_lalande
desperate_lalande
當(dāng)我們?cè)俅螆?zhí)行docker ps,就不再有容器運(yùn)行了
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
但是,此時(shí),我們這是停止了容器;雖然它不再運(yùn)行,但仍然存在。默認(rèn)情況下,docker ps只會(huì)顯示正在運(yùn)行的容器,如果我們附加-a(all) 標(biāo)識(shí),它會(huì)顯示所有運(yùn)行和未運(yùn)行的容器。
# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f6d31ab01fc9 5c82215b03d1 nginx -g 'daemon off 4 weeks ago Exited (-1) About a minute ago desperate_lalande
為了能完整地移除容器,我們?cè)谟胐ocker命令時(shí),附加rm選項(xiàng)。
# docker rm desperate_lalande
desperate_lalande
雖然容器被移除了;但是我們?nèi)該碛锌捎玫?span style="border:0px;font-style:inherit;font-variant:inherit;font-weight:700;line-height:inherit;vertical-align:baseline;">nginx鏡像(LCTT 譯注:鏡像緩存)。如果我們重新運(yùn)行docker run -d nginx,Docker 就無需再次拉取 nginx 鏡像即可啟動(dòng)容器。這是因?yàn)槲覀儽镜叵到y(tǒng)中已經(jīng)保存了一個(gè)副本。
為了列出系統(tǒng)中所有的本地鏡像,我們運(yùn)行docker命令,附加images選項(xiàng)。
# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
nginx latest 9fab4090484a 5 days ago 132.8 MB
構(gòu)建我們自己的鏡像
截至目前,我們已經(jīng)使用了一些基礎(chǔ)的 Docker 命令來啟動(dòng)、停止和移除一個(gè)預(yù)構(gòu)建好的普通鏡像。為了“Docker 化(Dockerize)”這篇博客,我們需要構(gòu)建我們自己的鏡像,也就是創(chuàng)建一個(gè)?Dockerfile。
在大多數(shù)虛擬機(jī)環(huán)境中,如果你想創(chuàng)建一個(gè)機(jī)器鏡像,首先,你需要建立一個(gè)新的虛擬機(jī)、安裝操作系統(tǒng)、安裝應(yīng)用,最后將其轉(zhuǎn)換為一個(gè)模板或者鏡像。但在 Docker 中,所有這些步驟都可以通過 Dockerfile 實(shí)現(xiàn)全自動(dòng)。Dockerfile 是向 Docker 提供構(gòu)建指令去構(gòu)建定制鏡像的方式。在這一章節(jié),我們將編寫能用來部署這個(gè)博客的定制 Dockerfile。
理解應(yīng)用
我們開始構(gòu)建 Dockerfile 之前,第一步要搞明白,我們需要哪些東西來部署這個(gè)博客。
這個(gè)博客本質(zhì)上是由一個(gè)靜態(tài)站點(diǎn)生成器生成的靜態(tài) HTML 頁面,這個(gè)生成器是我編寫的,名為?hamerkop。這個(gè)生成器很簡單,它所做的就是生成該博客站點(diǎn)。所有的代碼和源文件都被我放在了一個(gè)公共的Github 倉庫[4]。為了部署這篇博客,我們要先從 Github 倉庫把這些內(nèi)容拉取下來,然后安裝Python和一些Python模塊,最后執(zhí)行hamerkop應(yīng)用。我們還需要安裝nginx,來運(yùn)行生成后的內(nèi)容。
截止目前,這些還是一個(gè)簡單的 Dockerfile,但它卻給我們展示了相當(dāng)多的 Dockerfile 語法[5])。我們需要克隆 Github 倉庫,然后使用你最喜歡的編輯器編寫 Dockerfile,我選擇vi。
# git clone https://github.com/madflojo/blog.git
Cloning into 'blog'...
remote: Counting objects: 622, done.
remote: Total 622 (delta 0), reused 0 (delta 0), pack-reused 622
Receiving objects: 100% (622/622), 14.80 MiB | 1.06 MiB/s, done.
Resolving deltas: 100% (242/242), done.
Checking connectivity... done.
# cd blog/
# vi Dockerfile
FROM - 繼承一個(gè) Docker 鏡像
第一條 Dockerfile 指令是FROM指令。這將指定一個(gè)現(xiàn)存的鏡像作為我們的基礎(chǔ)鏡像。這也從根本上給我們提供了繼承其他 Docker 鏡像的途徑。在本例中,我們還是從剛剛我們使用的nginx開始,如果我們想從頭開始,我們可以通過指定ubuntu:latest來使用UbuntuDocker 鏡像。
## Dockerfile that generates an instance of http://bencane.com
FROM nginx:latest
MAINTAINER Benjamin Cane <ben@bencane.com>
除了FROM指令,我還使用了MAINTAINER,它用來顯示 Dockerfile 的作者。
Docker 支持使用#作為注釋,我將經(jīng)常使用該語法,來解釋 Dockerfile 的部分內(nèi)容。
運(yùn)行一次測試構(gòu)建
因?yàn)槲覀兝^承了?nginxDocker鏡像,我們現(xiàn)在的 Dockerfile 也就包括了用來構(gòu)建nginx鏡像的Dockerfile[6] 中所有指令。這意味著,此時(shí)我們可以從該 Dockerfile 中構(gòu)建出一個(gè) Docker 鏡像,然后以該鏡像運(yùn)行一個(gè)容器。雖然,最終的鏡像和nginx鏡像本質(zhì)上是一樣的,但是我們這次是通過構(gòu)建 Dockerfile 的形式,然后我們將講解 Docker 構(gòu)建鏡像的過程。
想要從 Dockerfile 構(gòu)建鏡像,我們只需要在運(yùn)行?docker命令的時(shí)候,加上build選項(xiàng)。
# docker build -t blog /root/blog
Sending build context to Docker daemon 23.6 MB
Sending build context to Docker daemon
Step 0 : FROM nginx:latest
---> 9fab4090484a
Step 1 : MAINTAINER Benjamin Cane <ben@bencane.com>
---> Running in c97f36450343
---> 60a44f78d194
Removing intermediate container c97f36450343
Successfully built 60a44f78d194
上面的例子,我們使用了-t(tag)標(biāo)識(shí)給鏡像添加“blog”的標(biāo)簽。實(shí)質(zhì)上我們就是在給鏡像命名,如果我們不指定標(biāo)簽,就只能通過 Docker 分配的Image ID來訪問鏡像了。本例中,從 Docker 構(gòu)建成功的信息可以看出,Image ID值為60a44f78d194。
除了-t標(biāo)識(shí)外,我還指定了目錄/root/blog。該目錄被稱作“構(gòu)建目錄”,它將包含 Dockerfile,以及其它需要構(gòu)建該容器的文件。
現(xiàn)在我們構(gòu)建成功了,下面我們開始定制該鏡像。
使用 RUN 來執(zhí)行 apt-get
用來生成 HTML 頁面的靜態(tài)站點(diǎn)生成器是用?Python語言編寫的,所以,在 Dockerfile 中需要做的第一件定制任務(wù)是安裝 Python。我們將使用 Apt 軟件包管理器來安裝 Python 軟件包,這意味著在 Dockerfile 中我們要指定運(yùn)行apt-get update和apt-get install python-dev;為了完成這一點(diǎn),我們可以使用RUN指令。
## Dockerfile that generates an instance of http://bencane.com
FROM nginx:latest
MAINTAINER Benjamin Cane <ben@bencane.com>
## Install python and pip
RUN apt-get update
RUN apt-get install -y python-dev python-pip
如上所示,我們只是簡單地告知 Docker 構(gòu)建鏡像的時(shí)候,要去執(zhí)行指定的apt-get命令。比較有趣的是,這些命令只會(huì)在該容器的上下文中執(zhí)行。這意味著,即使在容器中安裝了python-dev和python-pip,但主機(jī)本身并沒有安裝這些。說的更簡單點(diǎn),pip命令將只在容器中執(zhí)行,出了容器,pip命令不存在。
還有一點(diǎn)比較重要的是,Docker 構(gòu)建過程中不接受用戶輸入。這說明任何被RUN指令執(zhí)行的命令必須在沒有用戶輸入的時(shí)候完成。由于很多應(yīng)用在安裝的過程中需要用戶的輸入信息,所以這增加了一點(diǎn)難度。不過我們例子中,RUN命令執(zhí)行的命令都不需要用戶輸入。
安裝 Python 模塊
Python安裝完畢后,我們現(xiàn)在需要安裝 Python 模塊。如果在 Docker 外做這些事,我們通常使用pip命令,然后參考我的博客 Git 倉庫中名叫requirements.txt的文件。在之前的步驟中,我們已經(jīng)使用git命令成功地將 Github 倉庫“克隆”到了/root/blog目錄;這個(gè)目錄碰巧也是我們創(chuàng)建Dockerfile的目錄。這很重要,因?yàn)檫@意味著 Docker 在構(gòu)建過程中可以訪問這個(gè) Git 倉庫中的內(nèi)容。
當(dāng)我們執(zhí)行構(gòu)建后,Docker 將構(gòu)建的上下文環(huán)境設(shè)置為指定的“構(gòu)建目錄”。這意味著目錄中的所有文件都可以在構(gòu)建過程中被使用,目錄之外的文件(構(gòu)建環(huán)境之外)是不能訪問的。
為了能安裝所需的 Python 模塊,我們需要將requirements.txt從構(gòu)建目錄拷貝到容器中。我們可以在Dockerfile中使用COPY指令完成這一需求。
## Dockerfile that generates an instance of http://bencane.com
FROM nginx:latest
MAINTAINER Benjamin Cane <ben@bencane.com>
## Install python and pip
RUN apt-get update
RUN apt-get install -y python-dev python-pip
## Create a directory for required files
RUN mkdir -p /build/
## Add requirements file and run pip
COPY requirements.txt /build/
RUN pip install -r /build/requirements.txt
在Dockerfile中,我們?cè)黾恿?條指令。第一條指令使用RUN在容器中創(chuàng)建了/build/目錄。該目錄用來拷貝生成靜態(tài) HTML 頁面所需的一切應(yīng)用文件。第二條指令是COPY指令,它將requirements.txt從“構(gòu)建目錄”(/root/blog)拷貝到容器中的/build/目錄。第三條使用RUN指令來執(zhí)行pip命令;安裝requirements.txt文件中指定的所有模塊。
當(dāng)構(gòu)建定制鏡像時(shí),COPY是條重要的指令。如果在 Dockerfile 中不指定拷貝文件,Docker 鏡像將不會(huì)包含requirements.txt 這個(gè)文件。在 Docker 容器中,所有東西都是隔離的,除非在 Dockerfile 中指定執(zhí)行,否則容器中不會(huì)包括所需的依賴。
重新運(yùn)行構(gòu)建
現(xiàn)在,我們讓 Docker 執(zhí)行了一些定制任務(wù),現(xiàn)在我們嘗試另一次 blog 鏡像的構(gòu)建。
# docker build -t blog /root/blog
Sending build context to Docker daemon 19.52 MB
Sending build context to Docker daemon
Step 0 : FROM nginx:latest
---> 9fab4090484a
Step 1 : MAINTAINER Benjamin Cane <ben@bencane.com>
---> Using cache
---> 8e0f1899d1eb
Step 2 : RUN apt-get update
---> Using cache
---> 78b36ef1a1a2
Step 3 : RUN apt-get install -y python-dev python-pip
---> Using cache
---> ef4f9382658a
Step 4 : RUN mkdir -p /build/
---> Running in bde05cf1e8fe
---> f4b66e09fa61
Removing intermediate container bde05cf1e8fe
Step 5 : COPY requirements.txt /build/
---> cef11c3fb97c
Removing intermediate container 9aa8ff43f4b0
Step 6 : RUN pip install -r /build/requirements.txt
---> Running in c50b15ddd8b1
Downloading/unpacking jinja2 (from -r /build/requirements.txt (line 1))
Downloading/unpacking PyYaml (from -r /build/requirements.txt (line 2))
<truncated to reduce noise>
Successfully installed jinja2 PyYaml mistune markdown MarkupSafe
Cleaning up...
---> abab55c20962
Removing intermediate container c50b15ddd8b1
Successfully built abab55c20962
上述輸出所示,我們可以看到構(gòu)建成功了,我們還可以看到另外一個(gè)有趣的信息---> Using cache。這條信息告訴我們,Docker 在構(gòu)建該鏡像時(shí)使用了它的構(gòu)建緩存。
Docker 構(gòu)建緩存
當(dāng) Docker 構(gòu)建鏡像時(shí),它不僅僅構(gòu)建一個(gè)單獨(dú)的鏡像;事實(shí)上,在構(gòu)建過程中,它會(huì)構(gòu)建許多鏡像。從上面的輸出信息可以看出,在每一“步”執(zhí)行后,Docker 都在創(chuàng)建新的鏡像。
Step 5 : COPY requirements.txt /build/
---> cef11c3fb97c
上面片段的最后一行可以看出,Docker 在告訴我們它在創(chuàng)建一個(gè)新鏡像,因?yàn)樗蛴×?span style="border:0px;font-style:inherit;font-variant:inherit;font-weight:700;line-height:inherit;vertical-align:baseline;">Image ID:cef11c3fb97c。這種方式有用之處在于,Docker能在隨后構(gòu)建這個(gè)blog鏡像時(shí)將這些鏡像作為緩存使用。這很有用處,因?yàn)檫@樣, Docker 就能加速同一個(gè)容器中新構(gòu)建任務(wù)的構(gòu)建流程。從上面的例子中,我們可以看出,Docker 沒有重新安裝python-dev和python-pip包,Docker 則使用了緩存鏡像。但是由于 Docker 并沒有找到執(zhí)行mkdir命令的構(gòu)建緩存,隨后的步驟就被一一執(zhí)行了。
Docker 構(gòu)建緩存一定程度上是福音,但有時(shí)也是噩夢(mèng)。這是因?yàn)闆Q定使用緩存或者重新運(yùn)行指令的因素很少。比如,如果requirements.txt文件發(fā)生了修改,Docker 會(huì)在構(gòu)建時(shí)檢測到該變化,然后 Docker 會(huì)重新執(zhí)行該執(zhí)行那個(gè)點(diǎn)往后的所有指令。這得益于 Docker 能查看requirements.txt的文件內(nèi)容。但是,apt-get命令的執(zhí)行就是另一回事了。如果提供 Python 軟件包的Apt倉庫包含了一個(gè)更新的 python-pip 包;Docker 不會(huì)檢測到這個(gè)變化,轉(zhuǎn)而去使用構(gòu)建緩存。這會(huì)導(dǎo)致之前舊版本的包將被安裝。雖然對(duì)python-pip來說,這不是主要的問題,但對(duì)使用了存在某個(gè)致命攻擊缺陷的軟件包緩存來說,這是個(gè)大問題。
出于這個(gè)原因,拋棄 Docker 緩存,定期地重新構(gòu)建鏡像是有好處的。這時(shí),當(dāng)我們執(zhí)行 Docker 構(gòu)建時(shí),我簡單地指定--no-cache=True即可。
部署博客的剩余部分
Python 軟件包和模塊安裝后,接下來我們將拷貝需要用到的應(yīng)用文件,然后運(yùn)行hamerkop應(yīng)用。我們只需要使用更多的COPY和RUN指令就可完成。
## Dockerfile that generates an instance of http://bencane.com
FROM nginx:latest
MAINTAINER Benjamin Cane <ben@bencane.com>
## Install python and pip
RUN apt-get update
RUN apt-get install -y python-dev python-pip
## Create a directory for required files
RUN mkdir -p /build/
## Add requirements file and run pip
COPY requirements.txt /build/
RUN pip install -r /build/requirements.txt
## Add blog code nd required files
COPY static /build/static
COPY templates /build/templates
COPY hamerkop /build/
COPY config.yml /build/
COPY articles /build/articles
## Run Generator
RUN /build/hamerkop -c /build/config.yml
現(xiàn)在我們已經(jīng)寫出了剩余的構(gòu)建指令,我們?cè)俅芜\(yùn)行另一次構(gòu)建,并確保鏡像構(gòu)建成功。
# docker build -t blog /root/blog/
Sending build context to Docker daemon 19.52 MB
Sending build context to Docker daemon
Step 0 : FROM nginx:latest
---> 9fab4090484a
Step 1 : MAINTAINER Benjamin Cane <ben@bencane.com>
---> Using cache
---> 8e0f1899d1eb
Step 2 : RUN apt-get update
---> Using cache
---> 78b36ef1a1a2
Step 3 : RUN apt-get install -y python-dev python-pip
---> Using cache
---> ef4f9382658a
Step 4 : RUN mkdir -p /build/
---> Using cache
---> f4b66e09fa61
Step 5 : COPY requirements.txt /build/
---> Using cache
---> cef11c3fb97c
Step 6 : RUN pip install -r /build/requirements.txt
---> Using cache
---> abab55c20962
Step 7 : COPY static /build/static
---> 15cb91531038
Removing intermediate container d478b42b7906
Step 8 : COPY templates /build/templates
---> ecded5d1a52e
Removing intermediate container ac2390607e9f
Step 9 : COPY hamerkop /build/
---> 59efd1ca1771
Removing intermediate container b5fbf7e817b7
Step 10 : COPY config.yml /build/
---> bfa3db6c05b7
Removing intermediate container 1aebef300933
Step 11 : COPY articles /build/articles
---> 6b61cc9dde27
Removing intermediate container be78d0eb1213
Step 12 : RUN /build/hamerkop -c /build/config.yml
---> Running in fbc0b5e574c5
Successfully created file /usr/share/nginx/html//2011/06/25/checking-the-number-of-lwp-threads-in-linux
Successfully created file /usr/share/nginx/html//2011/06/checking-the-number-of-lwp-threads-in-linux
<truncated to reduce noise>
Successfully created file /usr/share/nginx/html//archive.html
Successfully created file /usr/share/nginx/html//sitemap.xml
---> 3b25263113e1
Removing intermediate container fbc0b5e574c5
Successfully built 3b25263113e1
運(yùn)行定制的容器
成功的一次構(gòu)建后,我們現(xiàn)在就可以通過運(yùn)行docker命令和run選項(xiàng)來運(yùn)行我們定制的容器,和之前我們啟動(dòng) nginx 容器一樣。
# docker run -d -p 80:80 --name=blog blog
5f6c7a2217dcdc0da8af05225c4d1294e3e6bb28a41ea898a1c63fb821989ba1
我們這次又使用了-d(detach)標(biāo)識(shí)來讓Docker在后臺(tái)運(yùn)行。但是,我們也可以看到兩個(gè)新標(biāo)識(shí)。第一個(gè)新標(biāo)識(shí)是--name,這用來給容器指定一個(gè)用戶名稱。之前的例子,我們沒有指定名稱,因?yàn)?Docker 隨機(jī)幫我們生成了一個(gè)。第二個(gè)新標(biāo)識(shí)是-p,這個(gè)標(biāo)識(shí)允許用戶從主機(jī)映射一個(gè)端口到容器中的一個(gè)端口。
之前我們使用的基礎(chǔ)?nginx鏡像分配了80端口給 HTTP 服務(wù)。默認(rèn)情況下,容器內(nèi)的端口通道并沒有綁定到主機(jī)系統(tǒng)。為了讓外部系統(tǒng)能訪問容器內(nèi)部端口,我們必須使用-p標(biāo)識(shí)將主機(jī)端口映射到容器內(nèi)部端口。上面的命令,我們通過-p 8080:80語法將主機(jī)80端口映射到容器內(nèi)部的80端口。
經(jīng)過上面的命令,我們的容器看起來成功啟動(dòng)了,我們可以通過執(zhí)行docker ps核實(shí)。
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d264c7ef92bd blog:latest nginx -g 'daemon off 3 seconds ago Up 3 seconds 443/tcp, 0.0.0.0:80->80/tcp blog
總結(jié)
截止目前,我們擁有了一個(gè)運(yùn)行中的定制 Docker 容器。雖然在這篇文章中,我們只接觸了一些 Dockerfile 指令用法,但是我們還是要學(xué)習(xí)所有的指令。我們可以檢查 Docker's reference page[7] 來獲取所有的 Dockerfile 指令用法,那里對(duì)指令的用法說明得很詳細(xì)。
另一個(gè)比較好的資源是 Dockerfile Best Practices page[8],它有許多構(gòu)建定制 Dockerfile 的最佳練習(xí)。有些技巧非常有用,比如戰(zhàn)略性地組織好 Dockerfile 中的命令。上面的例子中,我們將articles目錄的COPY指令作為 Dockerfile 中最后的COPY指令。這是因?yàn)閍rticles目錄會(huì)經(jīng)常變動(dòng)。所以,將那些經(jīng)常變化的指令盡可能地放在最后面的位置,來最優(yōu)化那些可以被緩存的步驟。
轉(zhuǎn)載于:https://my.oschina.net/zhanghaiyang/blog/725744
總結(jié)
以上是生活随笔為你收集整理的Docker学习总结(6)——通过 Docker 化一个博客网站来开启我们的 Docker 之旅的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux基础(6)-shell编程
- 下一篇: 蚂蚁金服高级技术专家徐红星 :蚂蚁金服大