Docker 學(xué)習(xí)篇(六)
1、數(shù)據(jù)卷容器
(1)數(shù)據(jù)卷容器和容器數(shù)據(jù)卷
在上一節(jié),我們講了何為容器數(shù)據(jù)卷,當(dāng)我們使用 docker run -it -v /主機(jī)絕對(duì)路徑目錄:/容器內(nèi)目錄 鏡像名,-v 就是創(chuàng)建一個(gè)容器的數(shù)據(jù)卷,它的作用是把容器內(nèi)的數(shù)據(jù)持久化到宿主機(jī)上,當(dāng)容器內(nèi)數(shù)據(jù)發(fā)生變化,宿主機(jī)相應(yīng)位置的數(shù)據(jù)也會(huì)發(fā)生變化。
有兩個(gè)容器 dc01 和 dc02,我們使用 dc02 volume-from dc01 這個(gè)命令,就可以實(shí)現(xiàn)容器1和容器2之間的數(shù)據(jù)共享,類似于一種繼承關(guān)系,數(shù)據(jù)卷容器就是被 volume-from 的那個(gè)容器,即 dc01。
(2)舉個(gè)例子來講解數(shù)據(jù)卷容器
1、運(yùn)行我們創(chuàng)建的新鏡像 zhouxuejiao/centos 生成一個(gè)容器,命名為 dc01,docker run -it --name dc01 zhouxuejiao/centos。

在 dc01 內(nèi)部的容器數(shù)據(jù)卷 dataVolumeContainer2 中在增加一個(gè)文件 dc01_add.txt。

2、退出 dc01 容器,只退出,不停止運(yùn)行,使用 Ctrl+P+Q。
3、讓 dc02 和 dc03 分別繼承 dc01,
docker run -it --name dc02 --volumes-from dc01 zhouxuejiao/centos
docker run -it --name dc03 --volumes-from dc01 zhouxuejiao/centos
此時(shí) dc02 和 dc01 可以實(shí)現(xiàn)數(shù)據(jù)的共享,dc03 和 dc01 可以實(shí)現(xiàn)數(shù)據(jù)的共享,dc02 和 dc03 也可以實(shí)現(xiàn)數(shù)據(jù)的共享(因?yàn)?dc02 和 dc03 都繼承自 dc01,dc01 相當(dāng)于一個(gè)中介,所以 dc02 和 dc03 也可以實(shí)現(xiàn)數(shù)據(jù)的共享)。
4、此時(shí)提出一個(gè)問題,當(dāng)我們刪除父容器 dc01,dc02 和 dc03 還可以進(jìn)行數(shù)據(jù)的互享嗎?
答案是可以的,刪除父容器 dc01 后,dc02 和 dc03 仍然存在且可以進(jìn)行數(shù)據(jù)共享。這是因?yàn)槿萜髦g信息的傳遞,數(shù)據(jù)卷的生命周期一直持續(xù)到?jīng)]有容器使用它為止。
2、DockerFile
(1)DockerFile 是什么?
DockerFile 是用來構(gòu)建 Docker 鏡像的構(gòu)建文件,是由一系列命令和參數(shù)構(gòu)成的腳本。
(2)DockerFile 構(gòu)建的三步驟
第一步:編寫。手動(dòng)編寫一個(gè) DockerFile 文件,必須符合 DockerFile 的規(guī)范。
第二步:構(gòu)建。有了 DockerFile 文件后,直接執(zhí)行 docker build DockerFile 這個(gè)命令,即可獲得一個(gè)自定義的鏡像。
第三步:運(yùn)行。使用 docker run 運(yùn)行上一步得到的鏡像。
(3)DockerFile 文件長(zhǎng)什么樣?
以 centos 鏡像的 DockerFile 文件為例,見圖3,我們來看一下 DockerFile 文件長(zhǎng)什么樣。

