之前已经了解到镜像的定制实际上就是定制每一层所添加的配置、文件。如果可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,这就是Dockerfile。这里详细了解下编写Dockerfile的多个指令。

格式描述

Dockerfile 整体由两类语句组成

  • Comment 注释信息
  • Instruction arguments 指令参数,一行一个指令

注意

  • Dockerfile 文件名首字母必须大写
  • Dockerfile 指令不区分大小写,但为方便和参数做区分,通常指令使用大写字母
  • Dockerfile 指令按顺序从上至下依次执行
  • Dockerfile 第一个非注释行必须是 FROM 指令,用来指定制作当前镜像依据的是哪个基础镜像
  • Dockerfile 需要调用的文件必须跟 Dockerfile 文件在同一目录下,或者在其子目录下。父目录或者其它路径无效

    FROM

  • 功能为指定基础镜像,并且必须是第一条指令

  • 如果不以任何镜像为基础写法为: FROM scratch ,意味着接下来所写的指令将作为镜像的第一层开始 注意:如果没有指定仓库, > docker build 会先从本机查找是否有此基础镜像,如果没有默认去 > Docker Hub Registry 上拉取,再找不到就会报错> **

    1. FROM <image>
    2. FROM <image>:<tag>
    3. FROM <image>:<digest>
    4. # 三种写法,其中<tag>和<digest> 是可选项,如果没有选择,那么默认值为latest

    MAINTAINER(新版本过时)

  • Dockerfile 作者信息,一般格式是:姓名+邮箱地址

  • 并不限制 MAINTAINER 指令的位置,但建议放在 FROM 指令之后
  • 在较新的 docker 版本中,MAINTAINER 已经被 LABEL 替代
    1. MAINTAINER "merle@example.com"

    LABEL

    为镜像指定各种元数据(键值对格式): LABEL 会继承基础镜像种的 LABEL ,如遇到 key 相同 值会被覆盖
    1. LABEL <key>=<value> <key>=<value>

    COPY

    复制宿主机上的文件到目标镜像中,格式:
    1. COPY [--chown=:] <源路径>... <目标路径>
    2. COPY [--chown=:] ["<源路径1>",... "<目标路径>"]
    RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。比如:
    1. COPY package.json /usr/src/app/
    <源路径> 可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 filepath.Match 规则,比如:
    1. COPY hom* /mydir/
    2. COPY hom?.txt /mydir/
    <目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录

    注意:使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候

在使用该指令的时候还可以加上 --chown=<user>:<group> 选项来改变文件的所属用户及所属组

  1. COPY --chown=55:mygroup files* /mydir/
  2. COPY --chown=bin files* /mydir/
  3. COPY --chown=1 files* /mydir/
  4. COPY --chown=10:11 files* /mydir/

ADD

  • ADD 指令跟 COPY 类似,不过它支持使用 tar 文件和 URL 路径
  • 当拷贝的源文件是 tar 文件时,会自动展开为一个目录并拷贝进新的镜像中;通过 URL 获取到的 tar 文件不会自动展开
  • 主机可以联网的情况下, docker build 可以将网络上的某文件引用下载并打包到新的镜像中
    1. ADD <src>... <dest>
    2. ADD ["<src>",... "<dest>"]

    注意:ADD 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。在 Docker 官方的 Dockerfile 最佳实践文档 中要求,尽可能的使用 COPY,因为 COPY 的语义很明确,就是复制文件,而 ADD 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用 ADD 的场合,就是所提及的需要自动解压缩的场合。因此在 COPYADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD

