之前已经了解到镜像的定制实际上就是定制每一层所添加的配置、文件。如果可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,这就是 Dockerfile。
格式描述#
Dockerfile 整体由两类语句组成
Comment注释信息Instruction arguments指令参数,一行一个指令
注意
Dockerfile文件名首字母必须大写Dockerfile指令不区分大小写,但为方便和参数做区分,通常指令使用大写字母Dockerfile指令按顺序从上至下依次执行Dockerfile第一个非注释行必须是FROM指令,用来指定制作当前镜像依据的是哪个基础镜像Dockerfile需要调用的文件必须跟Dockerfile文件在同一目录下,或者在其子目录下。父目录或者其它路径无效
FROM#
- 功能为指定基础镜像,并且必须是第一条指令
- 如果不以任何镜像为基础写法为:
FROM scratch,意味着接下来所写的指令将作为镜像的第一层开始
注意:如果没有指定仓库,
docker build会先从本机查找是否有此基础镜像,如果没有默认去Docker Hub Registry上拉取,再找不到就会报错
FROM <image>
FROM <image>:<tag>
FROM <image>:<digest>
# 三种写法,其中<tag>和<digest> 是可选项,如果没有选择,那么默认值为latestMAINTAINER(新版本过时)#
Dockerfile作者信息,一般格式是:姓名+邮箱地址- 并不限制
MAINTAINER指令的位置,但建议放在FROM指令之后 - 在较新的
docker版本中,MAINTAINER已经被LABEL替代
MAINTAINER "merle@example.com"LABEL#
为镜像指定各种元数据(键值对格式): LABEL 会继承基础镜像中的 LABEL ,如遇 key 相同值将会被覆盖
LABEL <key>=<value> <key>=<value>COPY#
复制宿主机上的文件到目标镜像中,格式:
COPY [--chown=:] <源路径>... <目标路径>
COPY [--chown=:] ["<源路径1>",... "<目标路径>"]和 RUN 指令一样,COPY 也有两种格式,一种类似于命令行,一种类似于函数调用。COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。比如:
COPY package.json /usr/src/app/<源路径> 可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 filepath.Match 规则,比如:
COPY hom* /mydir/
COPY hom?.txt /mydir/<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先创建缺失目录。
注意:使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候
在使用该指令的时候还可以加上 --chown=<user>:<group> 选项来改变文件的所属用户及所属组
COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/ADD#
ADD指令跟COPY类似,不过它支持使用tar文件和URL路径- 当拷贝的源文件是
tar文件时,会自动展开为一个目录并拷贝进新的镜像中。通过URL获取到的tar文件不会自动展开 - 主机可以联网的情况下,
docker build可以将网络上的某文件引用下载并打包到新的镜像中
ADD <src>... <dest>
ADD ["<src>",... "<dest>"]注意:
ADD指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。在 Docker 官方的 Dockerfile 最佳实践文档 中要求,尽可能的使用 COPY,因为 COPY 的语义很明确,就是复制文件,而 ADD 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用 ADD 的场合,就是所提及的需要自动解压缩的场合。因此在COPY和ADD指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用COPY指令,仅在需要自动解压缩的场合使用ADD
在使用该指令的时候也可以加上 --chown=<user>:<group> 选项来改变文件的所属用户及所属组
ADD --chown=55:mygroup files* /mydir/
ADD --chown=bin files* /mydir/
ADD --chown=1 files* /mydir/
ADD --chown=10:11 files* /mydir/WORKDIR#
- 同
docker run -w - 指定工作目录,可以指定多个,每个
WORKDIR只影响它下面的指令,直到遇见下一个WORKDIR为止 WORKDIR可以调用由ENV指令定义的变量WORKDIR相对路径或者绝对路径:相对路径是相对于上一个WORKDIR指令的路径,如果上面没有WORKDIR指令,那就是当前Dockerfile文件的目录
VOLUME#
- 同
docker run -v - 用于在镜像中创建一个挂载点目录。之前提到
Volume有两种类型:挂载主机目录和数据卷。在Dockerfile中只支持docker数据卷,也就是说只能指定容器内的路径,不能指定宿主机的路径
VOLUME <mountpoint>
VOLUME ["<mountpoint>"]EXPOSE#
- 同
docker run -expose指定容器中待暴露的端口。比如容器提供的是一个https服务且需要对外提供访问,那就需要指定待暴露443端口,然后在使用此镜像启动容器时搭配-P参数才能将待暴露的状态转换为真正暴露的状态,转换的同时443也会转换成一个随机端口,跟-p:443一个意思 EXPOSE指令可以一次指定多个端口,例如:EXPOSE 11111/udp 11112/tcp
EXPOSE <port>[/<protocol>] [<port>[/<protocol>] ...]
<protocol>用于指定协议类型,如果不指定,默认TCP协议ENV#
- 同
docker run -e - 为镜像定义所需的环境变量,并可被
ENV指令后面的其它指令所调用。调用格式为$variable_name或者${variable_name} - 使用
docker run启动容器的时候加上-e的参数为variable_name赋值,可以覆盖Dockerfile中ENV指令指定的环境变量值。但不会影响到Dockerfile中已经引用过此变量的文件
ENV <key> <value>
ENV <key>=<value> ...
# 第一种格式一次只能定义一个变量,<key>之后所有内容都会被视为<value>的组成部分
# 第二种格式一次可以定义多个变量,每个变量为一个"="的键值对,如果<value>中包含空格,可以用反斜线 \ 进行转义,也可以为<value>加引号,另外参数过长时可用反斜线做续行。
# 定义多个变量时,建议使用第二种方式,因为Dockerfile中每一行都是一个镜像层,构建起来比较吃资源RUN#
- 用于指定
docker build过程中运行的程序,可以是任何命令 RUN指令后所执行的命令必须在FROM指令后的基础镜像中存在才行
RUN <command>
RUN ["executable", "param1", "param2"]第一种后边直接跟 shell 命令
linux上默认/bin/sh -cwindows上默认cmd /S /C
第二种类似于函数调用,可将 executable 理解成为可执行文件,后面就是两个参数。
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOMERUN ["/bin/bash", "-c", "echo hello"]
注意:多行命令不要写多个RUN,原因是Dockerfile中每一个指令都会建立一层,多少个RUN就构建了多少层镜像,会造成镜像的臃肿、多层,不仅仅增加了构件部署的时间,还容易出错。RUN书写时的换行符是
\
CMD#
- 指定启动容器的默认要运行的程序,也就是
PID为1的进程命令,且其运行结束后容器也会终止。如果不指定,默认是bash CMD指令指定的默认程序会被docker run命令行指定的参数所覆盖Dockerfile中可以存在多个CMD指令,但仅最后一个生效。因为一个docker容器只能运行一个PID为1的进程。CMD类似于RUN指令,也可以运行任意命令或程序,但是两者的运行时间点不同(RUN指令运行在docker build的过程中,而CMD指令运行在基于新镜像启动容器也就是docker run时)
CMD command param1 param2
CMD ["executable","param1","param2"]
CMD ["param1","param2"]前两种语法格式同 RUN 指令。第一种用法对于CMD指令基本没有意义,因为它运行的程序PID不为1。
第三种则需要结合 ENTRYPOINT 指令使用,CMD指令后面的命令作为 ENTRYPOINT 指令的默认参数。如果 docker run 命令行结尾有参数指定,那 CMD 后面的参数不生效。
ENTRYPOINT#
- 类似
CMD指令的功能,用于为容器指定默认运行程序 Dockerfile中可以存在多个ENTRYPOINT指令,但仅最后一个生效- 与CMD区别在于:由
ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且这些命令行参数会被当做参数传递给ENTRYPOINT指令指定的程序 docker run的--entrypoint选项的参数可覆盖ENTRYPOINT指定的默认程序
ENTRYPOINT command param1 param2
ENTRYPOINT ["executable", "param1", "param2"]USER#
- 用于指定
docker build过程中任何RUN、CMD等指令的用户名或者UID - 默认情况下容器的运行用户为
root
USER <user>[:<group>]
USER <UID>[:<GID>]实践中UID需要是
/etc/passwd中某用户的有效UID,否则docker run命令将运行失败
HEALTHCHECK#
顾名思义,健康检查。此指令的就是告诉 docker 检查容器是否正常工作
HEALTHCHECK [OPTIONS] CMD command
HEALTHCHECK NONEHEALTHCHECK 指令定义一个 CMD ,在CMD后面编写一条命令去判断服务运行是否正常。检查肯定不是一次性的,所以 OPTIONS 就是指定检查的频率等
--interval=DURATION(默认值:30s):每隔多久检查一次,默认30s--timeout=DURATION(默认值:30s):超时时长,默认30s--start-period=DURATION(默认值:0s):启动健康检查的等待时间。因为容器启动成功时,进程不一定立马就启动成功,过早开始检查就会返回不健康--retries=N(默认值:3):如果检查一次失败就返回不健康未免太武断,所以默认三次机会
CMD 健康检测命令发出时,返回值有三种情况:
- 0:成功
- 1:不健康
- 2:保留,无实际意义。
HEALTHCHECK NONE 就是不做健康检查
HEALTHCHECK --interval=5m --timeout=3s
CMD curl -f http://localhost/ || exit 1SHELL#
用来指定运行程序默认要使用的 shell 类型,因为 windows 环境默认是 powershell 。此指令一般不会使用。
SHELL ["executable", "parameters"]STOPSIGNAL#
指定发送使容器退出的系统调用信号。 docker stop 之所以能停止容器,就是发送了15的信号给容器内PID为1的进程。此指令一般不会使用。
STOPSIGNAL signalARG#
ARG 命令同 EVN 类似,也是指定一个变量,但不同的是, ENV 指令配合 -e 参数可以在 docker run 过程中传参,而使用 ARG 指令配合 --build-arg 参数可以在 docker build 过程中传参,方便为不同场景构建不同镜像。
ARG <name>[=<default value>]ONBUILD#
- 用于在
Dockerfile中定义一个触发器 ONBUILD后面指定的指令在docker build时是不会执行,构建完的镜像在被另一个Dockerfile文件中FROM指令所引用的时才会触发执行
ONBUILD [INSTRUCTION]- 几乎任何指令都可以成为触发器指令,但
ONBUILD不能自我嵌套,且不会触发FROM和MAINTAINER指令,多数情况是使用RUN或者ADD - 在使用
COPY指令时,应该注意后续引用该镜像的Dockerfile的同级目录下是否有被拷贝的文件