scratch:所有鏡像的父鏡像,類似于Java中的Object是所有類的根父類。
MAINTAINER:作者及郵箱。
LABEL:說明。
(4)編寫 DockerFile 的規(guī)范
(1)每條保留字指令都必須為大寫字母,且后面跟隨至少一個(gè)參數(shù),以FROM scratch為例,F(xiàn)ROM就是保留字指令,其后面必須跟一個(gè)參數(shù),即scratch,否則會(huì)報(bào)錯(cuò),dockerfile的語(yǔ)法有問題。
(2)指令按照從上到下,順序執(zhí)行。
(3)#表示注釋。
(4)每條指令都會(huì)創(chuàng)建一個(gè)新的鏡像層(之前講過,鏡像是分層的),并對(duì)鏡像進(jìn)行提交。
(5)實(shí)際開發(fā)部署場(chǎng)景中,DockerFile 是如何使用的?
首先開發(fā)人員將要部署的程序編寫到 DockerFile 中,并將其 build 成一個(gè)鏡像,然后開發(fā)人員將鏡像交給運(yùn)維人員,最后運(yùn)維人員運(yùn)行鏡像,生成容器實(shí)例,此時(shí)程序就開始提供服務(wù)了。
(6)DockerFile 保留字指令
FROM:當(dāng)前編寫這個(gè)鏡像是基于哪個(gè)鏡像創(chuàng)建的,即它的父鏡像是誰(shuí)。
MAINTAINER:鏡像維護(hù)者的姓名和郵箱。
RUN:容器構(gòu)建過程中需要執(zhí)行的一些命令,類似于java程序中的System.ou.println,java中是會(huì)打印到控制臺(tái),DockerFile 運(yùn)行的時(shí)候,這些語(yǔ)句也會(huì)打印在命令行界面里。
EXPOSE:該鏡像變成容器以后,對(duì)外暴漏的接口,比如 centos 的默認(rèn)端口是 6379,見圖4。

WORKDIR:運(yùn)行鏡像成為一個(gè)容器,登錄容器終端直接進(jìn)入到的目錄。以下面的centos為例,容器終端直接進(jìn)入的是容器的根目錄/,見圖5。

ENV:用來在構(gòu)建鏡像的過程中設(shè)置環(huán)境變量。比如
ENV MY_PATH /usr/mytest // 定義環(huán)境變量 MY_PATH,其值為 /usr/mytest
WORKDIR $MY_PATH // 引用上面定義的環(huán)境變量,引用時(shí)環(huán)境變量前面需要加上$,這句話等價(jià)于 WORKDIR /usr/mytest
ADD:拷貝文件或目錄進(jìn)鏡像,ADD 命令不僅僅會(huì)拷貝,還會(huì)解壓 tar 壓縮包。以 centos 的 DockerFile 為例,它拷貝并解壓 docker.tar 到鏡像中。

COPY:類似ADD,拷貝文件和目錄到鏡像中,但是它只是拷貝,不會(huì)解壓 tar包。COPY 將從構(gòu)建上下文目錄中<源路徑>的文件復(fù)制到新的一層的鏡像內(nèi)的<目標(biāo)路徑>位置。它的使用方法有兩種:COPY src test和COPY ["src","dest"]。
VOLUME:容器數(shù)據(jù)卷,用于數(shù)據(jù)保存和持久化工作。
CMD:指定一個(gè)容器啟動(dòng)時(shí)要運(yùn)行的命令,比如 centos 中的 CMD ["/bin/bash"] 相當(dāng)于 docker run -it centos /bin/bash。DockerFile 中可以有多個(gè) CMD 指令,但只有最后一個(gè)生效,比如CMD ["/bin/bash"] CMD cat /etc/hosts,最后cat /etc/hosts 會(huì)覆蓋 /bin/bash。
ENTRYPOINT:指定一個(gè)容器啟動(dòng)時(shí)要運(yùn)行的命令,功能與 CMD 一樣,和 CMD 不同的是,當(dāng)有多條 ENTRYPOINT 指令時(shí),不會(huì)出現(xiàn)覆蓋,而是追加執(zhí)行。
ONBUILD:父鏡像在被子鏡像繼承的時(shí)候,父鏡像 DockerFile 中的 ONBUIL 命令會(huì)被觸發(fā)執(zhí)行。
接下來我們將用下面的幾個(gè)案例,對(duì)這幾個(gè)保留字進(jìn)行說明。
3、自定義鏡像 mycentos
(1)、編寫 DockerFile
阿里云上拉取下來的 centos,進(jìn)入容器的默認(rèn)路徑是/,且不支持 vim 和 ifconfig 命令。
我們來自定義一個(gè) mycentos 使其具備如下特性:進(jìn)入容器的默認(rèn)路徑不再是根目錄,而是 /usr/local 目錄,支持vim編譯器,支持ifconfig查看網(wǎng)絡(luò)配置。
DockerFile如下:
FROM centos // 我們以本地的 centos 為父鏡像,增加一些功能,形成新鏡像 mycentos。
MAINTAINER zhoxuuejiao<[email protected]>
ENV MYPATH /usr/local
WORKDIR $MYPATH // 進(jìn)入容器默認(rèn)目錄是 /usr/local
RUN yum -y install vim
RUN yum -y install net-tools // RUN 容器構(gòu)建時(shí)執(zhí)行的命令,所以我們讓容器運(yùn)行時(shí),安裝 vim 編譯器、網(wǎng)絡(luò)查看工具
EXPOSE 80 // 向外暴漏 80 端口
CMD echo $MYPATH
CMD echo "success.............ok" // 打印
CMD /bin/bash


