一、概述
二、Dockerfile的使用
Dockerfile描述了组装对象的步骤,其中每条指令都是单独运行的。除了FROM指令,其他每条命令都会在上一条指令所生成镜像的基础上执行,执行完后会生成一个新的镜像层,新的镜像层覆盖在原来的镜像之上从而形成了新的镜像。Dockerfile所生成的最终镜像就是在基础镜像上面叠加一层层的镜像层组建的。
三、Dockerfile指令
Dockerfile的基本格式如下:
在Dockerfile中,指令(INSTRUCTION)不区分大小写,但是为了与参数区分,推荐大写。Docker会顺序执行Dockerfile中的指令,第一条指令必须是FROM指令,它用于指定构建镜像的基础镜像。在Dockerfile中以#开头的行是注释,而在其他位置出现的#会被当成参数。
Dockerfile中的指令有FROM、MAINTAINER、RUN、CMD、EXPOSE、ENV、ADD、COPY、ENTRYPOING、VOLUME、USER、WORKDIR、ONBUILD,错误的指令会被忽略。下面将详细讲解一些重要的Docker指令。
1、FROM
格式: FROM
FROM指令的功能是为后面的指令提供基础镜像,因此Dockerfile必须以FROM指令作为第一条非注释指令。从公共镜像库中拉取镜像很容易,基础镜像可以选择任何有效的镜像。在一个Dockerfile中FROM指令可以出现多次,这样会构建多个镜像。tag的默认值是latest,如果参数image或者tag指定的镜像不存在,则返回错误。
2、ENV
格式: ENV
ENV指令可以为镜像创建出来的容器声明环境变量。并且在Dockerfile中,ENV指令声明的环境变量会被后面的特定指令(即ENV、ADD、COPY、WORKDIR、EXPOSE、VOLUME、USER)解释使用。
其他指令使用环境变量时,使用格式为$variable_name或者${variable_name}。如果在变量面前添加斜杠可以转义。如$foo或者${foo}将会被转换为$foo和${foo},而不是环境变量所保存的值。另外,ONBUILD指令不支持环境替换。
3、COPY
格式: COPY
COPY指令复制所指向的文件或目录,将它添加到新镜像中,复制的文件或目录在镜像中的路径是
4、ADD
格式:ADD
ADD与COPY指令在功能上很相似,都支持复制本地文件到镜像的功能,但ADD指令还支持其他功能。
ADD 和 COPY指令虽然功能相似,但一般推荐使用COPY,因为COPY只支持本地文件,相比ADD而言,它更加透明。
5、EXPOSE
格式: EXPOSE
EXPOSE指令通知Docker该容器在运行时侦听指定的 络端口。可以指定端口是侦听TCP还是UDP,如果未指定协议,则默认值为TCP。这个指令仅仅是声明容器打算使用什么端口而已,并不会自动在宿主机进行端口映射,可以在运行的时候通过docker -p指定。
6、USER
格式: USER
USER指令设置了user name和user group(可选)。在它之后的RUN,CMD以及ENTRYPOINT指令都会以设置的user来执行。
7、WORKDIR
格式: WORKDIR /path/to/workdir
WORKDIR指令设置工作目录,它之后的RUN、CMD、ENTRYPOINT、COPY以及ADD指令都会在这个工作目录下运行。如果这个工作目录不存在,则会自动创建一个。WORKDIR指令可在Dockerfile中多次使用。如果提供了相对路径,则它将相对于上一个WORKDIR指令的路径。例如
输出结果是 /a/b/c
8、RUN
格式1: RUN
格式2: RUN [“executable”, “param1”, “param2”] (exec格式,推荐使用)
RUN指令会在前一条命令创建出的镜像的基础上创建一个容器,并在容器中运行命令,在命令结束运行后提交容器为新镜像,新镜像被Dockerfile中的下一条指令使用。
RUN指令的两种格式表示命令在容器中的两种运行方式。当使用shell格式时,命令通过/bin/sh -c运行。当使用exec格式时,命令是直接运行的,容器不调用shell程序,即容器中没有shell程序。exec格式中的参数会被当成JSON数组被Docker解析,故必须使用双引 而不能使用单引 。因为exec格式不会在shell中执行,所以环境变量的参数不会被替换。
比如执行RUN [“echo”, “$HOME”]指令时,$HOME不会做变量替换。如果希望运行shell程序,指令可以写成 RUN [“/bin/bash”, “-c”, “echo”, “$HOME”]。
9、CMD
CMD指令有3种格式。
格式1:CMD
格式2:CMD [“executable”, “param1”, “param2”] (exec格式,推荐使用)
格式3:CMD [“param1”, “param2”] (为ENTRYPOINT指令提供参数)
CMD指令提供容器运行时的默认值,这些默认值可以是一条指令,也可以是一些参数。一个Dockerfile中可以有多条CMD指令,但只有最后一条CMD指令有效。 CMD [“param1”, “param2”]格式是在CMD指令和ENTRYPOINT指令配合时使用的,CMD指令中的参数会添加到ENTRYPOING指令中.使用shell和exec格式时,命令在容器中的运行方式与RUN指令相同。
不同之处在于,RUN指令在构建镜像时执行命令,并生成新的镜像;CMD指令在构建镜像时并不执行任何命令,而是在容器启动时默认将CMD指令作为第一条执行的命令。如果用户在命令行界面运行docker run命令时指定了命令参数,则会覆盖CMD指令中的命令。
10、ENTRYPOINT
ENTRYPOINT指令有两种格式。
格式1:ENTRYPOINT
格式2:ENTRYPOINT [“executable”, “param1”, “param2”] (exec格式,推荐格式)
ENTRYPOINT指令和CMD指令类似,都可以让容器在每次启动时执行相同的命令,但它们之间又有不同。一个Dockerfile中可以有多条ENTRYPOINT指令,但只有最后一条ENTRYPOINT指令有效。
当使用Shell格式时,ENTRYPOINT指令会忽略任何CMD指令和docker run命令的参数,并且会运行在bin/sh -c中。这意味着ENTRYPOINT指令进程为bin/sh -c的子进程,进程在容器中的PID将不是1,且不能接受Unix信 。即当使用docker stop
推荐使用exec格式,使用此格式时,docker run传入的命令参数会覆盖CMD指令的内容并且附加到ENTRYPOINT指令的参数中。从ENTRYPOINT的使用中可以看出,CMD可以是参数,也可以是指令,而ENTRYPOINT只能是命令;另外,docker run命令提供的运行命令参数可以覆盖CMD,但不能覆盖ENTRYPOINT。
四、Dockerfile实践心得
1、为镜像使用标签
给镜像打上标签,有利于帮助了解进镜像功能
2、谨慎选择基础镜像
选择基础镜像时,尽量选择当前官方镜像库的肩宽,不同镜像的大小不同,目前Linux镜像大小有如下关系:
busybox
同时在构建自己的Docker镜像时,只安装和更新必须使用的包。此外相比Ubuntu镜像,更推荐使用Debian镜像,因为它非常轻量级(目前其大小是在100MB以下),并且仍然是一个完整的发布版本。
3、充分利用缓存
Docker daemon会顺序执行Dockerfile中的指令,而且一旦缓存失效,后续命令将不能使用缓存。为了有效地利用缓存,需要保证指令的连续性,尽量将所有Dockerfile文件相同的部分都放在前面,而将不同的部分放到后面。
4、正确使用ADD与COPY命令
当在Dockerfile中的不同部分需要用到不同的文件时,不要一次性地将这些文件都添加到镜像中去,而是在需要时添加,这样也有利于重复利用docker缓存。另外考虑到镜像大小问题,使用ADD指令去获取远程URL中的压缩包不是推荐的做法。应该使用RUN wget或RUN curl代替。这样可以删除解压后不在需要的文件,并且不需要在镜像中在添加一层。
错误做法:
正确的做法:
5、RUN指令
在使用较长的RUN指令时可以使用反斜杠分隔多行。大部分使用RUN指令的常见是运行yum命令,在该场景下请注意以下几点。
- 不要在一行中单独使用指令RUN yum update。当软件源更新后,这样做会引起缓存问题,导致RUN yum install指令运行失败。所以,RUN yum update和RUN yum install应该写在同一行。比如 RUN yum update && yum install -y package-1 package-2 package-3
- 避免使用指令RUN yum upgrade 和 RUN yum dist-upgrade。因为在一个无特权的容器中,一些必要的包会更新失败。如果需要更新一个包(如package-1),直接使用命令RUN yum install -y package-1。
6、CMD和ENTRYPOINT命令
CMD和ENTRYPOINT命令指定是了容器运行的默认命令,推荐二者结合使用。使用exec格式的ENTRYPOINT指令设置固定的默认命令和参数,然后使用CMD指令设置可变的参数。
比如下面这个例子:
run.sh内容如下:
运行后输出结果为param1, Dockerfile中CMD和ENTRYPOINT的顺序不重要(CMD写在ENTRYPOINT前后都可以)。
当在windows系统下build dockerfile你可能会遇到这个问题
7、不要再Dockerfile中做端口映射
使用Dockerfile的EXPOSE指令,虽然可以将容器端口映射在主机端口上,但会破坏Docker的可移植性,且这样的镜像在一台主机上只能启动一个容器。所以端口映射应在docker run命令中用-p 参数指定。
五、实践Dockerfile的写法
1、Java 服务的DockerFile
可以看到基础镜像是openjdk,然后设置了两个环境变量,服务访问端口是9090(意味着springboot应用中指定了server.port=8080),设置了工作目录是/app。通过ENTRYPOINT设定了启动镜像时要启动的命令(./run.sh)。这个脚本中的内容如下:
如果我们要指定jvm的一些参数,可以通过在环境变量中设置env_jvm_flags来指定。
2、Maven Dockerfile
maven的Dockerfile也写的很好,这里我发上来也给大家参考下
可以看到它是基于openjdk这个基础镜像来创建的,先去下载maven的包,然后进行了安装。 然后又设置了MAVEN_HOME和MAVEN_CONFIG这两个环境变量,最后通过mvn-entrypoing.sh来进行了启动。
3、前端服务的两阶段构建
我有一个前端服务,目录结构如下:
myaccount目录下是放置的js,vue等,resources放置的是css,images等。third_party放的是第三方应用。
这里采用了两阶段构建,即采用上一阶段的构建结果作为下一阶段的构建数据
需要注意结尾的 –from=builder这里和开头是遥相呼应的。
六、总结
我相信看完dockerfile指令,你看任何一个dockerfile应该都没有太大问题,不记得的命令回来翻一下就行了。如果你觉得还可以,关注下哟。
文章知识点与官方知识档案匹配,可进一步学习相关知识云原生入门技能树首页概览8829 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!