Dockerfile指令详解

dockerfile记载了从一个镜像创建另一个新镜像的步骤。撰写好Dockerfile文件之后,我们就可以轻而易举的使用docker build命令来创建镜像了。Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了 Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻烦。

Dockerfile的组成部分

部分命令
基础镜像信息FROM
维护者信息MAINTAINER
镜像操作指令RUN、COPY、ADD、EXPOSE、WORKDIR、ONBUILD、USER、VOLUME等
容器启动时执行指令CMD、ENTRYPOINT

下面主要列举出Dockerfile中比较常用的指令及其用法和说明,需要注意的是这些指令都是全部大写。

FROM

  • 说明:指定该镜像的基础镜像
  • 格式如:FROM <image>FROM <image>:<tag>
  • 示例:FROM centos:7
  • 注意:FROM一般出现在文件头,且每个镜像都需要有一个FROM来指定基础镜像。

MAINTAINER

  • 说明:指定作者信息
  • 格式如:MAINTAINER <name>
  • 示例:MAINTAINER 知识林 "393156105@qq.com"

RUN

  • 说明:在构建镜像时执行脚本
  • 格式如:RUN <command>RUN ["exec", "par1", "par2"]
  • 示例:RUN ls -l(以详细信息方式列表当前目录下的文件,跟在shell终端运行一样)

CMD

  • 说明:在运行容器时执行脚本
  • 格式如:CMD <command> <par1> <par2> ...CMD ["exec", "par1", "par2"]
  • 示例:CMD ["ls", "-l"]
  • 注意:一个Dockerfile中只有一条CMD,如果有多条只执行最后一条;在运行容器时如果用户指定了运行命令则Dockerfile中的CMD将被覆盖。

Label:

  • 说明:给构建的镜像打标签。

  • 格式如:LABEL <key>=<value> <key>=<value> <key>=<value> ...

  • 示例:

    1
    2
    3
    4
    5
    LABEL "com.example.vendor"="ACME Incorporated"
    LABEL com.example.label-with-value="foo"
    LABEL version="1.0"
    LABEL description="This text illustrates \
    that label-values can span multiple lines."

    一个镜像可以有多个标签,如果基础镜像也有标签则继承,名字相同的话则会覆盖。如果使用多个标签,建议合并成一个标签指令,如果使用多个标签指令, 则每个标签指令都会生成一个图层,这会导致镜像生成效率低下。举个栗子:

    1
    LABEL multi.label1="value1" multi.label2="value2" other="value3"

    也可以写成:

    1
    2
    3
    LABEL multi.label1="value1" \
    multi.label2="value2" \
    other="value3"
  • 注意:标签是键值对格式,要在标签中包含空格则需转义或用引号"括起来。

ENTRYPOINT

  • 说明:在运行容器时执行脚本
  • 格式如:ENTRYPOINT ["exec", "par1", "par2"]ENTRYPOINT command par1 par2
  • 示例:ENTRYPOINT ["catalina.sh", "run"]
  • 注意:一个Dockerfile中只有一条ENTRYPOINT,如果有多条只执行最后一条;不可以被容器运行时的命令所覆盖。

EXPOSE

  • 说明:让Docker暴露容器的端口号,供其他容器使用,在宿主机以外的网络中是无法使用的
  • 格式如:EXPOSE <port> ...
  • 示例:EXPOSE 8080
  • 注意:
    • 在Docker中有两种暴露端口的概念,一种叫EXPOSE隐式暴露,只供Docker服务内部使用;另一种叫PUBLISH显式暴露,供外部网络使用,PUBLISH只是一个概念在Dockerfile中没有这个指令。
    • EXPOSE 只在Dockerfile中出现,所暴露的端口只是被其他容器使用
    • PUBLISH 没有该指令而是通过docker run命令的参数-p-P或在docker-compose中的ports来体现
    • -P:大写是属于自动映射,将Dockerfile中EXPOSE所暴露的所有端口分别映射到宿主机的随机端口,每次启动或重启容器时端口都可能有所不同
    • -p:小写是属于固定映射,格式如:-p 宿主端口:容器端口,宿主端口和容器端口可以是纯数字也可以是一个范围,如:-p 8060-8080:8060-8080,意为将宿主机的8060(含)到8080(含)的端口映射到容器的8060(含)到8080(含)端口,需要注意的是在使用范围时,宿主端口个数应该与容器端口个数匹配;但上面这个例子可以写成:-p 7060-7080:8060-8080,这样宿主的端口就在7060-7080范围内

ENV

  • 说明:指定环境变量,在Dockerfile文件中的后续代码中使用,在容器运行时也可以使用
  • 格式如:ENV <key> <value>
  • 示例:ENV tomcat_home /web/tomcat/

ADD

  • 说明:添加文件(夹)到容器
  • 格式如:ADD <src> <dest>
  • 示例:ADD web.jar /web.jar
  • 注意:复制指定的<src>到容器中的<dest><src>可以是Dockerfile所在目录的一个相对路径,也可以是一个URL,也可以是一个tar文件(tar文件将自动解压成文件目录)

