文章目录
- 一.剖析dockerfile
- 1、dockerfile的定义
- 2、dockerfile的常用命令
- (1)FROM
- (2)MAINTAIN
- (3)COPY
- (4)ADD
- (5)ENV
- (6)EXPOSE
- (7)VOLUME
- (8)WORKDIR
- (9)RUN
- (10)CMD和ENTRYPOINT
一.剖析dockerfile
1、dockerfile的定义
dockerfile是用来构建docker镜像的构建文件,是一个由一系列命令和参数构成的脚本。
2、dockerfile的常用命令
知道了dockerfile的定义,那么就需要学习一下dockerfile中的一系列命令和参数该怎么写,下面解释一些dockerfile中的常用命令。
(1)FROM
指定base镜像(基础镜像),如果本地不存在该镜像,则会自动从远程仓库拉取下载。这里的基础镜像就是说要在这个镜像的基础上来做操作、改动。
- 比如写一个这样的dockerfile文件,FROM指定base镜像为ubuntu镜像,然后在该镜像基础上做了两层自定义镜像层的改动。
建立好新的镜像,看以下二者的构建历史就可以发现,新镜像比ubuntu镜像多了两层,通过镜像层的ID号可以发现,ubuntu镜像层的ID与新镜像的旧镜像层ID相同,也就是说新镜像是在ubuntu镜像的基础上进行了新的改动。
(2)MAINTAIN
设置镜像的作者信息,比如用户、邮箱等等,如果镜像有什么问题或者其他什么,可以通过这个联系作者来进行交流。(这个并不是必须要设置的,但是还是建议设置一下比较好)
(3)COPY
把文件从build context(镜像构建目录)复制到镜像。
支持两种写法:COPY src dest 和 COPY [“src”,”dest”](src在这里就是指你要指定的build context中的文件或目录,dest是你想要将指定的文件或目录复制到的目的地,可以在写目的地时指定名称来起到更名的效果,若是不指定则会默认使用文件或目录的原名称)。
注意:指定复制的文件或目录一定要在镜像构建目录中存在哦。
- 提供一个实际操作的例子,方便对COPY的理解。
我想演示两种写法更名和不更名时的效果,所以我这里建立了四个test文件,文件内容分别对应文件名以方便区分。(注意文件一定要建立在镜像构建目录中,放在其他地方就无法获取到了)
接下来修改dockerfile文件的内容,test1文件与test3文件用方式一来写,test2与test4文件用方式二来写,test3和test4更名为rename3和rename4
下面用该dockerfile文件构建新镜像test:v1
用新镜像启动一个名为vm1的容器,在容器的tmp目录中可以看到四个test文件都被复制了进来,且更名成功,内容也和设定的相符合。
(4)ADD
ADD的用法与COPY类似,但不同的是:
<1>使用ADD时,src可以是一个归档压缩文件,且这个归档压缩文件会被自动解压到设置的dest下。
<2>使用ADD时还可以写一个URL,该命令会将URL中的内容自动下载,并拷贝到镜像中。
注:ADD的解压是在拷贝到镜像中之前就解压好的,再拷贝到镜像中。
- 这里我找了个压缩包来测试,图中可以看到nginx的压缩包在docker构建目录中了,dockerfile文件我已经改好了,格式和COPY的格式一致即可,用改好的dockerfile文件构建了新的镜像。
使用新镜像开启一个容器,这里我改了命令,使用了–rm,意思是当容器运行完毕后自动删除,这样测试用的容器不会堆积很多,比较方便,由于用完就自动删除了,所以也不需要再专门给容器起个名字了。
进入到tmp目录中,可以发现nginx安装包已经解开了。
(5)ENV
ENV可以定义环境变量,定义的变量在后续的命令中可以被使用。
(6)EXPOSE
EXPOSE可以暴露容器中运行的服务的端口,主要是方便宿主机做端口映射。
(7)VOLUME
用法:VOLUME [“dest”]
声明数据卷,数据卷一般是用来指定应用的数据挂载点的,如果在封装镜像时声明了数据卷,则容器在启动时,docker引擎会自动在宿主机上生成一个数据目录,并且将该目录挂载到容器内的数据挂载点上,也就是说在容器的数据挂载点中操作,也就是在宿主机上的数据目录上操作。
这样的好处是在容器内操作时,往容器内的数据挂载点中写数据时,这些数据能够持久的直接保存到宿主机上。
注意:声明数据卷时,若是dest不存在,会在容器中自动建立这个dest。
如果不想用docker引擎自动在宿主机上生成的数据目录,可以手动指定一个数据目录挂载到容器内的数据挂载点上。
- 下面先演示一下自动生成的数据卷的特性。这里我更改了dockerfile的内容,新建了新的镜像
用新镜像打开新容器vm1,此时不能用–rm,因为这个容器需要保持存在,才能看出效果。
可以看到,容器中自动生成了目录datapoint,与我指定的相符,进入到该目录中,建立一个内容为test1,名称为test1的测试文件,然后Ctrl+p+q退出,让容器在后台运行。
此时用这个命令,可以查看vm1容器的信息。
可以看到”Source”这一项中,写出了数据卷挂载点,所有容器的数据卷都会默认挂载到/var/lib/docker/volume目录下,后续的一长串是自动生成的地址。
进到该目录中,在该目录中可以看到test1,内容也可以看到,也就是说这个目录和数据卷是同一个目录。
在目录中进行修改,删除test1并且建立test2,再去容器中测试一下看看。
打开容器,会发现确实和预想的一样。 - 不想使用自动建立的数据挂载点,可以手动指定一个数据目录挂载到容器内的数据挂载点上,当指定的这个目录不存在也没关系,会自动建立出来。使用-v即可。
此处我就做了个实例,我启动一个新容器vm2,并且指定数据目录为宿主机的/opt/datapoint,数据挂载点是容器内的/datapoint,在容器内的数据挂载点中建立test1文件测试,再Ctrl+p+q将容器打入后台运行,去数据目录查看能否看到test1的信息,信息一致,也就说明测试成功。
(8)WORKDIR
为RUN、CMD、等命令设置镜像中的当前工作目录,其实这个命令就像是cd命令一样,切到一个目录中进行操作。
注意:与cd命令不同的是,当切换的目录不存在时,会自动新建目录。
(9)RUN
在容器中运行命令,并创建新的一层自定义镜像层,每个RUN都会建立一层新的自定义镜像层。
RUN这个命令在上期的内容中已经做出了详细介绍,此时就不再赘述。
注意:RUN是在容器启动当中运行的命令,与CMD和ENTRYPOINT命令要做区分。
(10)CMD和ENTRYPOINT
这两个指令是用于设置容器启动后运行的命令。
区别:CMD命令会被docker run后面的命令行覆盖,而ENTRYPOINT命令不会被覆盖,一定会被执行。
docker run后面的参数可以传递给ENTRYPOINT命令当作参数使用,但CMD命令不可以使用。
特性:dockerfile中只能指定一个ENTRYPOINT命令,若指定了多个该命令,则只有最后一个会生效。
<shell格式和exec格式书写的区别>
shell格式书写时,底层会自动调用/bin/sh -c来执行命令,调用这个执行命令的效果就是可以自动解析变量。shell格式时,ENTRYPOINT会忽略任何CMD或docker run提供的参数。
exec格式书写时,没有自动调用,所以不会自动解析变量。不过可以通过书写来调用/bin/sh -c来达到同样的效果。exec格式时,ENTRYPOINT不会忽略CMD或docker run提供的参数,也就是说,可以动态替换CMD的额外参数,起到一个传参的效果。
- 那么通过实际的例子来更清晰的看一下便于理解吧
首先看一下shell格式书写时,容器运行完成后的反馈。
这里先修改了dockerfile文件,ENV就是前面讲到的设置变量的命令,格式如图所示,为了方便理解,key值直接用key做了名字,value值也直接用value做了名字,输出时调用了变量key。 - 用该dockerfile建立好了新的镜像,并且启动了一个新容器,方便测试就用了–rm,容器运行完后自动调用了变量,输出了变量的value值。
- 再看一下exec格式书写时,容器运行完成后的反馈。
先将dockerfile文件改成了exec格式,注意exec格式书写时,是不会自动调用/bin/sh -c来执行命令的,用新的dockerfile文件建立了新的镜像开启了新容器之后,可以看到反馈时并没有调用变量key的值,因为默认不会自动调用/bin/sh -c,所以无法识别变量的值。 - 那么可以这样修改dockerfile文件,来手动调用/bin/sh -c,达到和shell格式一样的效果。
图中可以看到修改好的dockerfile文件,使用它建立新镜像启动新容器做了测试,测试结果证明它可以正确识别变量。 - exec格式时,ENTRYPOINT不会忽略CMD或docker run提供的参数,也就是说,可以动态替换CMD的额外参数,现在来试试会有什么样的效果。
ENTRYPOINT的特性是一定会被执行的,而CMD命令则是,会被docker run后面的命令所覆盖。 - 用新dockerfile建立好新的镜像
- 用新镜像运行容器,会发现,当docker run后不接新的参数时,会输出CMD的参数,当接了新的参数时,CMD的参数会被覆盖,输出新参数,这就起到了动态替换的效果。