Dockerfile 的使用
Docker build命令和镜像构建过程
docker build
命令使用的时候其参数有3种类型来表示构建context的3种来源:PATH
, -
和 URL
。更多>>
这里的构建context是指传入docker build
命令的所有文件。
一般情况下,将本地主机的一个包含Dockerfile的目录中的所有内容作为context。context通过docker build
命令传入到Docker daemon后,便开始按照Dockerfile中的内容构建镜像。
除了FROM
指令,其它每一条指令都会在上一条指令所生成的镜像的基础上执行,执行完后会生成一个新的镜像层,新的镜像层覆盖在原来的镜像之上从而形成了新镜像。Dockerfile所生成的最终镜像就是在基础镜像上面叠加一层层的镜像层组建的。
为了提高镜像构建的速度,Docker daemon会缓存构建过程中的中间镜像。当从一个已经在缓存中的基础镜像开始构建新镜像时,会像Dockerfile中的下一条指令和基础镜像的所有子镜像做比较,如果有一个子镜像是由相同命令生成的,则命中缓存,直接使用该镜像,而不用再生成一个新的镜像。
但是COPY
和ADD
命令与其它命令稍有不同,其他指令只对比生成镜像的指令字符串是否相同;COPY
和ADD
还要对比容器中的文件内容和所添加的文件内容是否相同。此外,镜像构建过程中,一旦缓存失效,则后续的指令都将生成新的镜像,而不再使用缓存。
Dockerfile指令
- FROM
格式:
FROM <image>
FROM <image>:<tag>
FROM
指令为后面的指令提供基础镜像,因些一个有效的Dockerfile必须以FROM
作为第一条非注释指令。
- ENV
格式:
ENV <key> <value>
ENV <key>=<value>
ENV
用于声名明境变量。并且在Dockerfile中ENV指令声明环境可以被后面的特定指令(ENV
,ADD
,COPY
,WORKDIR
,EXPOSE
,VOLUME
,USER
)解释使用。
使用格式:
$variable_name
${variable_name}
- COPY
格式:
COPY <src> <dest>
<src>所指定的源可以有多个,但必须在context中,即必须是context根目录的相对路径。不能使用形如COPY ../something /something
这样的指令。此外,<src>可以使用通配符指向所有匹配通配符的文件或目录,如,COPY hom* /mydir/
。
<dest>可以是文件或目录,但必须是目标镜像中的绝对路径或者相对于WORKDIR
的相对路径。
- ADD
格式:
ADD <src> <dest>
ADD
和COPY
在功能上很相似,但ADD
还支持其他功能。<src>可以是一个指向一个网络文件的URL,此时若<dest>指向一个目录,则URL必须是完全路径,这样可以获得该网络文件名filename,该文件会被复制添加到<dest>/<filename>。例如,ADD http://example.com/foobar /
会创建文件/foobar。
一般推荐使用COPY
,因为COPY
只支持本地文件,相比ADD
而言,它更透明。
- RUN
格式:
RUN <command> // shell格式
RUN ["executable", "param1", "param2"] // exec格式,推荐格式
RUN
指令会在前一条命令创建出的镜像的基础上创建一个容器,并在容器中运行命令,在命令结束运行后提交容器为新镜像。
当使用shell格式时,命令通过/bin/sh -c
运行;
当使用exec格式时,命令是直接运行的,容器不调用shell程序,即容器中没有shell程序。exec格式中的参数会当成JSON数组被Docker解析,故必须使用双引号而不能使用单引号。因为exec格式不会在shell中执行,所以环境变量的参数不会被替换。
- CMD
格式:
CMD <command> // shell格式
CMD ["executable", "param1", "param2"] // exec格式,推荐格式
CMD ["param1", "param2"] // 为ENTRYPOINT指令提供参数
CMD
提供运行的默认值,默认值可以是一些参数,或指令。一个Dockerfile中只有最后一条CMD
指令有效。而且CMD
指令需要要ENTRYPOINT
指令配合使用。
与RUN
指令不周的是:
RUN
指令在构建镜像时执行命令,并生成的镜像;
CMD
指令在构建镜像时并不执行任何命令,而是在容器启动时默认将CMD指令作为第一条执行的命令。如果用户在命令行界面运行docker run
命令时指定了命令参数,则会覆盖CMD
指令中的命令。
- ENTRYPOINT
格式:
ENTRYPOINT <command> // shell格式
ENTRYPOINT ["executable", "param1", "param2"] // exec格式,推荐格式
ENTRYPOINT
指令和CMD
指令类似,都可以让容器在每次启动时执行相同的命令。但它们之间又有不同。一个Dockerfile中只有最后一条ENTRYPOINT
指令有效。
当使用sheel格式时,ENTRYPOINT
指令会忽略任何CMD
指令和docker run
命令的参数,并且运行在bin/sh -c
中。这意味着ENTRYPOINT
指令进程为bin/sh -c
的子进程,进程在容器中的PID将不是1,且不能接受Unix信号。即当使用docker stop <container>命令时,命令进程接受不到SIGTERM信号。
我们推荐使用exec格式,使用此格式时,docker run
传入的命令参数会覆盖CMD
指令的内容,并且添加到ENTRYPOINT
指令的参数中。
从ENTRYPOINT
的使用中可以看出,CMD
可以是参数,也可以是指令,而ENTRYPOINT
只能是命令;另外,docker run
提供的运行命令参数可以覆盖CMD
,但是不能覆盖ENTRYPOINT
。
Dockerfile 使用心得
- 使用标签
给镜像打标签,易读的镜像标签可以帮助了解镜像的功能,如:
docker build -t "mydockerimage:1.0" .
- 谨慎选择基础镜像
busybox < debian < centos < ubuntu
- 充分利用缓存
为了有效地利用缓存,需要保证指令的连续性,尽量将所有Dockerfile文件中相同的部分都放在前面,而将不同的部分放在后面。
- CMD和ENTRYPOINT
CMD
和ENTRYPOINT
指令指定了容器运行的命令,推荐二者结合使用。使用exec格式的ENTRYPOINT
指令设置固定的默认命令和参数,然后使用CMD
指令设置可变的参数。
- 不要在Dockerfile中做端口映射
Docker的两个核心概念是可重复性和可移植性,镜像应该可以在任何主机上运行多次。使用Dockerfile的EXPOSE
指令,虽然可以将容器端口映射到主机端口上,但会破坏Docker的可移植性,且这样的镜像在一台主机上只能启动一个容器。所以端口映射就在docker run
命令中用-p参数指定。
# 不要在Dockerfile中做如下映射
EXPOSE 80:8080
# 仅仅暴露80端口,需要另做映射
EXPOSE 80