(2)、根據(jù) DockerFile 構(gòu)建鏡像,docker build -f /mydocker/Dockerfile -t mycentos .
構(gòu)建過程的日志見圖9至圖15。







構(gòu)建完成,使用 dcoker images 查看所有鏡像,發(fā)現(xiàn) mycentos 鏡像的鏡像 ID 和日志中(圖15)的一樣。

(3)、運(yùn)行 mycentos 鏡像,得到 mycentos 容器
此時(shí),進(jìn)入容器的默認(rèn)路徑是我們?cè)?Dokcerfile 中指定的 /usr/local,見圖17。

此時(shí),在容器中可以使用 vim 編譯器,見圖18、19,vim test.txt,創(chuàng)建并編譯 test.txt 這個(gè)文件。


此時(shí),使用 ifconfig 查看網(wǎng)絡(luò)信息,也是可以的,見圖20。

至此,我們使用 DockerFile 創(chuàng)建 mycentos 鏡像,創(chuàng)建成功。
(4)、查看鏡像的創(chuàng)建歷史。
docker histor 鏡像ID,見圖21,首先根據(jù) centos 生成一個(gè) base 鏡像,然后每執(zhí)行一句 DockerFile 中的指令,就會(huì)產(chǎn)生一個(gè)新的鏡像,包裹在前面生成的鏡像的外面,再次驗(yàn)證了鏡像的分層結(jié)構(gòu)。

4、DockerFile 中的 CMD 和 ENTRYPOINT 指令
通過上一個(gè)案例,我們大致了解了一部分保留字指令,但是對(duì)于 CMD 和 ENTRYPOINT 還不是很了解,現(xiàn)在我們通過一個(gè)案例來講解一下這兩個(gè)保留字。
(1)、二者的共同點(diǎn)
CMD 和 ENTRYPOINT,都是指定一個(gè)容器啟動(dòng)時(shí)要運(yùn)行的命令。
(2)、二者的不同點(diǎn)
1、CMD:DockerFile 中可以有多個(gè) CMD 指令,但只有最后一個(gè)生效,CMD 會(huì)被 docker run 之后的參數(shù)替換。
使用一個(gè)案例,來解釋 “CMD 會(huì)被 docker run 之后的參數(shù)替換”,這句話是什么意思?
當(dāng)我們執(zhí)行 docker run -it -p 7777:8080 tomcat 這條命令啟動(dòng) tomcat 時(shí),tomcat 之所以能夠正常啟動(dòng),完全是因?yàn)槠?DockerFile 最后一行的 CMD ["catalina.sh", run]。
如果我們改一下啟動(dòng)命令,docker run -it -p 7777:8080 tomcat ls -l,就相當(dāng)于在 DockerFile 最后加了一條指令 CMD ls -l,它會(huì)覆蓋掉 CMD ["catalina.sh", run],導(dǎo)致 tomcat 啟動(dòng)不起來。這就是 CMD 會(huì)被 docker run 之后的參數(shù)替換。

