之前已经了解到镜像的定制实际上就是定制每一层所添加的配置、文件。如果可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,这就是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/tcp
EXPOSE <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` 指令后的基础镜像中存在才行
```dockerfile
RUN <command>
RUN ["executable", "param1", "param2"]
第一种后边直接跟 shell
命令
linux
上默认/bin/sh -c
windows
上默认cmd /S /C
第二种类似于函数调用,可将 executable
理解成为可执行文件,后面就是两个参数
写法比对:
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME
RUN ["/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
```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` 就是不做健康检查
```dockerfile
HEALTHCHECK --interval=5m --timeout=3s
CMD 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
的同级目录下是否有被拷贝的文件