在使用该指令的时候可以加上 --chown=<user>:<group> 选项来改变文件的所属用户及所属组

  1. ADD --chown=55:mygroup files* /mydir/
  2. ADD --chown=bin files* /mydir/
  3. ADD --chown=1 files* /mydir/
  4. 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数据卷,也就是说只能指定容器内的路径,不能指定宿主机的路径

    1. VOLUME <mountpoint>
    2. VOLUME ["<mountpoint>"]

    EXPOSE

  • docker run -expose 指定容器中待暴露的端口。比如容器提供的是一个https服务且需要对外提供访问,那就需要指定待暴露443端口,然后在使用此镜像启动容器时搭配 -P 参数才能将待暴露的状态转换为真正暴露的状态,转换的同时443也会转换成一个随机端口,跟 -p :443一个意思

  • EXPOSE 指令可以一次指定多个端口,例如: EXPOSE 11111/udp 11112/tcp

    1. EXPOSE <port>[/<protocol>] [<port>[/<protocol>] ...]
    2. <protocol>用于指定协议类型,如果不指定,默认TCP协议

    ENV

  • docker run -e

  • 为镜像定义所需的环境变量,并可被 ENV 指令后面的其它指令所调用。调用格式为 $variable_name 或者 ${variable_name}
  • 使用 docker run 启动容器的时候加上 -e 的参数为 variable_name 赋值,可以覆盖 DockerfileENV 指令指定的此 variable_name 的值。但不会影响到 dockerfile 中已经引用过此变量的文件 ```dockerfile ENV

ENV =

第一种格式一次只能定义一个变量,之后所有内容都会被视为的组成部分

第二种格式一次可以定义多个变量,每个变量为一个”=”的键值对,如果中包含空格,可以用反斜线 \ 进行转义,也可以为加引号,另外参数过长时可用反斜线做续行。

定义多个变量时,建议使用第二种方式,因为Dockerfile中每一行都是一个镜像层,构建起来比较吃资源

  1. <a name="f8MCc"></a>
  2. ### RUN
  3. - 用于指定 `docker build` 过程中运行的程序,可以是任何命令
  4. - `RUN` 指令后所执行的命令必须在 `FROM` 指令后的基础镜像中存在才行
  5. ```dockerfile
  6. RUN <command>
  7. 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 时)

    1. CMD command param1 param2
    2. CMD ["executable","param1","param2"]
    3. 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 指定的默认程序

    1. ENTRYPOINT command param1 param2
    2. ENTRYPOINT ["executable", "param1", "param2"]

    USER

  • 用于指定 docker build 过程中任何 RUNCMD 等指令的用户名或者 UID

  • 默认情况下容器的运行用户为 root ```dockerfile USER [:]

USER [:]

  1. > 实践中UID需要是 `/etc/passwd` 中某用户的有效UID,否则docker run命令将运行失败
  2. <a name="lDmJ7"></a>
  3. ### HEALTHCHECK
  4. 顾名思义,健康检查。此指令的就是告诉 `docker` 如果检查容器是否正常工作<br />`HEALTHCHECK [OPTIONS] CMD command`<br />`HEALTHCHECK NONE`<br />`HEALTHCHECK` 指令去定义一个 `CMD` ,在CMD后面编写一条命令去判断服务运行是否正常。检查肯定不是一次性的,所以 `OPTIONS` 就是指定检查的频率等
  5. - `--interval=DURATION` (默认值:30s):每隔多久检查一次,默认30s
  6. - `--timeout=DURATION` (默认值:30s):超时时长,默认30s
  7. - `--start-period=DURATION` (默认值:0s):启动健康检查的等待时间。因为容器启动成功时,进程不一定立马就启动成功,过早开始检查就会返回不健康
  8. - `--retries=N` (默认值:3):如果检查一次失败就返回不健康未免太武断,所以默认三次机会
  9. CMD健康检测命令发出时,返回值有三种情况
  10. - 0:成功
  11. - 1:不健康
  12. - 2:保留,无实际意义。
  13. `HEALTHCHECK NONE` 就是不做健康检查
  14. ```dockerfile
  15. HEALTHCHECK --interval=5m --timeout=3s
  16. CMD curl -f http://localhost/ || exit 1

SHELL

用来指定运行程序默认要使用的 shell 类型,因为 windows 环境默认是 powershell 。此指令一般不会使用

  1. SHELL ["executable", "parameters"]

STOPSIGNAL

指定发送使容器退出的系统调用信号。 docker stop 之所以能停止容器,就是发送了15的信号给容器内PID为1的进程。此指令一般不会使用

  1. STOPSIGNAL signal

ARG

  • ARG 命令同 EVN 类似,也是指定一个变量,但不同的是, ENV 指令配合 -e 参数可以在 docker run 过程中传参,而使用 ARG 指令配合 --build-arg 参数可以在 docker build 过程中传参,方便为不同场景构建不同镜像

    1. ARG <name>[=<default value>]

    ONBUILD

  • 用于在 Dockerfile 中定义一个触发器

  • ONBUILD 后面指定的指令在 docker build 时是不会执行,构建完的镜像在被另一个 Dockerfile 文件中 FROM 指令所引用的时才会触发执行

    1. ONBUILD [INSTRUCTION]
  • 几乎任何指令都可以成为触发器指令,但 ONBUILD 不能自我嵌套,且不会触发 FROMMAINTAINER 指令,多数情况是使用 RUN 或者 ADD

  • 在使用 COPY 指令时,应该注意后续引用该镜像的 Dockerfile 的同级目录下是否有被拷贝的文件