2、ENTRYPOINT:功能與 CMD 一樣,和 CMD 不同的是,當(dāng)有多條 ENTRYPOINT 指令時(shí),不會(huì)出現(xiàn)覆蓋,而是追加執(zhí)行。docker run 之后的參數(shù)會(huì)被當(dāng)做參數(shù)傳給 ENTRYPOINT,之后形成新的命令組合。
使用一個(gè)案例來解釋,“docker run 之后的參數(shù)會(huì)被當(dāng)做參數(shù)傳給 ENTRYPOINT,之后形成新的命令組合”,這句話是什么意思?
首先我們使用 CMD 編寫 DockerFile 制作一個(gè)可以查詢百度網(wǎng)站首頁(yè)的容器。
FROM centos
RUN yum install -y curl // 下載 curl 工具,curl 可以用來訪問網(wǎng)址,使用 curl http://www.baidu.com,即可獲得百度網(wǎng)站首頁(yè)的信息。
CMD ["curl", "-s", "https://www.baidu.com"] // crul https://www.baidu.com,訪問百度網(wǎng)站首頁(yè)。


然后根據(jù) DockerFile 構(gòu)建鏡像,docker build -f /mydocker/Dockerfile1 -t mybaidu . 。

最后,運(yùn)行這個(gè)就鏡像,訪問百度網(wǎng)站首頁(yè)。docker run -it mybaidu。

現(xiàn)在我們想要不光返回百度首頁(yè)信息,還希望顯示 HTTP 請(qǐng)求的頭信息,此時(shí)就需要加上 -i 參數(shù)。docker -it mybaidu -i,這樣運(yùn)行是會(huì)失敗的,因?yàn)?DockerFile 用的是 CMD,加上這個(gè) -i 參數(shù),就相當(dāng)于在 DockerFile 莫問加上了 CMD -i,它就把 CMD ["curl", "-s", "https://www.baidu.com"] 這最重要的一句覆蓋了,自然失敗了。

那應(yīng)該怎么辦呢?答案是使用 ENTRYPOINT,因?yàn)樗粫?huì)覆蓋,而是追加執(zhí)行。來我們修改 Dockerfile。
FROM centos
RUN yum install -y curl
ENTRYPOINT ["curl", "-s", "https://www.baidu.com"]


構(gòu)建鏡像,docker build -f /mydocker/Dockerfile2 -t mybaidu2 . 。

運(yùn)行這個(gè)鏡像,訪問百度網(wǎng)站首頁(yè),并且在運(yùn)行命令后加上參數(shù) -i,獲取 HTTP 頭信息。docker run -it mybaidu2 -i。這就相當(dāng)于在 DockerFile 末尾增加一句 ENTRYPOINT -i,因?yàn)?ENTRYPOINT 追加執(zhí)行,所以不會(huì)有任何影響。


(9)DockerFile 中的 ONBUILD 指令
ONBUILD 指令:父鏡像在被子鏡像繼承的時(shí)候,父鏡像 DockerFile 中的 ONBUIL 命令會(huì)被觸發(fā)執(zhí)行。
使用一個(gè)案例,講解 ONBUILD 這個(gè)指令。
首先制作父鏡像 mybaidu_father,DockerFile 如下:
FROM centos
RUN yum install -y curl
ENTRYPOINT ["curl", "-s", "https://www.baidu.com"]
ONBUILD RUN echo "father images onbuild--------666"


構(gòu)建父鏡像,docker build -f /mydocker/Dockerfile3 -t mybaidu_father .。

然后我們制作子鏡像 mybaidu_son,DockerFile 如下:
FROM mybaidu_father // 指定其父鏡像為 mybaidu_father
RUN yum install -y curl
ENTRYPOINT ["curl", "-s", "https://www.baidu.com"]


構(gòu)建子鏡像,docker build -f /mydocker/Dockerfile4 -t mybaidu_son .。
注意注意啦,當(dāng)我們構(gòu)建子鏡像的時(shí)候,會(huì)觸發(fā)父鏡像中的 ONBUILD 指令,圖38是構(gòu)建子鏡像的過程日志,紅色框內(nèi)就是在觸發(fā)父鏡像中的 ONBUILD 指令。

