Dockerfile常用指令

这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战

上一篇:Docker 镜像管理

上一篇中,有一个简单的 Dockerfile 示例。Dockerfile 是由一系列指令组成的,通过使用 docker build 命令构建新的镜像。值得一提的是,Dockerfile 中的每一条指令都会顺序执行被执行,并且每一步的构建结果都会被提交,也就是说每一条指令执行成功后都会输出一个镜像。 (这一点在执行 docker build 命令时从控制台的输出内容也能看出来)

之前只是给出了一个简单的 Dockerfile 示例,这一篇来详细介绍 Dockerfile 中常用的指令。

FROM

FROM 是几乎所有 Dockerfile 文件的第一个指令,它用来指定一个已经存在的镜像,作为当前镜像的基础镜像(父镜像),后续的指令都基于这个镜像进行。比如:

FROM ubuntu:12.04
复制代码

唯一可以出现在 FROM 之前的是 ARG 指令,它可以在 FROM 指令之前声明一个变量给 FROM 使用。

ARG IMAGE_TAG=latest

FROM ubuntu:${IMAGE_TAG}
复制代码

关于 ARG 的具体用法,下文会详细介绍。

RUN

RUN 指令将在当前镜像的顶部执行一个命令,并提交结果,用于下一个指令。例如:

RUN apt-get update && apt-get install -y nginx
复制代码

RUN 指令有两种书写格式:

  • RUN apt-get update
  • RUN ["apt-get", "update"]

CMD

CMD 指令也会执行一个命令,但是一个 Dockerfile 当中只能有一个 CMD 指令,更严谨地说,Dockerfile 中只有最后一次出现的 CMD 命令会生效。它为执行容器提供了一个默认值。

在上一篇的示例中,使用一下命令运行了一个 Nginx 容器,并启动了其中的 Nginx 服务:

docker run -d -p 80 pseudocode/nginx_image nginx -g "daemon off;"
复制代码

如果我们在构建镜像时,在 Dockerfile 末尾增加了 CMD 命令:

CMD ["nginx", "-g:", "daemon off;"]
复制代码

那么,再运行容器的时候,就可以省去最后执行命令的部分:

docker run -d -p 80 pseudocode/nginx_image
复制代码

之所以说它提供了一个默认值,是因为,当运行容器时没有指定要执行的命令,那么 CMD 指令中的内容会被执行,如果再运行容器时指定了要在容器中执行的命令,那么 CMD 中的内容将被覆盖,不被执行。

ENTRYPOINT

ENTRYPOINT 指令和 CMD 指令非常相似,他们的区别是,启动容器时提供的命令不会覆盖 ENTRYPOINT 指令提供的命令,而 docker run 命令中指定的参数都会被传递个 ENTRYPOINT 中的命令。因此,我们可以在 Dockerfile 中指定容器运行的命令,而在 docker run 命令中为其提供参数。

ENTRYPOINT ["nginx"]
复制代码
docker run -d -p 80 pseudocode/nginx_image -g "daemon off;"
复制代码

这样既可以限制容器中执行的命令,又可以为其提供修改参数的可能。

ENTRYPOINT 还可以结合 CMD 指令,巧妙地为命令提供默认的参数:

ENTRYPOINT ["nginx"]

CMD ["-g", "daemon off;"]
复制代码

这样,如果在运行容器时没有为 ENTRYPOINT 中的命令提供参数,则默认使用 CMD 包含的命令参数,如果指定了,那么 CMD 中的命令参数则会被覆盖。

LABEL

LABEL 指令可以为镜像添加一些原数据,比如:

LABEL version="1.0"

LABEL role="db" name="mysql"
复制代码

这些元数据可以通过 docker inspect 命令查看镜像的详细信息时看到。

EXPOSE

EXPOSE 指令可以告诉 Docker 在容器运行时监听的网络端口,它并不会发布端口,在容器运行时,需要通过 docker run-p 指定要发布的端口号何其映射的宿主机端口号。

EXPOSE 的用法如下:

