Dockerfile文件由一系列指令和参数组成。指令的一般格式为INSTRUCTION arguments。具体来说,包括"配置指令"(配置镜像信息)和"操作指令"(具体执行操作)。每条指令,如FROM,都是大小写不敏感的。但是为了区分指令和参数,强烈建议统一使用大写字母。Dockerfile中的指令会按顺序从上到下执行,所以应根据需要合理安排指令的顺序。一个Dockerfile文件以FROM指令作为构建开始的第一个指令。每条指令都会创建一个新的镜像层并对镜像进行提交。Dockerfile目前支持的指令中具体如下表所述:
分类 | 指令 | 描述 |
---|---|---|
配置指令 | ARG | 定义创建镜像过程中使用的变量 |
配置指令 | FROM | 指定所创建镜像的基础镜像 |
配置指令 | LABEL | 为生成的镜像添加元数据标签信息 |
配置指令 | MAINTAINER(deprecated) | 指定镜像的作者,不推荐使用,可以使用LABEL指令替换 |
配置指令 | EXPOSE | 声明镜像内服务监听的端口 |
配置指令 | ENV | 指令环境变量 |
配置指令 | ENTRYPOINT | 指定镜像的默认入口命令 |
配置指令 | VOLUME | 创建一个数据卷挂载点 |
配置指令 | USER | 指定运行容器时的用户名和用户组等信息 |
配置指令 | WORKDIR | 配置工作目录 |
配置指令 | ONBUILD | 创建子镜像时指定自动执行的操作指令 |
配置指令 | STOPSIGNAL | 指定退出的信号值 |
配置指令 | HEALTHCHECK | 配置所启动容器如何进行健康检查 |
配置指令 | SHELL | 指定默认shell类型 |
操作指令 | RUN | 运行指定命令 |
操作指令 | CMD | 启动容器时指定默认执行的命令 |
操作指令 | ADD | 添加内容到镜像 |
操作指令 | COPY | 复制内容到镜像 |
更多Dockerfile指令的介绍可以参考Docker官网Dockerfile reference一文。
ARG
ARG指令用来定义构建镜像过程中需要使用的变量。指令格式如下:
ARG <name> [=<default value>]
在Dockerfile文件中使用ARG指令的示例如下:
ARG IMAGE_VERSION
RUN echo $IMAGE_VERSION
可见,这里使用$+变量的方式引用声明的变量。
这样在执行docker build时,可以通过–build-arg参数来为变量赋值。
$docker build --build-arg IMAGE_VERSION=V1
当使用docker build命令成功构建镜像后,ARG声明的变量将不再存在 (ENV指定的变量将在镜像中保留)。也就是说,ARG指令声明的变量,只存在构建阶段,并不会代入到镜像中,更不可能出现在容器中。
Docker内置了一些镜像创建变量,用户可以直接使用而无须声明,包括(不区分大小写)HTTP PROXY 、HTTPS PROXY、FTP PROXY、 NO PROXY等。
更多ARG介绍可以参考官网ARG一文。
FROM
FROM指令用来指定一个父镜像,以开始新的构建阶段。Dockerfile支持在一个文件中使用多个FROM指令,以创建多个镜像。FROM的指令格式如下:
FROM [--platform=<platform>] <image>[:<tag>|@<digest>|-] [AS <name>]
其中,–platform参数用来指定镜像应用的平台,主要应用于多平台场景,如linux/amd64或linux/arm64 等;tag和digest用来指定需要引用的镜像的tag,如果不指定,则使用latest。如果一个Dockerfile中需要创建多个镜像,会使用到多个FROM。如果下一个FROM指令中需要使用上一个FROM指令构建的镜像,可以现在上一个FROM指令中定义别名,也即使用AS <name>。某些场景下,不需要父镜像,如构建一个操作系统镜像,这时可以使用"FROM scratch"这个指令来表示不需要父镜像。
可以在FROM指令前使用ARG指令,但是FROM前的指令只能用于FROM指令,不能用于FROM后的指令,如果需要使用,还需重新声明。之所以会这样,是因为FROM代表一次构建,如果在FROM之前声明变量,则这个变量是在这次构建之外的,也就无法在构建中使用。示例如下:
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version
上述示例中,ARG中定义的第一个VERSION只能用于FROM指令,FROM后面的指定无法使用。为了使用VERSION变量,在RUN指定前又声明了一个VERSION。
更多FROM指令介绍可以参考官网FROM一文。
LABEL
LABEL指令可以为生成的镜像添加元数据标签信息。这些信息可以用来辅助过滤出特定镜像。格式为
LABEL <key>=<value> <key>=<value> <key>=<value> ...
LABEL <key>=<value> \<key>=<value> \<key>=<value> \...
可以使用上面两种方式的任何一种方式来使用LABEL,为生成的镜像添加元数据标签信息。如果LABEL的key或value包含空格、点号等特殊字符,可以使用双引号来引用。使用示例如下:
LABEL version=V1
LABEL "image verison"="V1.0.0"
LABEL author="hello-world@github.com" date="2024-03-07"
LABEL author="hello-world@github.com" \date="2024-03-07"
需要说明的是,使用FROM构建的镜像会继承父镜像(parent)的LABEL信息。如果LABEL中存在同名的key,则最近定义的key的value将覆盖已存在的key的value。
可以使用docker image inspect命令来查看指定镜像中包含的LABEL信息。示例如下:
$ docker image inspect --format='{{json .Config.Labels}}' target-image-name-or-id
更多LABEL指令介绍可以参考官网LABEL一文。
MAINTAINER(deprecated)
MAINTAINER指令可以用来指定镜像作者信息。格式为:
MAINTAINER <name>
不推荐使用MAINTAINER来指定镜像作者信息,推荐使用LABEL来指定镜像作者信息。使用LABEL来记录作者信息会以元数据方式存储,可以通过docker image inspect命令方便的查询。使用LABEL指令记录镜像作者信息示例如下:
LABEL author="hello-world@github.com" date="2024-03-07"
EXPOSE
EXPOSE指令用来声明镜像内服务监听的端口。指令格式为:
EXPOSE <port> [<port/<protocol>...]
一个服务可能需要监听多个端口,且不同的端口使用的协议不同。使用示例如下:
EXPOSE 22
EXPOSE 22/tcp
EXPOSE 80/udp
在EXPOSE指令中,如果不指定协议,则默认是tcp协议。
注意该指令只是起到声明的作用,并不会自动完成端口的映射。如果要映射端口出来,在启动容器时可以使用-P参数 (Docker主机会自动分配一个宿主机的临时端口)或-p HOST_PORT:CONTAINER_PORT参数(具体指定所映射的本地端口)。不推荐EXPOSE的使用方式,因为EXPOSE并不会完成容器到宿主机的端口映射,只是起到一个声明的效果。如果期望使用EXPOSE指令,还需和docker run命令的-P参数联合使用。这显然不符合关注点分离思想,且-P参数会分配一个随机端口,不推荐使用这种方式。
更多EXPOSE指令介绍可以参考官网EXPOSE一文。
ENV
ENV指令用来定义环境变量。指令格式为:
ENV <key1> <value1>
ENV <key1>=<value1> <key2>=<value2>
在使用ENV指令定义环境变量时,key和value之间的等号可以省略,但是不推荐这么做,因为省略等号后,一个ENV指令只能定义一个key-value。推荐使用带等号的方式定义环境变量。
一个ENV指令可以同时定义多个key-value,且对于特殊字符,可以使用双引号包裹。使用示例如下:
ENV JAVA_HOME=/etc/bin JRE="/etc/jre"
ENV "JAVA HOME"="/etc/bin"
与ARG指令定义的变量只能应用于镜像构建阶段不同,ENV指令不仅可以应用于镜像构建阶段,还会持久化到镜像中,并可在容器运行时使用。
ENV定义的环境变量在Dockerfile中使用的方式与ARG定义的变量的使用方式一致,都是使用$+变量名的方式,使用示例如下:
ENV JAVA_HOME=/etc/bin JRE="/etc/jre"
RUN echo $JAVA_HOME
可以使用docker image inspect命令来查看指定镜像中包含的ENV信息。示例如下:
$ docker image inspect --format='{{json .Config.Env}}' target-image-name-or-id
更多ENV指令介绍可以参考官网ENV一文。
CMD
CMD指令用来指定启动容器时默认执行的命令。支持三种格式:
CMD ["executable","param1", "param2"],exec模式,相当于执行 executable param1 param2,推荐方式
CMD command param1 param2, shell模式,在默认的shell中执行,提供给需要交互的应用
CMD ["param1","param2"],exec模式,提供给ENTRYPOINT指令的默认参数
每个Dockerfile中,只能有一条CMD指令。如果出现了多个CMD指令,不会报错,但只有最后一条会被执行。如果用户启动容器时候手动指定了运行的命令(作为docker run命令的参数),则会覆盖掉CMD指定的命令。
CMD指令中的命令不会在镜像构建中执行,而是在启动容器时执行。更多CMD指令介绍可以参考官网CMD一文。
ENTRYPOINT
ENTRYPOINT指令用来指定默认可执行文件。支持两种格式:
ENTRYPOINT ["executable", "paraml1", "param2"],exec模式,相当于执行 executable param1 param2
ENTRYPOINT command param1 param2,shell模式,在默认的shell中执行,提供给需要交互的应用
在执行docker run命令时,对于CMD指令,docker run命令中的参数会覆盖CMD指令中的参数,而对于ENTRYPOINT指令中的参数,而不会被覆盖,ENTRYPOINT指令中的参数会被保留。但是,如果docker run命令中使用了–entrypoint选项,则也将覆盖ENTRYPOINT指令指定的命令。
与CMD指令一样,每个Dockerfile中只能有一个ENTRYPOINT,如果出现了多个ENTRYPOINT指令,只有最后一个起效。
CMD和ENTRYPOINT的关系
CMD指令和ENTRYPOINT均用来定义启动容器时需要执行的命令。两者需遵循以下规则:
(1) Dockerfile中至少有一个CMD或ENTRYPOINT指令。
(2) 每个Dockerfile中只能有一个ENTRYPOINT/CMD生效,如果出现了多个ENTRYPOINT/CMD指令,只有最后一个起效。
(3) 当容器是一个可执行程序时,应该优先使用ENTRYPOINT。
(4) CMD可用来为ENTRYPOINT提供默认参数,也可用来在容器中执行临时命令(ad-hoc command)。
(5) docker run命令中的参数会覆盖CMD指令中的相同参数,但不会覆盖ENTRYPOINT。如果docker run命令中使用–entrypoint选项,则会覆盖ENTRYPOINT指令中的命令。
CMD和ENTRYPOINT的关系可以用下表表示:
No ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [“exec_entry”, “p1_entry”] | |
---|---|---|---|
No CMD | error, not allowed | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
CMD [“exec_cmd”, “p1_cmd”] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry exec_cmd p1_cmd |
CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |
上表中,如果一个Dockerfile中即不存在CMD指令,也不存在ENTRYPOINT指令,则会导致构建报错。对同时存在CMD指令和ENTRYPOINT指令的场景,要根据指令使用的exec模式或shell模式,进行如下划分:如果ENTRYPOINT指令是shell模式,则会忽略CMD指令;如果ENTRYPOINT指令是exec模式,CMD指令是exec模式,则CMD指令的命令失效,参数会追加到ENTRYPOINT指令上;如果ENTRYPOINT指令是exec模式,CMD指令是shell模式,则先执行ENTRYPOINT指令,再执行CMD指令。
更多ENTRYPOINT指令介绍可以参考官网ENTRYPOINT一文。
RUN
RUN指令用来执行构建命令。一条RUN指令会在当前镜像的最上层创建一个新的层。RUN指令支持两种格式:
## Shell form,默认在 shell 终端中运行命令
RUN [OPTIONS] <command> ...
## Exec form,使用 exec 执行,不会启动 shell 环境
RUN [OPTIONS] [ "<command>", ... ]
其中使用双引号的方式,会被解析为JSON数组,所以必须使用双引号。
如果需要使用其他类型shell,也可在exec格式中指定shell,示例如下:
RUN ["/bin/bash", "- C", "echo hello"]
但是,上述方式并不会更改当前构建上下文的默认shell,如果需要修改,可以使用SHELL指令。
注意,每条RUN指令将在当前镜像基础上执行指定命令,并提交为新的镜像层。所以,为减少镜像层数,通常将多个RUN指令合并成一条RUN指令执行,使用&&连接指令。当指令较长时,可以使用\来换行。示例如下:
RUN apt-get update \
&& apt-get install -y libsnappy-dev zliblg-dev libbz2-dev \
&& rm -rf /var/cache/apt
更多RUN指令介绍可以参考官网RUN一文。接下来重点介绍RUN指令支持的OPTIONS。
–mount选项
RUN指令使用–mount选项来调整命令执行时的网络环境类型。–link选项是BuildKit的一个新功能,可能在不同版本的Docker中有所不同,在使用前需先确认该功能是否可以可用。指令格式如下:
RUN --mount=[type=<TYPE>][,option=<value>[,option=<value>]...]
type的类型常用类型有如下几种:
(1) bind:这种挂载类型允许将上下文或镜像中的目录(只读)绑定到正在构建的容器中。需要注意的是,由于RUN指令只在容器构建阶段生效,因此挂载的目录也仅在该阶段可访问。另外,由于每次RUN指令都会创建新的层,所以只有同一个RUN指令中才能访问挂载的目录。此外,仅支持挂载上下文或者引用的镜像中存在的目录,不能挂载宿主机上的目录或者上下文以及镜像中不存在的目录。
(2) cache:这种挂载类型允许挂载缓存目录,从而加速构建过程。它可以用于访问编译器、包管理器或其他构建工具的缓存目录,从而避免重复下载或编译相同的依赖项。
(3) tmpfs:这是Docker 18.09版本引入的一种新方式。它用于挂载一个临时文件系统(tmpfs)到容器中的某个目录,并在容器内执行指定的命令。使用这种方式,可以在运行容器时动态创建一个临时的文件系统,从而提高容器的性能和安全性,因为临时文件系统中的数据将不会被持久化。
除了上述三种类型外,RUN --mount可能还支持其他类型的挂载,具体取决于Docker的版本和配置,如secret、ssh等。
–network选项
RUN指令使用–network选项来调整命令执行时的网络环境类型。指令格式如下:
RUN --network=<TYPE>
支持的类型有:
Type | 描述 |
---|---|
default | 在默认的网络环境运行命令,默认类型 |
none | 在没有网络访问的环境运行命令 |
host | 在宿主机的网络环境运行命令 |
–security选项
RUN指令使用–security选项来调整容器的安全模式。默认的安全模式是sandbox。当安全模式调整成insecure,则可以构建阶段以不安全模式运行没有沙箱的命令,这相当于运行docker run --privileged。使用示例如下:
## syntax=docker/dockerfile:1-labs
FROM ubuntu
RUN --security=insecure echo "hello world"
COPY
COPY指令用来复制文件或目录,并将其添加到容器的文件系统中。指令有两种格式:
COPY [OPTIONS] <src> ... <dest>
COPY [OPTIONS] ["<src>", ... "<dest>"]
其中使用双引号的方式,主要应用于路径中存在空格的情况,推荐使用带双引号的方式。
src指定的源路径可以有多个,且路径支持正则表达式的写法。src指定的源路径是相对于构建上下文所在的路径,而不是相对宿主机。
所谓Docker构建上下文,是指在进行Docker镜像构建时,传递给Docker构建命令(如docker build)的一组文件和目录。这些文件和目录包含了构建镜像所需的所有资源,如源代码、依赖项、配置文件等。构建上下文是Docker构建过程的基础,它定义了镜像的内容和结构。构建上下文可以是本地文件系统中的一个目录,也可以是一个远程的Git仓库或其他类型的资源。**当使用本地目录作为构建上下文时,通常需要将Dockerfile放在该目录的根目录下,并使用docker build命令指定该目录的路径。**Docker会递归地将该目录及其子目录中的所有文件打包到构建上下文中。注意,当使用标准输入(STDIN)构建镜像时(docker build - < somefile)时,构建上下文将不存在,这时COPY指令将失效。
dest只能有一个,要么是一个绝对路径,要么是一个基于WORKDIR的相对目录。
在使用COPY指令时,要遵循以下规范:
(1) src指定的路径必须在构建上下文中。对于需要访问构建上下文之外的路径将无法执行。如COPY …/path/file因为需要访问构建上下文之外的目录,所以在执行是会报错。
(2) 当src指定的是目录时,该目录下的所有内容都会拷贝到容器中,包括文件系统的元数据信息。注意,目录本身不会被拷贝。
(3) 如果dest指定的路径在容器中不存在,则会默认逐层创建。
更多COPY指令介绍可以参考官网COPY一文。接下来重点介绍COPY指令支持的选项:
–chown --chmod
COPY指令使用–chown和–chmod选项来调整用户/用户组,以及访问控制。注意,–chown和–chmod选项只支持Linux系统,对于Windows系统则不适用。使用格式如下:
COPY [--chown=<user>:<group>] [--chmod=<perms> ...] <src> ... <dest>
从构建上下文复制的文件或目录默认其UID和GID都是0。也就是说,一但使用USER去设置用户和用户组信息,则在使用COPY指令时,必须设置复制的文件或目录的UID和GID。此外,如果仅设置UID,而未设置GID,则GID和UID保持一致。使用示例如下:
COPY --chown=1000 --chmod=644 files* /somedir/
COPY --chown=1000:1000 --chmod=644 files* /somedir/
–link选项
COPY指令使用–link选项来创建新的镜像层,以便文件保持独立于其前身存在的层。使用–link选项后,Docker会创建一个新的层来保存COPY指令的结果,这个层将独立于之前的层。这有助于减少不必要的层,并提高构建效率,特别是在多阶段构建或频繁更改构建上下文的情况下。–link选项是BuildKit的一个新功能,可能在不同版本的Docker中有所不同,在使用前需先确认该功能是否可以可用。使用格式如下:
COPY [--link[=<boolean>]] <src> ... <dest>
ADD
ADD指令用来复制文件或目录或远程文件,并将其添加到容器的文件系统中。可以看到ADD指令和COPY指令功能类似,都是用来将文件复制到容器并添加到其文件系统。COPY指令和ADD指令的主要区别在于ADD指令支持自动解压缩和从URL复制文件,而COPY指令则仅支持本地文件的简单复制。根据实际需求,可以选择使用这两个指令中的任何一个。但为了避免不必要的解压缩和复杂性,通常建议优先使用COPY指令。
ADD指令有两种格式:
ADD [OPTIONS] <src> ... <dest>
ADD [OPTIONS] ["<src>", ... "<dest>"]
其中使用双引号的方式,主要应用于路径中存在空格的情况,推荐使用带双引号的方式,这和CMD指令一致。
与CMD指令一致,src指定的源路径可以有多个,且路径支持正则表达式的写法。src指定的源路径是相对于构建上下文所在的路径,而不是相对宿主机。
与CMD指令一致,dest只能有一个,要么是一个绝对路径,要么是一个基于WORKDIR的相对目录。
在使用ADD指令时,要遵循以下规范:
(1) src指定的路径必须在构建上下文中。对于需要访问构建上下文之外的路径将无法执行。如COPY …/path/file因为需要访问构建上下文之外的目录,所以在执行是会报错。
(2) 当src是一个URL,且dest是一个路径,从URL获取的内容将放置到dest的路径下,且访问权限为600。
(3) 当src指定的是目录时,该目录下的所有内容都会拷贝到容器中,包括文件系统的元数据信息。注意,目录本身不会被拷贝。
(4) 当src指定的是一个本地压缩包(格式是gzip、bzip2等),复制到容器后,会将其解压成目录。
(5) 如果dest指定的路径在容器中不存在,则会默认逐层创建。
更多ADD指令介绍可以参考官网ADD一文。接下来重点介绍ADD指令支持的OPTIONS。
–keep-git-dir选项
ADD指令使用–keep-git-dir选项保留.git目录。当基于HTTP或SSH协议从远程Git仓库获取文件或目录时,默认会排除.git目录。如果需要保留.git目录,则可以使用–keep-git-dir选项。示例如下:
ADD --keep-git-dir=true https://github.com/moby/buildkit.git#v0.10.1 /
–checksum选项
ADD指令使用–checksum选项来校验远程资源的checksum,目前只支持HTTP资源。示例如下:
ADD --checksum=sha256:24454f830cdb571e2c4ad15481119c43b3cafd48dd869a9b2945d1036d1dc68d https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/linux-0.01.tar.gz /
其他选项
对于–chown --chmod、–link等选项,其用法与CMD指令一致,有需要的同学可以参考下CMD指令。
USER
USER指令用来指定容器运行时的用户名(或UID)和可选的组名称(或GID)。USER指令会影响后续所有的RUN、CMD和ENTRYPOINT等指令的执行用户。因此,确保所选的UID和GID具有执行这些指令所需的适当权限是很重要的。
Linux使用用户和用户组进行权限分配。对业务应用来说,在设计时要考虑到是否以root用户运行,如果不是,则要考虑设置用户名。如果不指定用户名信息,则默认会使用root用户。因为root权限太高,对业务应用来说,尽量设置一个自己的用户。指令有两种格式:
USER <user>[:<group>]
USER <UID>[:<GID>]
user和group,UID和GID都是成对出现的。如果在设置user时,group缺省,则默认的group是通常是user,除非这个user已经存在,且已分配了一个group。同样的,如果使用USER指令仅设置UID,那么Docker也会为该UID分配一个默认的GID。这个默认的GID通常是与UID相同的值,除非该UID已经存在于/etc/passwd文件中并且有一个与之关联的GID。
设置user和group前,该user必须已经定义。增加user和group的命令如下:
$ useradd -u 1001 myuser,这里-u用来指定uid
$ groupadd -g 1001 mygroup,这里-g用来指定gid
推荐使用UID和GID,理由有两点:(1) 在Linux等操作系统内部识别的还是UID和GID,而不是GID;(2)如果使用user和group,还需保证这个user和group已经存在,而使用UID和GID,如果不存在,会创建一个新的用户。
在设置UID时,要考虑到UID的值大小问题。通常0用来代表系统管理员或root用户,而1-999一般会保留给系统使用。所以推荐业务应用使用1000及以后的值。注意,不同操作系统对值大小的划分可能不同,这里主要考虑Linux系统。
为了确保一致性和可预测性,最好在创建用户时同时指定UID和GID,这样可以避免任何潜在的混淆或冲突。如果确实只想设置UID并接受默认的GID,那么需要确保这符合当前业务的需求。使用示例如下:
USER 1000:1000
更多USER介绍可以参考官网USER一文。
VOLUME
VOLUME指令用来创建一个数据卷挂载点。VOLUME指令可以帮助将宿主机目录或者其他容器目录挂载到这个容器。VOLUME指令主要用在需要数据持久化场景。因为容器自身不能持久化运行时数据,容器重启后,之前容器运行时产生的数据将丢失。指令格式为:
VOLUME ["/data"]
注意,VOLUME指令只是声明了容器中需要挂载到宿主机或其它容器的目录,但是不能保证这个目录与宿主机或其他容器的目录的映射,也不能保证宿主机或其他容器的目录一定存在。正常的使用流程是:在Dockerfile中使用VOLUME指令声明容器需要与外部建立联系的目录,然后在容器启动时(也即执行docker run),指定映射关系(也即在docker run命令中增加-v参数)。将容器的/data目录映射到宿主机的/path/on/host目录的示例如下:
$ docker run -v /path/on/host:/data image-id-or-name
如果执行docker run命令未增加-v参数,则容器内的文件系统和宿主机目录将完成隔离。这意味着容器内部对文件的任何修改(创建、删除、更新)都不会反映到宿主机上,反之亦然。
更多VOLUME指令介绍可以参考官网VOLUME一文。
WORKDIR
WORKDIR指令用来设置工作目录。WORKDIR指令会影响后续所有的RUN、CMD、ENTRYPOINT、COPY、ADD等指令的工作目录。如果不指定WORKDIR,那么默认的工作目录是/,也即根目录。指令格式为:
WORKDIR /path/to/workdir
可以使用多个WORKDIR指令,后续命令如果参数是相对路径,则会基于之前命令指定的的路径,后续指令如果是参数是绝对路径,则会更新工作目录。例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
WORKDIR /e
RUN pwd
则第一个RUN pwd指令的工作目录为/a/b/c,第二个RUN pwd指令的工作目录为/e。为了避免出错,推荐WORKDIR指令中只使用绝对路径。
一般情况下,业务镜像都是基于基础镜像或父镜像构建,如果不设置自己的工作目录,则会集成基础镜像或父镜像的目录。为避免对基础镜像或父镜像产生影响,推荐业务应用设置自己的工作目录。
更多WORKDIR介绍可以参考官网WORKDIR一文。
ONBUILD
ONBUILD指令是Docker中的一个特殊指令,它用于向镜像中添加触发器。这些触发器将在以当前镜像作为基础镜像去构建下一级镜像时执行。换句话说,当某个镜像被用作另一个镜像的构建基础时,ONBUILD指令中指定的指令将在构建过程中被执行。指令格式如下:
ONBUILD <INSTRUCTION>
ONBUILD指令后面可以跟随其他Docker指令,如RUN、COPY等。这些指令在当前镜像构建时并不会被执行,而是在基于当前镜像构建新的镜像时才会被执行。这种机制使得ONBUILD指令非常适用于创建有继承关系的Dockerfile文件,即父镜像在被子镜像继承后,父镜像的ONBUILD指令会被触发。
ONBUILD指令的使用可以减少Dockerfile文件的重复内容编写,因为它允许你在基础镜像中定义一些通用的构建步骤,然后在继承该基础镜像的子镜像中自动执行这些步骤。但需要注意的是,ONBUILD指令只能在构建子镜像时执行,对孙子镜像的构建是无效的。此外,ONBUILD指令不能使FROM或MAINTAINER指令触发,且不允许使用ONBUILD ONBUILD来链接ONBUILD指令。示例如下:
ONBUILD COPY [".", "/var/testapp"]
ONBUILD RUN go build /var/testapp
跟随在ONBUILD后的指令不会在包含它们的Dockerfile被构建时被执行,这些指令会被记录在生成镜像的元数据ContainerConfig.OnBuild下。这个元数据会一直被保留,直到生成的镜像被另外的Dockerfile作为基础镜像,当一个下游的Dockerfile通过FROM指令使用上游的镜像(带有ONBUILD指令的Dockerfile产生的镜像),那么这些在ONBUILD后跟随的指令将会在FROM指令后,下一条指令前被执行。
更多ONBUILD介绍可以参考官网ONBUILD一文。
STOPSIGNAL
STOPSIGNAL指令用来设置容器退出时需要发送的系统调用信号。这个信号可以是一个有效的无符号数字,与内核系统调用表中的位置相匹配,如9,或者是SIG格式的信号量,如SIGKILL。如果未定义,则默认值为 SIGTERM。指令格式如下:
STOPSIGNAL signal
可以在在 docker run 和 docker create 上使用–stop-signal参数指定STOPSIGNAL,来覆盖DockerFile中定义的STOPSIGNAL。
更多STOPSIGNAL指令介绍可以参考官网STOPSIGNAL一文。
HEALTHCHECK
HEALTHCHECK指令用来对容器进行健康检查,确认容器是否正常。指令有两种格式:
HEALTHCHECK [OPTIONS] CMD command, 根据在容器中执行命令的返回值是否为 0 来判断容器是否健康,如果是0,表示健康,否则不健康
HEALTHCHECK NONE, 禁止基础镜像中的健康检查
启用健康检查后,容器会额外新增一个健康状态(health status)。当容器启动后,健康状态是starting;当健康检查通过后,健康状态是healthy;当健康检查失败后,健康状态是unhealthy。
HEALTHCHECK指令的OPTION支持如下参数:
-interval=DURAT工ON (default: 30s):过多久检查一次;
-timeout=DURATION (default: 30s):每次检查等待结果的超时;
–start-period=DURATION(default: 0s):初次检查开始时间,如果容器启动较慢,可以设置该参数,延后健康探测时间
-retries=N (default: 3):如果失败了,重试几次才最终确定失败
每个Dockerfile中,只能有一条HEALTHCHECK指令。如果出现了多个HEALTHCHECK指令,不会报错,但只有最后一条会生效。
更多HEALTHCHECK指令介绍可以参考官网HEALTHCHECK一文。
SHELL
SHELL指令用来设置镜像使用的默认shell。指令格式为:
SHELL ["executable" , "parameters"]
Linux的默认shell是[“/bin/sh”, “-c”],Windows的默认shell是[“cmd”, “/S”, “/C”]。SHELL指令可以出现多次。每条SHELL指令都会覆盖所有先前的SHELL指令,并影响所有后续指令。使用示例如下:
FROM microsoft/windowsservercore## Executed as cmd /S /C echo default
RUN echo default## Executed as cmd /S /C powershell -command Write-Host default
RUN powershell -command Write-Host default## Executed as powershell -command Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello## Executed as cmd /S /C echo hello
SHELL ["cmd", "/S", "/C"]
RUN echo hello
shell的调整会影响到以下三个指令:RUN、CMD 和 ENTRYPOINT。当调整shell时,要充分考虑对以上三个指令的影响。
更多SHELL介绍可以参考官网SHELL一文。
参考
https://yiyan.baidu.com/ 文心一言
https://docs.docker.com/reference/dockerfile/ Dockerfile reference
https://www.runoob.com/docker/docker-dockerfile.html Docker Dockerfile
https://www.cnblogs.com/dance-walter/p/9581508.html Dockerfile语法简介
https://zhuanlan.zhihu.com/p/419175543 Docker Dockerfile指令大全
https://blog.csdn.net/qq_62344659/article/details/131844959 Docker镜像的创建
http://www.dockerinfo.net/3328.html 精简 Docker 镜像
https://www.cnblogs.com/tkuang/p/17219527.html 在 Dockerfile 中以 scratch 为基础镜像 (FROM scratch)
https://blog.csdn.net/jthivan/article/details/50530955 UID详解
https://cn.linux-console.net/?p=20084 Linux 中的 UID 是什么?
https://blog.csdn.net/ximenjianxue/article/details/103127383 Linux用户不同UID分类区别
https://c.biancheng.net/view/3042.html Linux UID和GID(用户ID和组ID)
https://blog.csdn.net/WJSZMD/article/details/89331751 SIGINT,SIGKILL,SIGTERM信号区别
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/ Dockerfile Best Practices guide