Docker小白到實(shí)戰(zhàn)之Dockerfile解析及實(shí)戰(zhàn)演示,果然順手
本文轉(zhuǎn)載自微信公眾號(hào)「Code綜藝圈」,作者Code綜藝圈 。轉(zhuǎn)載本文請(qǐng)聯(lián)系Code綜藝圈公眾號(hào)。
前言
使用第三方鏡像肯定不是學(xué)習(xí)Docker的最終目的,最想要的還是自己構(gòu)建鏡像;將自己的程序、文件、環(huán)境等構(gòu)建成自己想要的應(yīng)用鏡像,方便后續(xù)部署、啟動(dòng)和維護(hù);而Dockerfile就是專門(mén)做這個(gè)事的,通過(guò)類似簡(jiǎn)單編碼的形式,最終就可以構(gòu)建出屬于自己的鏡像,所以必須學(xué)起來(lái)。
正文
1. Dockerfile簡(jiǎn)介
在日常開(kāi)發(fā)過(guò)程中,需要編寫(xiě)對(duì)應(yīng)的程序文件,最后通過(guò)編譯打包生成對(duì)應(yīng)的可執(zhí)行文件或是類庫(kù);這里的Dockerfile文件就好比平時(shí)我們編寫(xiě)的程序文件,但內(nèi)部的語(yǔ)法和關(guān)鍵字并沒(méi)有程序那么復(fù)雜和繁多,相對(duì)來(lái)說(shuō)還是很簡(jiǎn)單的,最后通過(guò)docker build命令就可以將對(duì)應(yīng)的程序、文件、環(huán)境等構(gòu)建成鏡像啦。
在第一篇文章最后就簡(jiǎn)單使用了Dockerfile構(gòu)建了一個(gè)鏡像,這里重新認(rèn)識(shí)下這個(gè)Dockerfile文件,如下圖:
Dockerfile就是一個(gè)文本文件,但不需要指定后綴類型;文件內(nèi)容中FROM、WORKDIR、COPY等就是關(guān)鍵字,按照規(guī)則寫(xiě)好之后,就可以將指定的文件構(gòu)建為鏡像啦。
構(gòu)建操作統(tǒng)一由Docker daemon進(jìn)行,它會(huì)先對(duì)文件內(nèi)容語(yǔ)法進(jìn)行初步驗(yàn)證(語(yǔ)法不對(duì)就會(huì)返回錯(cuò)誤信息),然后逐一運(yùn)行指令,每次生成一個(gè)新的鏡像層,直到執(zhí)行完所有指令,就構(gòu)建出最終的鏡像。 Dockerfile、鏡像、容器的關(guān)系如下:
總結(jié)一下Dockerfile的知識(shí)點(diǎn);
- 構(gòu)建時(shí),指令從上到下逐一執(zhí)行;
- 每條指令都會(huì)創(chuàng)建一個(gè)新的鏡像層,每一層都是前一層變化的增量;
- 使用#號(hào)進(jìn)行注釋;
- 關(guān)鍵字約定都是大寫(xiě),后面至少跟一個(gè)參數(shù);
2. Dockerfile關(guān)鍵字
2.1 FROM 關(guān)鍵字
指定基礎(chǔ)鏡像, 就是新鏡像是基于哪個(gè)鏡像構(gòu)建的。
比如建房子,可以在一塊空地開(kāi)始,也可以在別人打好的基石基礎(chǔ)上開(kāi)始, 甚至可以在別人弄好的毛坯房基礎(chǔ)上裝修即可。
如果要建房的話,可以FROM 空地,或者FROM 打好的基石,或者 FROM 毛坯房, 反正最后建好房就行;
這里需要注意的是,不管咋樣,空地是少不了的;構(gòu)建鏡像也一樣,最底層肯定有一個(gè)最基礎(chǔ)的鏡像。
建議使用官方的鏡像作為基礎(chǔ)鏡像,推薦使用Alpine這種類型,因?yàn)樗菄?yán)格控制的,而且體積很小。
用法如下:
- # FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
- ARG CODE_VERSION=latest # 定義變量
- FROM base:${CODE_VERSION} # 指定基礎(chǔ)鏡像
2.2 MAINTAINER/LABEL 關(guān)鍵字
MAINTAINER 指定維護(hù)者的相關(guān)信息,也就是構(gòu)建的鏡像是由誰(shuí)構(gòu)建的,他的郵箱是什么;
LABLE 就是用于給鏡像打標(biāo)簽,以鍵值對(duì)的方式進(jìn)行指定,相對(duì)MAINTAINER 來(lái)說(shuō)比較靈活,可以使用LABLE替代MAINTAINER。
用法如下:
- # LABEL <key>=<value> <key>=<value> <key>=<value> ...
- LABEL com.example.version="0.0.1-beta"
- LABEL vendor1="ACME Incorporated"
2.3 RUN 關(guān)鍵字
構(gòu)建過(guò)程中需要運(yùn)行的命令, 比如在構(gòu)建過(guò)程中需要執(zhí)行一條命令下載對(duì)應(yīng)的包,這里就需要用到RUN關(guān)鍵字;
用法如下:
- # 兩種命令方式都可以
- # RUN <command>
- # RUN ["executable", "param1", "param2"]
- # 執(zhí)行命令,Linux支持的相關(guān)命令
- RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
- RUN ["/bin/bash", "-c", "echo hello"]
2.4 WORKDIR 關(guān)鍵字
根據(jù)鏡像啟動(dòng)容器時(shí),通常需要進(jìn)入到容器內(nèi)部;則可以通過(guò)WORKDIR指定進(jìn)入容器時(shí)的目錄;
用法如下:
- WORKDIR /path # 指定路徑
2.5 ENV 關(guān)鍵字
可以在構(gòu)建過(guò)程中設(shè)置環(huán)境變量;就好比平時(shí)我們安裝完程序,需要配置環(huán)境變量,方便訪問(wèn);ENV關(guān)鍵字就是根據(jù)需求可以設(shè)置對(duì)應(yīng)的環(huán)境變量;
用法如下:
- # ENV <key>=<value> ...
- # 指定環(huán)境變量
- ENV PATH=/usr/local/postgres-$PG_MAJOR/bin:$PATH
2.6 ADD 關(guān)鍵字
將宿主機(jī)的資源拷貝進(jìn)鏡像中,會(huì)自動(dòng)解壓縮,而且還能從遠(yuǎn)程宿主機(jī)中讀取資源并拷貝到鏡像中;
用法如下:
- # 兩種命令方式都可以
- # ADD [--chown=<user>:<group>] <src>... <dest>
- # ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
- ADD https://example.com/big.tar.xz /usr/src/things/
2.7 COPY 關(guān)鍵字
將宿主機(jī)的資源拷貝到鏡像中,只支持讀取構(gòu)建所在宿主機(jī)的資源。相對(duì)于ADD關(guān)鍵字來(lái)說(shuō)更加透明,操作什么就是什么。
用法如下:
- # 拷貝資源到容器,兩種命令格式都行
- # COPY [--chown=<user>:<group>] <源地址>... <目標(biāo)地址>
- # COPY [--chown=<user>:<group>] ["<源地址>",... "<目標(biāo)地址>"]
- COPY requirements.txt /tmp/
2.8 VOLUME 關(guān)鍵字
掛載數(shù)據(jù)卷,之前在常用命令那說(shuō)到通過(guò)命令的方式進(jìn)行數(shù)據(jù)卷掛載,在Dockerfile中使用VOLUME指定掛載路徑即可,根據(jù)構(gòu)建出來(lái)的鏡像運(yùn)行容器時(shí),默認(rèn)就有構(gòu)建時(shí)掛載的信息。
用法如下:
- # 掛載數(shù)據(jù)卷
- VOLUME ["/data"]
- VOLUME /myvol
2.9 EXPOSE 關(guān)鍵字
指定運(yùn)行容器時(shí)對(duì)外暴露的端口;即根據(jù)鏡像啟動(dòng)容器時(shí),容器向外暴露端口。
用法如下:
- # EXPOSE <port> [<port>/<protocol>...]
- EXPOSE 80/tcp # 暴露端口
- EXPOSE 80/udp
2.10 CMD 關(guān)鍵字
指定啟動(dòng)容器時(shí)要執(zhí)行的命令,只有最后一個(gè)會(huì)生效;即根據(jù)鏡像啟動(dòng)容器時(shí),容器需要執(zhí)行啥命令。
用法如下:
- # 兩種格式都行
- # CMD ["param1","param2"]
- # CMD command param1 param2
- # 執(zhí)行命令統(tǒng)計(jì) 行數(shù)、字?jǐn)?shù)、字節(jié)數(shù)
- CMD echo "This is a test." | wc -
- # 執(zhí)行wc --help命令
- CMD ["/usr/bin/wc","--help"]
2.11 ENTRYPOINT 關(guān)鍵字
指定根據(jù)鏡像啟動(dòng)容器時(shí)要執(zhí)行的命令,可以追加命令;執(zhí)行時(shí)機(jī)同CMD。
用法如下:
- # ENTRYPOINT ["executable", "param1", "param2"]
- # ENTRYPOINT command param1 param2
- ENTRYPOINT ["top", "-b"]
2.12 ARG 關(guān)鍵字
通過(guò)ARG指令定義了一個(gè)變量;和寫(xiě)代碼時(shí)定義的變量一樣,根據(jù)需要,定義就行啦。
用法如下:
- # ARG <name>[=<default value>]
- ARG user1=someuser
- ARG buildno=1
2.13 ONBUILD 關(guān)鍵字
基于父鏡像構(gòu)建新的鏡像時(shí),父鏡像的OBUILD會(huì)被觸發(fā)。
3. 實(shí)戰(zhàn)演示
這里還是以.NetCore項(xiàng)目構(gòu)建鏡像為例,其他編程語(yǔ)言的項(xiàng)目同理;這次咱們一步一步的來(lái),搞清楚每個(gè)命令的使用。
以下關(guān)于項(xiàng)目創(chuàng)建和發(fā)布的具體細(xì)節(jié)在第一篇最后就分享了,小伙伴可以參考,這里主要演示Dockerfile關(guān)鍵字。
3.1 準(zhǔn)備項(xiàng)目和Dockerfile文件
新建一個(gè)項(xiàng)目,啥都不需要改,就用默認(rèn)的接口演示,如下:
Dockerfile內(nèi)容如下:
- # 指定基礎(chǔ)鏡像,在此基礎(chǔ)上構(gòu)建自己的項(xiàng)目鏡像
- FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
- # 指定自己的工作目錄,進(jìn)入容器時(shí)目錄
- WORKDIR /myApp
- # 將構(gòu)建上下文目錄下的文件拷貝到容器的當(dāng)前工作目錄中,即/myApp
- COPY . .
- # 容器向外暴露端口,項(xiàng)目以什么端口啟動(dòng)就暴露對(duì)應(yīng)的端口
- EXPOSE 80
- # 執(zhí)行命令,這里默認(rèn)是以80端口啟動(dòng)的
- #就類似于在Linux系統(tǒng)的項(xiàng)目目錄下執(zhí)行 dotnet DockerfileDemo.dll 是一樣的
- ENTRYPOINT ["dotnet", "DockerfileDemo.dll"]
記得右鍵Dockerfile,選擇屬性,然后設(shè)置Dockerfile為始終復(fù)制,這樣后續(xù)更新變動(dòng),發(fā)布時(shí)就會(huì)自動(dòng)拷貝到對(duì)應(yīng)的發(fā)布目錄。
3.2 以文件的形式發(fā)布項(xiàng)目,并連同Dockerfile拷貝到安裝好Docker的機(jī)器上進(jìn)行構(gòu)建(這里還是用我的云服務(wù)器);
docker build -t myimage:v1.0 .解析:
- -t:指定鏡像的名字及標(biāo)簽,通常 name:tag 或者 name 格式,myimage就是鏡像名字,v1.0就是tag;
- -f :指定要使用的Dockerfile路徑,這里由于Dockerfile在當(dāng)前路徑,所以不用指定;
- 最后面的點(diǎn):官方稱為構(gòu)建上下文,點(diǎn)表示指定為當(dāng)前目錄。會(huì)把指定的這個(gè)目錄下的文件發(fā)送給docker daemon進(jìn)行構(gòu)建,所以千萬(wàn)不要指定/(斜杠代表根目錄,有很多文件的)。
- 其他選項(xiàng)參數(shù)小伙伴可以根據(jù)需要使用,以上是比較重要的。
3.3 根據(jù)構(gòu)建出來(lái)的鏡像啟動(dòng)容器,看Dockerfile中的命令效果;
啟動(dòng)容器如下:
ENTRYPOINT ["dotnet", "DockerfileDemo.dll"]這行代碼就等同于的項(xiàng)目目錄下直接執(zhí)行 dotnet DockerfileDemo.dll是一樣的,目的就是啟動(dòng)我們的項(xiàng)目。
通過(guò)docker logs可以查看容器內(nèi)部的日志,如下:
3.4 豐富化Dockefile文件內(nèi)容并查看構(gòu)建之后的細(xì)節(jié)
文件內(nèi)容如下:
- # 指定基礎(chǔ)鏡像,在此基礎(chǔ)上構(gòu)建自己的項(xiàng)目鏡像
- FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
- # 指定維護(hù)人
- MAINTAINER CodeZYQ<1137533407@qq.com>
- # 打標(biāo)簽
- LABEL createname="CodeZYQ"
- # 指定自己的工作目錄,進(jìn)入容器時(shí)目錄 app
- WORKDIR /myapp
- # 將構(gòu)建上下文目錄下的文件拷貝到容器中的工作目錄中
- COPY . .
- # 定義變量
- ARG myPort=8080
- # 使用環(huán)境變量方式改變啟動(dòng)端口,拼接用到了定義的變量
- ENV ASPNETCORE_URLS=http://+:$myPort
- # 通過(guò)RUN 執(zhí)行相關(guān)命令,根據(jù)需要執(zhí)行相關(guān)命令
- RUN mkdir testDir
- # 掛載數(shù)據(jù)卷,這里模擬掛載日志目錄
- VOLUME /Logs
- # 容器向外暴露端口,項(xiàng)目以什么端口啟動(dòng)就暴露對(duì)應(yīng)的端口
- EXPOSE $myPort
- # 執(zhí)行命令,這里默認(rèn)是以80端口啟動(dòng)的
- # 就類似于在Linux系統(tǒng)的項(xiàng)目目錄下執(zhí)行 dotnet DockerfileDemo.dll 是一樣的
- ENTRYPOINT ["dotnet", "DockerfileDemo.dll"]
執(zhí)行如下命令,構(gòu)建新的鏡像:
通過(guò)docker logs看看容器日志,如下:
看看數(shù)據(jù)卷掛載是否成功,進(jìn)入容器,看根目錄下就會(huì)多了Logs目錄,也可以通過(guò)docker inspect 容器 看容器詳細(xì)信息,如下:
標(biāo)簽也打成功了:
也可以通過(guò)docker inspect 鏡像查看鏡像內(nèi)部的詳細(xì)信息,執(zhí)行命令docker inspect newimage如下:
關(guān)于步驟和效果,在Dockerfile注釋和圖表中已經(jīng)詳細(xì)描述。
3.5 CMD和ENTRYPOINT的區(qū)別
兩個(gè)命令都是啟動(dòng)容器時(shí)指定執(zhí)行命令和對(duì)應(yīng)的參數(shù),但兩者稍有不同,如下:
- CMD:只能最后一個(gè)命令會(huì)生效,命令會(huì)被docker run之后的參數(shù)替換掉;
- ENTRYPOINT:可以追加命令,比如增加參數(shù);
上面構(gòu)建出來(lái)的newimage鏡像用到的是ENTRYPOINT,所以我們先來(lái)測(cè)試一下ENTRYPOINT,如下:
docker run啟動(dòng)容器時(shí)指定了參數(shù) --urls="http://+:9999",容器正常啟動(dòng),并且參數(shù)還能生效,等同于在當(dāng)前目錄直接執(zhí)行如下命令:
- dotnet DockerfileDemo.dll --urls="http://+:9999"
現(xiàn)在把ENTRYPOINT換成CMD試試,如下:
- # 在以上的Dockerfile中
- # 將ENTRYPOINT ["dotnet", "DockerfileDemo.dll"]換成CMD,如下:
- CMD ["dotnet", "DockerfileDemo.dll"]
然后重新構(gòu)建一個(gè)鏡像試試,測(cè)試如下:
如上圖,對(duì)于CMD而言,如果在運(yùn)行容器時(shí),后面指定參數(shù),這個(gè)參數(shù)就會(huì)把CMD命令替換掉,不能拼接,導(dǎo)致命令不對(duì),所以報(bào)錯(cuò);但這樣就可以執(zhí)行,如下:
如果在當(dāng)前構(gòu)建的上下文目錄中不想要一些文件參與構(gòu)建,可以通過(guò)在.dockerignore文件中進(jìn)行配置,這個(gè)和git中的.gitignore一個(gè)道理,編寫(xiě)也比較簡(jiǎn)單,這里就不演示了。
對(duì)了,.NetCore的鏡像列表可以參照這個(gè)地址:https://hub.docker.com/_/microsoft-dotnet-aspnet/,每個(gè)鏡像都有對(duì)應(yīng)的Dockerfile,感興趣的小伙伴可以點(diǎn)進(jìn)去看看,參考參考。
總結(jié)
關(guān)于Dockerfile的演示就先說(shuō)那么多,小伙伴們一定要舉一反三,上面演示只是一個(gè)小例子而已,在正式項(xiàng)目中可以根據(jù)需要,編輯出屬于符合需求的Dockefile文件,最終構(gòu)建出方便、好用的鏡像,這樣開(kāi)發(fā)和運(yùn)維就和諧了(嘿嘿嘿)。