COPY

  • 说明:添加文件(夹)到容器
  • 格式如:COPY <src> <dest>
  • 示例:COPY web.jar /web.jar
  • 注意:与ADD功能相似,只是不能指定URL,使用本地文件(夹)为源文件时,推荐使用COPY

VOLUME

  • 说明:创建挂载点

  • 格式如:VOLUME [path]

  • 示例:VOLUME ["/datas"]

  • 注意:

    • VOLUME在原理和概念上与EXPOSE差不多,都是属于供容器与容器间使用
    • 通过VOLUME挂载的卷可以供其他容器使用
  • 举例说明:

    • 创建一个Dockerfile来构建一个镜像,内容如下:

      1
      2
      FROM centos
      VOLUME ["/web/images", "/web/files"]
    • 构建镜像

      1
      docker build -t "zsl131/test01" .
    • 启动容器

      1
      docker run -d --name test-root
    • 可以使用命令:docker inspect test-root来查看容器详细信息,在Mounts部份可以看到两个挂载点:/web/images/web/files

    • 启动另一个容器来共用这两个挂载卷

      1
      docker run -it --name test-1 --volumes-from test-root centos

      注意:使用--volumes-from来指定挂载点,这时容器test-roottest-1里面都分别有挂载卷/web/images/web/files,可以启动任意多个容器使用--volumes-from来共用这些挂载卷,这些容器可以来自不同的镜像。当任何一个容器中的挂载卷中的文件发生变化时其他容器挂载卷中的内容也随之改变。

      容器test-root即使已经停止也可以在启动其他容器时使用--volumes-from test-root来挂载这些卷,只要test-root不被删除,不过如果test-root真被删除还可以使用--volumes-from test-1,因为容器test-1中还存在我们所需要的挂载卷,换句话说这些挂载卷永远存在直到所有使用这些挂载卷的容器都被删除。

    • VOLUMEdocker run参数-v是有区别的。docker run -v /host/web/images:/web/images -v /host/web/files:/web/files:rw是将容器内的/web/images挂载到宿主机的/host/web/images目录上;将容器内的/web/files挂载到宿主机的/host/web/files目录上,rw表示可读写。

WORKDIR

  • 说明:设置工作目录
  • 格式如:WORKDIR /path
  • 示例:WORKDIR /web
  • 注意:可以使用绝对路径,也可以使用相对路径,设置之后的所有操作都将在这个目录下完成

ARG:

指定用户在docker build --build-arg <varname>=<value>时可以使用的参数。

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

如果用户指定了未在Dockerfile中定义的构建参数,则构建会输出警告。

1
[Warning] One or more build-args [foo] were not consumed.

构建参数在定义的时候生效而不是在使用的时候。如下面第三行开始的user才是用户构建参数传递过来的user:

1
2
3
4
5
FROM busybox
# 此处获取不到用户传来的user
USER ${user:-some_user}
ARG user
USER $user

您可以使用ARGENV指令指定RUN指令可用的变量。使用ENV指令定义的环境变量始终覆盖同名的ARG指令。

1
2
3
4
5
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER v1.0.0
# 始终是v1.0.0
RUN echo $CONT_IMG_VER

正确的用法:

1
2
3
4
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER

要在多个阶段中使用arg,每个阶段都必须包含该ARG指令。

1
2
3
4
5
6
7
FROM busybox
ARG SETTINGS
RUN ./run/setup $SETTINGS

FROM busybox
ARG SETTINGS
RUN ./run/other $SETTINGS

此外docker还内置了一批构建参数,可以不用在Dockerfile中声明:HTTP_PROXYhttp_proxyHTTPS_PROXYhttps_proxyFTP_PROXYftp_proxyNO_PROXYno_proxy

注意:在使用构建参数(而不是在构建参数定义的时候)的指令中,如果构建参数的值发生了变化,会导致该指令发生变化,会重新寻找缓存。

特别注意

在上面的描述中可以看到有两组指令在功能上都差不多,但也是有区别的:

  • RUNCMDENTRYPOINT执行脚本的指令
    • 三个指令都是执行脚本
    • RUN是在创建镜像是执行,即使用docker build命令时执行,在一个Dockerfile里面可以有多个RUN
    • CMDENTRYPOINT是在运行容器时执行,即使用docker run命令时执行,这两个指令在Dockerfile中都只有最行一条被执行
    • CMD在使用docker run时可以加参数将Dockerfile中的CMD覆盖
    • ENTRYPOINT在Dockerfile中出现后就一定会在docker run时被执行,不必担心会被其他参数所覆盖。
  • ADDCOPY拷贝文件(夹)到容器
    • ADD拷贝文件(夹)时可以指定本地文件、远程URL地址,如果拷贝的是tar文件时将会被自动解压成文件夹
    • COPY拷贝文件(夹)时不可以指定远程URL地址,拷贝tar文件也不会被自动解压成文件夹,在拷贝本地文件时建议使用COPY

参考

Dockerfile指令详解
12 - Dockerfile指令详解(Docker系列)

------ 本文结束------
坚持原创技术分享,您的支持将鼓励我继续创作!

欢迎关注我的其它发布渠道