之前已经了解到镜像的定制实际上就是定制每一层所添加的配置、文件。如果可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,这就是Dockerfile。这里详细了解下编写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> 是可选项,如果没有选择,那么默认值为latest
MAINTAINER(新版本过时)
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 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有两种类型:挂载主机目录和docker数据卷。在dockerfile中只支持docker数据卷,也就是说只能指定容器内的路径,不能指定宿主机的路径VOLUME <mountpoint>VOLUME ["<mountpoint>"]
EXPOSE
同
docker run -expose指定容器中待暴露的端口。比如容器提供的是一个https服务且需要对外提供访问,那就需要指定待暴露443端口,然后在使用此镜像启动容器时搭配-P参数才能将待暴露的状态转换为真正暴露的状态,转换的同时443也会转换成一个随机端口,跟 -p :443一个意思EXPOSE指令可以一次指定多个端口,例如:EXPOSE 11111/udp 11112/tcpEXPOSE <port>[/<protocol>] [<port>[/<protocol>] ...]<protocol>用于指定协议类型,如果不指定,默认TCP协议
ENV
同
docker run -e- 为镜像定义所需的环境变量,并可被
ENV指令后面的其它指令所调用。调用格式为$variable_name或者${variable_name} - 使用
docker run启动容器的时候加上-e的参数为variable_name赋值,可以覆盖Dockerfile中ENV指令指定的此variable_name的值。但不会影响到dockerfile中已经引用过此变量的文件 ```dockerfile ENV
ENV
第一种格式一次只能定义一个变量,之后所有内容都会被视为的组成部分
第二种格式一次可以定义多个变量,每个变量为一个”=”的键值对,如果中包含空格,可以用反斜线 \ 进行转义,也可以为加引号,另外参数过长时可用反斜线做续行。
定义多个变量时,建议使用第二种方式,因为Dockerfile中每一行都是一个镜像层,构建起来比较吃资源
<a name="f8MCc"></a>### RUN- 用于指定 `docker build` 过程中运行的程序,可以是任何命令- `RUN` 指令后所执行的命令必须在 `FROM` 指令后的基础镜像中存在才行```dockerfileRUN <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 param2CMD ["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 param2ENTRYPOINT ["executable", "param1", "param2"]
USER
用于指定
docker build过程中任何RUN、CMD等指令的用户名或者UID- 默认情况下容器的运行用户为
root```dockerfile USER[: ]
USER
> 实践中UID需要是 `/etc/passwd` 中某用户的有效UID,否则docker run命令将运行失败<a name="lDmJ7"></a>### HEALTHCHECK顾名思义,健康检查。此指令的就是告诉 `docker` 如果检查容器是否正常工作<br />`HEALTHCHECK [OPTIONS] CMD command`<br />`HEALTHCHECK NONE`<br />`HEALTHCHECK` 指令去定义一个 `CMD` ,在CMD后面编写一条命令去判断服务运行是否正常。检查肯定不是一次性的,所以 `OPTIONS` 就是指定检查的频率等- `--interval=DURATION` (默认值:30s):每隔多久检查一次,默认30s- `--timeout=DURATION` (默认值:30s):超时时长,默认30s- `--start-period=DURATION` (默认值:0s):启动健康检查的等待时间。因为容器启动成功时,进程不一定立马就启动成功,过早开始检查就会返回不健康- `--retries=N` (默认值:3):如果检查一次失败就返回不健康未免太武断,所以默认三次机会CMD健康检测命令发出时,返回值有三种情况- 0:成功- 1:不健康- 2:保留,无实际意义。`HEALTHCHECK NONE` 就是不做健康检查```dockerfileHEALTHCHECK --interval=5m --timeout=3sCMD curl -f http://localhost/ || exit 1
SHELL
用来指定运行程序默认要使用的 shell 类型,因为 windows 环境默认是 powershell 。此指令一般不会使用
SHELL ["executable", "parameters"]
STOPSIGNAL
指定发送使容器退出的系统调用信号。 docker stop 之所以能停止容器,就是发送了15的信号给容器内PID为1的进程。此指令一般不会使用
STOPSIGNAL signal
ARG
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的同级目录下是否有被拷贝的文件