EXPOSE 80

EXPOSE 80/udp

EXPOSE 80/tcp
复制代码

可以指定端口在 TCP 或者 UDP 协议上监听,如果不指定的话,默认为 TCP 协议。

ENV

ENV 指令用来在镜像构建过程中设置环境变量,如:

ENV JAVA_HOME /path/to/java/
复制代码

这个环境变量可以在后续的任何 RUN 指令中使用。

ADD

ADD 指令可以将构建上下文中的文件和目录复制到镜像中。

ADD xxx.tar.gz /usr/local/
复制代码

这个指令会讲构建上下文中的 xxx.tar.gz 文件复制到镜像中的 /usr/local/ 目录中。而且,当 ADD 命令处理归档文件的时候,会自动解压,这算是 ADD 命令的一个小魔法。

指令后面的文件路径如果以 / 结尾,那么 Docker 会将它作为目录,操作目录中的文件,否则会将其作为文件处理。

COPY

COPY 与 ADD 类似,区别是,它只复制构建上下文中的文件,但不会做任何处理。如:

COPY conf.d/ /etc/nginx/conf.d/
复制代码

值得注意的是,本地文件的路径必须是基于当前构建上下文的相对路径,而且文件都必须放在构建上下文目录中。否则,文件将不会在构建开始时发送给 Docker 守护进程中。

VOLUME

VOLUME 指令可以创建一个具有特定名称的挂载点,然后挂载来自宿主机或者其他容器的卷。卷是一个特定的目录,跟普通的目录相比,有几点不同:

VOLUME ["/usr/app"]
复制代码

上面的指令会为基于当前镜像创建的容器创建一个名为 /usr/app 的挂载点。

USER

USER 指令可以指定基于当前镜像的容器以什么用户运行,比如:

USER nginx
复制代码

WORKDIR

WORKDIR 指令用来设置工作目录,ENTRYPOINT 和 CMD 的命令将会在这个目录下执行。

WORKDIR /opt/webapp

CMD ["java", "-jar", "xxx.jar"]
复制代码

WORKDIR 指令可以有多个,每一个指令设置的工作目录都会被它之后的其他指令作为工作哦目录。

ARG

ARG 指令用来定义在 docker build 运行时传递个构建运行时的变量。前面在介绍 FROM 指令的时候提到过它的用法:

ARG username=pseudocode
复制代码

以上指令相当于定义了一个变量,并且制定了它的默认值。也可以只定义不指定默认值:

ARG username
复制代码

无论是否具有默认值,都可以在构建时为其指定一个值,此时,如果具有默认值,则会被 docker build 命令中指定的值覆盖:

docker build --build-arg username=someone -t image/tag .
复制代码

ONBUILD

从 ONBUILD 指令的名字就看得出来,它是构建动作的触发器。当一个镜像被用作其它镜像的基础镜像时,这个触发器就会被执行。

ONBUILD ADD . /var/www/
复制代码

如上,ONBUILD 指令后跟的是另外一条指令。如果有一个新的镜像将当前镜像作为父镜像,那么它在构建时,ONBUILD 触发器中的指令就会执行,实行的顺序与父镜像中定义的顺序相同,在子镜像的 FROM 指令之后。

注意,FROM 指令,以及 ONBUILD 指令本身不能作为 ONBUILD 触发器中的指令。

HEALTHCHECK

HEALTHCHECK 指令可以告诉 Docker 如何检查容器在正常工作,一个 Dockerfile 中只能有一个 HEALTHCHECK 指令,否则只有最后一个会生效。它的书写格式如下:

HEALTHCHECK [OPTIONS] CMD command
复制代码

其中的 [OPTIONS] 可以设置轮训时间、超时时间、重试次数等,CMD 后面是检查时需要执行的命令,命令需要通过一个退出状态表示容器的运行状况:

  • 0 表示容器是健康的
  • 1 表示容器不能正常工作

比如下面的指令,会每 5 分钟检查一次是否能在三秒内相应网络请求。

HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1
复制代码