構(gòu)建鏡像的核心文件 Dockerfile
點擊關(guān)注公眾號,Java干貨及時送達(dá)
Java技術(shù)迷 | 出品
Dockerfile 是一個用來構(gòu)建鏡像的文本文件,文本內(nèi)容包含了一條條構(gòu)建鏡像所需的指令和說明。
通過Dockerfile我們能夠?qū)㈨椖繕?gòu)建成一個鏡像,這樣的好處是可以將一個復(fù)雜的項目直接打包成一個鏡像存儲和運行。
Dockerfile提供了非常多的指令供我們操作,下面例舉一些常用的命令:
| 指令 | 作用 |
| FROM | 指定當(dāng)前鏡像是基于哪個鏡像的 |
| RUN | 構(gòu)建鏡像時需要運行的指令 |
| EXPOSE | 當(dāng)前容器對外暴露的端口號 |
| WORKDIR | 指定在創(chuàng)建容器后終端默認(rèn)登錄進(jìn)來的工作目錄 |
| ENV | 用于在構(gòu)建鏡像過程中設(shè)置環(huán)境變量 |
| ADD | 將宿主機(jī)目錄下的文件拷貝到鏡像 |
| COPY | 拷貝文件和目錄到鏡像 |
| VOLUME | 容器數(shù)據(jù)卷,用于數(shù)據(jù)持久化 |
| CMD | 指定一個容器啟動時要運行的命令 |
| ENTRYPOINT | 指定一個容器啟動時要運行的命令 |
FROM
該指令用于指定當(dāng)前鏡像是基于哪個鏡像進(jìn)行構(gòu)建的,因為鏡像的構(gòu)建是一層一層進(jìn)行的,Docker Server會先基于某個基礎(chǔ)鏡像將項目打包成一個新鏡像,再基于這個新鏡像繼續(xù)打包,以此類推,不斷打包構(gòu)建,最終生成一個處理完成的鏡像。
下面就來簡單地使用一下FROM指令,在/opt目錄下新建一個test目錄,并創(chuàng)建Dockerfile文件,編寫如下內(nèi)容:
FROM centos文件內(nèi)容非常簡單,此時我們就可以根據(jù)Dockerfile進(jìn)行鏡像構(gòu)建了,執(zhí)行指令:
docker build -t mycentos_test01 .這里我們指定FROM為centos,Docker Server將基于centos鏡像進(jìn)行構(gòu)建,當(dāng)Docker中沒有該鏡像時,還會先將centos鏡像拉取下來再進(jìn)行構(gòu)建, . 表示Dockerfile文件所在的位置為當(dāng)前目錄:
[root@localhost test]# docker build -t mycentos_test01 .
Sending build context to Docker daemon 2.048kB
Step 1/1 : FROM centos
latest: Pulling from library/centos
7a0437f04f83: Pull complete
Digest: sha256:5528e8b1b1719d34604c87e11dcd1c0a20bedf46e83b5632cdeac91b8c04efc1
Status: Downloaded newer image for centos:latest
---> 300e315adb2f
Successfully built 300e315adb2f
Successfully tagged mycentos_test01:latest當(dāng)指令執(zhí)行成功后,就會生成一個新的鏡像,可以使用 docker images 指令進(jìn)行查看:
[root@localhost test]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 300e315adb2f 7 months ago 209MB
mycentos_test01 latest 300e315adb2f 7 months ago 209MB此時我們就可以啟動這個鏡像,同時進(jìn)入交互終端:
docker run -it mycentos_test01
[root@31c51ab4d120 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var可以看到這就是一個新的centos,這是因為我們只使用了FROM指令基于centos鏡像構(gòu)建,所以得到的鏡像仍然還是一個基礎(chǔ)的centos鏡像。
需要注意的是Dockerfile文件中的第一條指令必須是 FROM 。
RUN
該指令會將在當(dāng)前鏡像之上的新層中執(zhí)行任何命令并提交結(jié)果,生成的鏡像將用于下一步。
修改Dockerfile文件,內(nèi)容如下:
FROM centos
RUN yum install -y vim我們知道在沒加RUN命令之前構(gòu)建出來的鏡像就是一個centos,但現(xiàn)在,構(gòu)建出來的鏡像就是一個包含vim編輯器的centos系統(tǒng),馬上來構(gòu)建它:
docker build -t mycentos_test02 .構(gòu)建成功后就可以進(jìn)行啟動:
docker run -it mycentos_test02可以檢查一下鏡像中是否真的已經(jīng)包含了vim編輯器:
[root@46992a057ea4 /]# rpm -qa|grep vim
vim-minimal-8.0.1763-15.el8.x86_64
vim-common-8.0.1763-15.el8.x86_64
vim-enhanced-8.0.1763-15.el8.x86_64
vim-filesystem-8.0.1763-15.el8.noarchRUN指令還支持以數(shù)組的方式傳遞需要執(zhí)行的命令,比如:
RUN ["yum","install","-y","vim"]EXPOSE
該指令用于指定構(gòu)建的鏡像在運行時為對外暴露的端口,只有向外暴露了端口,外部才能夠訪問到這個進(jìn)鏡像提供的服務(wù)。
修改Dockerfile文件:
FROM centos
RUN ["yum","install","-y","vim"]
EXPOSE 80但事實上centos鏡像添加端口并沒有作用,因為它不像tomcat、mysql、redis那樣會向外部主機(jī)提供某項服務(wù),所以可以將基礎(chǔ)鏡像指定為tomcat:
FROM tomcat:8.0此時構(gòu)建鏡像然后啟動:
docker run -p 80:80 mytomcat_test01CMD
該指令用于為啟動的容器指定需要執(zhí)行的命令,修改Dockerfile:
FROM centos
RUN ["yum","install","-y","vim"]
EXPOSE 80
CMD ["echo","hello"]構(gòu)建鏡像:
docker build -t mycentos_test03此時啟動鏡像:
docker run -it mycentos_test03因為CMD指令的作用,所以在啟動這個鏡像的時候就會立馬執(zhí)行CMD中的命令,從而輸出 hello 字符串:
[root@localhost test]# docker run -it mycentos_test03
hello需要注意的是Dockerfile中只能有一條CMD命令,如果寫出了多條CMD,則以最后一條的內(nèi)容為準(zhǔn)。
ENTRYPOINT
該指令與CMD類似,也是用于指定容器啟動時需要執(zhí)行的命令,修改Dockerfile文件:
FROM centos
RUN ["yum","install","-y","vim"]
EXPOSE 80
ENTRYPOINT ["echo","hello"]構(gòu)建鏡像:
docker build -t mycentos_test04 .啟動鏡像:
[root@localhost test]# docker run -it mycentos_test04
hello可以看到ENTRYPOINT和CMD兩個指令的效果是一樣的,那么它倆有什么區(qū)別呢?其區(qū)別在于命令的覆蓋方式,對于CMD指令,我們可以在啟動鏡像的時候直接拼寫命令來覆蓋它,比如:
[root@localhost test]# docker run -it mycentos_test03 echo hello centos
hello centos此時在Dockerfile中配置的CMD命令將會被這里的 echo hello centos 覆蓋掉,最終輸出 hello cnetos 。
然而對于ENTRYPOINT,它是無法通過直接追加命令來覆蓋的,而是需要用到一個參數(shù):
[root@localhost test]# docker run --entrypoint="echo hello centos" mycentos_test04
hello centos由此可見,ENTRYPOINT指令對于命令的覆蓋是比CMD指令更加復(fù)雜的,為此,可以將那些基本固定不變的命令配置到ENTRYPOINT中,而將需要修改的命令配置到CMD中,比如:
FROM centos
RUN ["yum","install","-y","vim"]
EXPOSE 80
ENTRYPOINT ["echo"]
CMD ["hello centos"]WORKDIR
該指令用于指定鏡像的落腳點,即:啟動鏡像后初始進(jìn)入的目錄,若是沒有配置,則默認(rèn)是 / ,比如:
[root@localhost test]# docker run -it mycentos_test02
[root@9c958bd3830f /]#啟動鏡像后首先進(jìn)入的便是 / 目錄,但如果配置了WORKDIR:
FROM centos
WORKDIR /opt/work構(gòu)建鏡像:
docker build -t mycentos_test05 .啟動鏡像:
[root@localhost test]# docker run -it mycentos_test05
[root@37f177c1e546 work]# pwd
/opt/work需要注意,只要配置了WORKDIR,無論你有沒有在后續(xù)的指令中使用到它,WORKDIR配置的目錄是一定會在鏡像中被創(chuàng)建的。
ENV
該指令用于為鏡像設(shè)置環(huán)境變量,比如:
FROM centos
ENV name centos
WORKDIR /$name在這里配置了一個環(huán)境變量name,值為centos,并在WORKDIR指令中引用了該變量,所以通過該文件構(gòu)建出來的鏡像在啟動時就會直接進(jìn)入/centos目錄,驗證一下:
[root@localhost test]# docker build -t mycentos_test06 .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM centos
---> 300e315adb2f
Step 2/3 : ENV name centos
---> Running in 4c272c71fc62
Removing intermediate container 4c272c71fc62
---> 0c00112be488
Step 3/3 : WORKDIR /$name
---> Running in 19bb2ad9d3cb
Removing intermediate container 19bb2ad9d3cb
---> d7405fa45cb0
Successfully built d7405fa45cb0
Successfully tagged mycentos_test06:latest
[root@localhost test]# docker run -it mycentos_test06
[root@0a505138f024 centos]# pwd
/centosVOLUME
該指令用于定義容器運行時可以掛載到宿主機(jī)的目錄,修改一下Dockerfile:
FROM centos
ENV name centos
WORKDIR /$name
VOLUME /$name此時配置了VOLUME,值為 /$name ,則容器中的/centos目錄就允許被外部掛載。
構(gòu)建鏡像:
docker build -t mycentos_test07 .然后在啟動鏡像的同時掛載一下數(shù)據(jù)卷:
docker run -it -v /opt/centos:/centos mycentos_test07這樣兩個目錄就實現(xiàn)了共享,此時在/opt/centos目錄下的操作都會被同步到容器中的/centos目錄:
[root@localhost test]# cd /opt/centos/
[root@localhost centos]# touch test
[root@localhost centos]# docker run -it -v /opt/centos:/centos mycentos_test07
[root@37450c713b08 centos]# ls
testADD
該指令用于將context目錄中指定的文件復(fù)制到鏡像的指令目錄中去,那么首先我們就要知道何為context目錄。
context目錄意為上下文目錄,指的是Dockerfile文件所在的目錄,比如:
[root@localhost opt]# ll
total 262144
drwx--x--x. 4 root root 28 Jun 18 11:27 containerd
-rw-r--r--. 1 root root 268435456 Jun 23 15:46 disk.bin
-rw-r--r--. 1 root root 0 Jul 23 07:08 Dockerfile
drwxr-xr-x. 3 root root 25 Jul 22 12:45 javaproject
drwxr-xr-x. 8 10143 10143 273 Apr 7 19:26 jdk1.8.0_291
drwxr-xr-x. 2 root root 24 Jul 23 07:04 test
drwxr-xr-x. 8 root root 160 Jul 5 12:52 zookeeper-3.5.7若是此時Dockerfile文件處在/opt目錄下,則context目錄則為/opt目錄,因為DockerServer會將context目錄中的所有文件,包括子目錄及其子文件進(jìn)行打包構(gòu)建,所以這也是為什么我們一開始創(chuàng)建一個空的文件夾,并將Dockerfile放在其中,因為文件過多會導(dǎo)致構(gòu)建速度變慢。
修改Dockerfile:
FROM centos
ENV name centos
WORKDIR /$name
VOLUME /$name
ADD test.txt /在Dockerfile所在目錄下有一個test.txt,現(xiàn)在要將其復(fù)制到容器中的 / 目錄下,構(gòu)建鏡像:
docker build -t mycentos_test08 .啟動鏡像:
[root@localhost test]# docker run -it mycentos_test08
[root@184a8824b583 centos]# cd /
[root@184a8824b583 /]# ls -l | grep test
-rw-r--r--. 1 root root 0 Jul 23 07:25 test.txt若是想修改添加到容器后的文件名,則直接指定名字即可:
ADD test.txt /ntest.txtADD不僅能夠添加文件、目錄,還能夠添加一個url,比如:
ADD http://www.baidu.com /test.html則啟動構(gòu)建后的鏡像時,便會在 / 目錄下創(chuàng)建test.html文件,并將訪問http://www.baidu.com所得到的響應(yīng)結(jié)果寫入該文件。
構(gòu)建一個SpringBoot應(yīng)用的鏡像
大概了解Dockerfile中的一些指令之后,我們就可以通過它來構(gòu)建一個SpringBoot應(yīng)用的鏡像了,所以首先來編寫一個非常簡單的SpringBoot應(yīng)用:
@RestController
public class TestController {
@GetMapping("/testMethod")
public String testMethod(){
return "success";
}
}創(chuàng)建一個控制器用于測試即可,然后將應(yīng)用打成一個jar包,使用maven插件即可進(jìn)行打包,指令如下:
mvn clean package將得到的jar包上傳至服務(wù)器:
[root@localhost ~]# cd /opt/test/
[root@localhost test]# ls -l | grep .jar
-rw-r--r--. 1 root root 16568766 Jul 23 07:56 SpringBootDemo-0.0.1-SNAPSHOT.jar編寫Dockerfile:
FROM openjdk:8 # 指定基礎(chǔ)鏡像
WORKDIR /test # 指定工作目錄
ADD SpringBootDemo-0.0.1-SNAPSHOT.jar /test # 將jar包復(fù)制到容器中
EXPOSE 8080 # 向外暴露8080端口
ENTRYPOINT ["java","-jar"]
CMD ["SpringBootDemo-0.0.1-SNAPSHOT.jar"]因為運行jar包需要JDK環(huán)境,所以直接使用openjdk8作為基礎(chǔ)鏡像即可,然后指定工作目錄,接著將jar包復(fù)制到容器里的工作目錄中,并向外暴露8080端口,最后使用ENTRYPOINT和CMD指令聯(lián)合組成jar包的運行命令。
這樣就可以構(gòu)建鏡像了:
docker build -t springbootdemo .運行鏡像:
docker run -p 8080:8080 springbootdemo鏡像啟動后會自動運行jar包,此時外部主機(jī)就可以訪問到pringBoot應(yīng)用了:

上傳鏡像到服務(wù)器
這里以阿里云為例,我們將剛才構(gòu)建的鏡像上傳至阿里云的鏡像服務(wù)器中,首先登錄阿里云,搜索 容器鏡像服務(wù) 并開通:

開通后進(jìn)入管理控制臺,點擊左側(cè)的命名空間,創(chuàng)建一個命名空間:

然后點擊左側(cè)的鏡像倉庫,創(chuàng)建一個鏡像倉庫:

選擇剛剛創(chuàng)建的命名空間,并填寫倉庫名稱:

點擊下一步后選擇本地倉庫:

點擊創(chuàng)建鏡像倉庫,完成后可以看到阿里云提供的操作指南,我們照著操作指南進(jìn)行操作即可。
首先登錄到阿里云:
docker login --username=取個名字好困難a registry.cn-hangzhou.aliyuncs.com然后會要求輸入密碼,密碼在開通容器鏡像服務(wù)的時候就會讓你設(shè)置,如果沒有設(shè)置密碼,也可以在頁面右側(cè)的新手引導(dǎo)中點擊訪問憑證進(jìn)行設(shè)置:

然后為鏡像設(shè)置tag:
docker tag 8d4d7be1d926 registry.cn-hangzhou.aliyuncs.com/test_respository/springbootdemo:1.0.0這里有兩處地方需要設(shè)置(ImageId和版本號):
docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/test_respository/springbootdemo:[鏡像版本號]其中ImageId為鏡像的id,通過 docker images 即可查詢到,鏡像版本號可以隨意填寫。
最后將鏡像推送到服務(wù)器上:
docker push registry.cn-hangzhou.aliyuncs.com/test_respository/springbootdemo:1.0.0此時點擊鏡像版本查看一下鏡像是否成功上傳了:

接下來測試一下拉取功能,首先刪除掉之前構(gòu)建的鏡像:
docker rmi springbootdemo執(zhí)行指令進(jìn)行拉取:
docker push registry.cn-hangzhou.aliyuncs.com/test_respository/springbootdemo:1.0.0
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
registry.cn-hangzhou.aliyuncs.com/test_respository/springbootdemo 1.0.0 8d4d7be1d926 41 minutes ago 531MB
openjdk 8 f67a59e543c1 19 hours ago 514MB
centos latest 300e315adb2f 7 months ago 209MB
mycentos_test01 latest 300e315adb2f 7 months ago 209MB
tomcat 8.0 ef6a7c98d192 2 years ago 356MB拉取下來的鏡像就可以啟動了,只不過鏡像的名字變得比較長了:
docker run -p 8080:8080 registry.cn-hangzhou.aliyuncs.com/test_respository/springbootdemo:1.0.0本文作者:汪偉俊 為Java技術(shù)迷專欄作者 投稿,未經(jīng)允許請勿轉(zhuǎn)載!
往 期 推 薦
1、拖動文件就能觸發(fā)7-Zip安全漏洞,波及所有版本
3、一次 SQL 查詢優(yōu)化原理分析:900W+ 數(shù)據(jù),從 17s 到 300ms
點分享
點收藏
點點贊
點在看






