docker-dockerfile
Dockerfile
FROM
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
eg:
FROM centos:centos7
FROM centos:centos7 AS name
FROM 指令初始化一个新的构建阶段并为后续指令设置基础镜像。因此,有效的 Dockerfile 必须以 FROM 指令开头。image 可以是任何有效的镜像(从公共存储库中提取图像)
- ARG 是 Dockerfile 中唯一可能位于 FROM 之前的指令。 See Understand how ARG and FROM interact.
RUN
RUN <command> (shell form)
## the command is run in a shell, which by default is /bin/sh -c on Linux or cmd /S /C on Windows
RUN ["executable", "param1", "param2"] (exec form)
eg:
RUN mkdir /home/f1
RUN ["mkdir","/home/f2"]
RUN [ "sh", "-c", "mkdir /home/f3" ]
RUN 指令将在当前镜像之上的新层中执行任何命令并提交结果。生成的提交镜像将用于 Dockerfile 中的下一步。
RUN 指令的缓存在下一次构建期间不会自动失效。像 RUN apt-get dist-upgrade -y 这样的指令的缓存将在下一次构建期间重用。可以使用 --no-cache 标志使 RUN 指令的缓存失效,例如 docker build --no-cache。当然也可以在后面紧跟ADD或COPY指令使缓存失效 。
注意 exec 形式被解析为 JSON 数组,这意味着您必须在单词周围使用双引号 (“) 而不是单引号 (‘)。
在 JSON 形式中,需要转义反斜杠。这在反斜杠是路径分隔符的 Windows 上尤其重要。由于不是有效的 JSON,以下行将被视为 shell 形式,并以意想不到的方式失败。
RUN ["c:\windows\system32\tasklist.exe"]
正确的方式如下 :
RUN ["c:\\windows\\system32\\tasklist.exe"]
CMD
CMD ["executable","param1","param2"] (exec form, this is the preferred form)
CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
CMD command param1 param2 (shell form)
eg:
CMD ls
CMD [ "/bin/bash", "-c", "cd /home && ls && /bin/bash" ]
一个 Dockerfile 中只能有一个 CMD 指令。如果你列出了多个 CMD,那么只有最后一个 CMD 会生效。
CMD 的主要目的是为正在执行的容器提供默认值。这些默认值可以包含可执行文件,也可以省略可执行文件,在这种情况下,您还必须指定 ENTRYPOINT 指令
不要将 RUN 与 CMD 混淆。 RUN 实际运行一个命令并提交结果; CMD 在构建时不执行任何操作,而是指定镜像执行docker run时的预期命令
LABEL
LABEL <key>=<value> <key>=<value> <key>=<value> ...
eg:
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."
LABEL 指令向图像添加元数据。 LABEL 是键值对。要在 LABEL 值中包含空格,请像在命令行解析中一样使用引号和反斜杠,如上所示。
查询镜像LABLE的命令:
docker image inspect --format='' imagename
MAINTAINER
MAINTAINER <name>
MAINTAINER 指令设置生成图像的作者字段。 LABEL 指令是一个更灵活的版本,您应该改用它,因为它可以设置您需要的任何元数据,并且可以轻松查看,例如使用 docker inspect。要设置与 MAINTAINER 字段相对应的标签,您可以使用
LABEL org.opencontainers.image.authors="SvenDowideit@home.org.au"
EXPOSE
EXPOSE <port> [<port>/<protocol>...]
EXPOSE 指令通知 Docker 容器在运行时监听指定的网络端口。可以指定端口是监听TCP还是UDP,如果不指定协议,默认为TCP。
EXPOSE 指令实际上并不发布端口。它充当构建镜像的人和运行容器的人之间的一种文档,关于打算发布哪些端口。要在运行容器时实际发布端口,请在 docker run 上使用 -p 标志发布和映射一个或多个端口,或使用 -P 标志发布所有暴露的端口并将它们映射到高阶端口。
要同时在 TCP 和 UDP 上公开,请包含两行:
EXPOSE 80/tcp
EXPOSE 80/udp
绑定端口的命令:
## 指定端口
docker run -it -p 80:8088/tcp image
## 随机端口
docker run -it -P image
ENV
ENV <key>=<value> ...
ENV 指令将环境变量
ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy
# 这个注释的上下两段是等价的
ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \
MY_CAT=fluffy
当容器从生成的映像运行时,使用 ENV 设置的环境变量将持续存在。您可以使用 docker inspect 查看值,并使用docker run --env <key>=<value>
更改它们。
如果环境变量只在构建期间需要,而不是在最终映像中,请考虑为单个命令设置一个值
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ...
或者使用 ARG,它不会保留在最终image中
ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y ...
ADD
ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
ADD 指令从
包含空格的路径需要后一种形式。
--chown 功能仅在用于构建 Linux 容器的 Dockerfile 上受支持,不适用于 Windows 容器。由于用户和组所有权概念不会在 Linux 和 Windows 之间转换,因此使用 /etc/passwd 和 /etc/group 将用户和组名转换为 ID 限制了此功能仅适用于基于 Linux 操作系统的容器。
每个
添加所有以“hom”开头的文件:
ADD hom* /mydir/
在下面的例子中,?被任何单个字符替换,例如“home.txt”:
ADD hom?.txt /mydir/
ADD test.txt relativeDir/
而此示例使用绝对路径,并将“test.txt”添加到 /absoluteDir/ :
ADD test.txt /absoluteDir/
添加包含特殊字符(例如 [ 和 ])的文件或目录时,您需要按照 Golang 规则对这些路径进行转义,以防止它们被视为匹配模式。例如,要添加名为 arr[0].txt 的文件,请使用以下命令:
ADD arr[[]0].txt /mydir/
所有新文件和目录都使用 0 的 UID 和 UID 创建,除非可选的 --chown 标志指定给定的用户名、组名或 UID /UID 组合以请求添加内容的特定所有权.--chown 标志的格式允许用户名和组名字符串或直接整数 UID 和 GID 的任意组合。提供不带组名的用户名或不带 GID 的 UID 将使用与 GID 相同的数字 UID。如果提供了用户名或组名,则容器的根文件系统 /etc/passwd 和 /etc/group 文件将分别用于执行从名称到整数 UID 或 GID 的转换。以下示例显示了 --chown 标志的有效定义:
ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/
在
如果通过 STDIN (docker build - < somefile) 传递 Dockerfile 进行构建,则没有构建上下文,因此 Dockerfile 只能包含基于 URL 的 ADD 指令。
您还可以通过 STDIN 传递压缩存档:(docker build - < archive.tar.gz),存档根目录下的 Dockerfile 和存档的其余部分将用作构建的上下文。
如果您的 URL 文件使用身份验证保护,则需要使用 RUN wget、RUN curl 或使用容器内的其他工具,因为 ADD 指令不支持身份验证。
如果
ADD 遵循以下规则:
路径必须在构建的上下文中;您不能添加 ../something /something,因为 docker 构建的第一步是将上下文目录(和子目录)发送到 docker 守护进程。 - 如果
是一个 URL 并且 不以斜杠结尾,那么文件将从 URL 下载并复制到 。 - 如果
是一个 URL 并且 确实以斜杠结尾,则从 URL 推断文件名并将文件下载到 / 。例如,添加 http://example.com/foobar / 将创建文件 /foobar。 URL 必须有一个重要的路径,以便在这种情况下可以发现适当的文件名(http://example.com 将不起作用)。 - 如果
是目录,则复制目录的全部内容,包括文件系统元数据。
不会复制目录本身,只会复制其内容。
如果
- 目标路径中存在的任何内容和
- The contents of the source tree, with conflicts resolved in favor of “2.” on a file-by-file basis.
文件是否被识别为可识别的压缩格式完全取决于文件的内容,而不是文件的名称。例如,如果一个空文件恰好以 .tar.gz 结尾,这将不会被识别为压缩文件,也不会生成任何类型的解压缩错误消息,而只会将该文件复制到目标位置。
-
如果
是任何其他类型的文件,则将其与其元数据一起单独复制。在这种情况下,如果 以斜杠 / 结尾,它将被视为一个目录,并且 的内容将写入 /base( )。 -
如果直接指定了多个
资源,或者由于使用了通配符,则 必须是一个目录,并且必须以斜杠 / 结尾。 -
如果
不以斜杠结尾,它将被视为一个普通文件,并且 的内容将写入 。 -
如果
不存在,它会与路径中所有缺失的目录一起创建。
练习:
-
目录结构:
-
文件内容:
-
# 复制多个文件到/home/t/ 目录中去 ADD in_root.txt a/in_a.txt /home/t/
-
# a目录中的文件都会被复制到/home/adest目录中去,即使 adest不是以 / 为结尾。 ADD ./a /home/adest
-
# in_b.txt会被复制为/home目录下的bdest文件,此时bdest不是以 / 为结尾,所以被推断为一个文件。 ADD ./b/in_b.txt /home/bdest
-
# /home/er不是以 / 为结尾,c/in_c.docx 文件的内容被写入到了一个文件 er 中去. 但是格式不是文本,所以 er 文件我没法打开 ADD in_root.txt c/in_c.docx /home/er
-
# er 文件中只有 in_b.txt 的内容,说明文件内容不是追加写入的,而是覆盖写入。 ADD in_root.txt b/in_b.txt /home/er
COPY
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
COPY 指令从
每个
添加所有以“hom”开头的文件:
COPY hom* /mydir/
在下面的例子中,?被任何单个字符替换,例如“home.txt”
COPY hom?.txt /mydir/
COPY test.txt relativeDir/
而此示例使用绝对路径,并将“test.txt”添加到 /absoluteDir/
COPY test.txt /absoluteDir/
在复制包含特殊字符(例如 [ 和 ])的文件或目录时,您需要按照 Golang 规则对这些路径进行转义,以防止它们被视为匹配模式。例如,要复制名为 arr[0].txt 的文件,请使用以下命令;
COPY arr[[]0].txt /mydir/
所有新文件和目录都使用 0 的 UID 和 GID 创建,除非可选的 --chown 标志指定给定的用户名、组名或 UID/GID 组合以请求复制内容的特定所有权。 --chown 标志的格式允许用户名和组名字符串或直接整数 UID 和 GID 的任意组合。提供不带组名的用户名或不带 GID 的 UID 将使用与 GID 相同的数字 UID。如果提供了用户名或组名,则容器的根文件系统 /etc/passwd 和 /etc/group 文件将分别用于执行从名称到整数 UID 或 GID 的转换。以下示例显示了 --chown 标志的有效定义:
COPY --chown=55:mygroup files* /somedir/
COPY --chown=bin files* /somedir/
COPY --chown=1 files* /somedir/
COPY --chown=10:11 files* /somedir/
如果容器根文件系统不包含 /etc/passwd 或 /etc/group 文件,并且在 --chown 标志中使用了用户名或组名,则构建将在 COPY 操作中失败。使用数字 ID 不需要查找,也不依赖于容器根文件系统内容。
如果使用 STDIN (docker build - < somefile) 构建,则没有构建上下文,因此无法使用 COPY。
和ADD的区别:
COPY 可以选择接受一个标志 --from=
,该标志可用于将源位置设置为先前的构建阶段(使用 FROM .. AS 创建),该阶段将用于代替用户发送的构建上下文.如果无法找到具有指定名称的构建阶段,则会尝试使用具有相同名称的image。
COPY 遵循以下规则:
路径必须在构建的上下文中;您不能 COPY ../something /something,因为 docker 构建的第一步是将上下文目录(和子目录)发送到 docker 守护进程 - 如果
是目录,则复制目录的全部内容,包括文件系统元数据。 - 如果
是任何其他类型的文件,则将其与其元数据一起单独复制。在这种情况下,如果 以斜杠 / 结尾,它将被视为一个目录,并且 的内容将写入 /base( )。 - 如果直接指定了多个
资源,或者由于使用了通配符,则 必须是一个目录,并且必须以斜杠 / 结尾。 - 如果
不以斜杠结尾,它将被视为常规文件,并且 的内容将写入 。 - 如果
不存在,它会与路径中所有缺失的目录一起创建。
如果
的内容发生变化,第一个遇到的 COPY 指令将使来自 Dockerfile 的所有后续指令的缓存无效。这包括使 RUN 指令的缓存无效。有关详细信息,请参阅 Dockerfile 最佳实践指南 - 利用构建缓存。
ENTRYPOINT
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2
ENTRYPOINT 允许您配置将作为可执行文件运行的容器。
例如,以下内容以默认内容启动 nginx,侦听端口 80:
docker run -i -t --rm -p 80:80 nginx
docker run 的命令行参数将附加在 exec 形式的 ENTRYPOINT 中的所有元素之后,并将覆盖使用 CMD 指定的所有元素。这允许将参数传递给入口点,即 docker run
-d 会将 -d 参数传递给入口点。您可以使用 docker run --entrypoint 标志覆盖ENTRYPOINT 指令。
shell 形式可防止使用任何 CMD 或运行命令行参数,但缺点是您的 ENTRYPOINT 将作为 /bin/sh -c 的子命令启动,它不传递信号。这意味着可执行文件不会是容器的 PID 1 - 并且不会接收 Unix 信号 - 因此您的可执行文件不会从 docker stop
只有 Dockerfile 中的最后一条 ENTRYPOINT 指令会起作用。
Exec 形式的 ENTRYPOINT 示例:
您可以使用 ENTRYPOINT 的 exec 形式来设置相当稳定的默认命令和参数,然后使用任一形式的 CMD 来设置更可能更改的其他默认值。
FROM centos:centos7
# 容器运行后会执行这个命令
ENTRYPOINT ["top", "-b"]
# 这个会作为参数拼接到 top -b 后面
CMD ["-c"]
当你运行容器时,你可以看到 top 是唯一的进程:
PS D:\DOCKER> docker run -it --name containerName image -H
top - 08:00:24 up 1:46, 0 users, load average: 0.00, 0.00, 0.00
Threads: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 26072752 total, 24065128 free, 769484 used, 1238140 buff/cache
KiB Swap: 7340032 total, 7340032 free, 0 used. 24589552 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 56064 3656 3216 R 0.0 0.0 0:00.02 top
Shell 形式的 ENTRYPOINT 示例:
您可以为 ENTRYPOINT 指定一个纯字符串,它将在 /bin/sh -c 中执行。此表单将使用 shell 处理来替换 shell 环境变量,并将忽略任何 CMD 或 docker run 命令行参数。为了确保 docker stop 能够正确地向任何长时间运行的 ENTRYPOINT 可执行文件发出信号,您需要记住用 exec 启动它
FROM centos:centos7
ENTRYPOINT exec top -b
运行此映像时,您将看到单个 PID 1 进程:
docker run -it --name test image
top - 08:57:36 up 2:43, 0 users, load average: 0.00, 0.00, 0.00
Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 26072752 total, 24062656 free, 771740 used, 1238356 buff/cache
KiB Swap: 7340032 total, 7340032 free, 0 used. 24587320 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 56064 3524 3088 R 0.0 0.0 0:00.02 top
Understand how CMD and ENTRYPOINT interact
CMD 和 ENTRYPOINT 指令都定义了运行容器时执行的命令。很少有规则可以描述他们的合作
- Dockerfile 应至少指定 CMD 或 ENTRYPOINT 命令之一。
- 将容器用作可执行文件时,应定义 ENTRYPOINT。
- CMD 应该用作定义 ENTRYPOINT 命令的默认参数的一种方式或用于在容器中执行临时命令。
- 使用替代参数运行容器时,CMD 将被覆盖
下表显示了针对不同的 ENTRYPOINT / CMD 组合执行的命令:
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 [“p1_cmd”, “p2_cmd”] | p1_cmd p2_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry p1_cmd p2_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 |
如果 CMD 是从基础image定义的,则设置 ENTRYPOINT 会将 CMD 重置为空值。在这种情况下,必须在当前image中定义 CMD 才能具有值。
VOLUME
VOLUME ["/data"]
VOLUME 指令创建一个具有指定名称的挂载点,并将其标记为保存来自本机主机或其他容器的外部挂载卷。该值可以是 JSON 数组、VOLUME ["/var/log/"] 或带有多个参数的纯字符串,例如 VOLUME /var/log 或 VOLUME /var/log /var/db。有关通过 Docker 客户端的更多信息/示例和安装说明,请参阅卷共享目录文档。
docker run 命令使用基础映像中指定位置存在的任何数据初始化新创建的卷。例如,考虑以下 Dockerfile 片段:
FROM centos:centos7
RUN mkdir /myvol
RUN "hello world" > /myvol/greeting
VOLUME /myvol
此 Dockerfile 生成的映像会导致 docker run 在 /myvol 创建新的挂载点 且将/myvol/greeting文件复制到新创建的卷中。
Notes about specifying volumes
-
基于 Windows 的容器上的卷:使用基于 Windows 的容器时,容器内卷的目标必须是以下之一: 一个不存在或空的目录, C 以外的驱动器。
-
从 Dockerfile 中更改卷:如果任何构建步骤在声明卷后更改了卷中的数据,则这些更改将被丢弃
-
JSON 格式:列表被解析为 JSON 数组。您必须用双引号 (") 而不是单引号 (') 将单词括起来。
-
主机目录在容器运行时声明:主机目录(挂载点)本质上是依赖于主机的。这是为了保持图像的可移植性,因为不能保证给定的主机目录在所有主机上都可用。因此,您无法从 Dockerfile 中挂载主机目录。 VOLUME 指令不支持指定主机目录参数。您必须在创建或运行容器时指定挂载点。
USER?
USER <user>[:<group>]
USER <UID>[:<GID>]
USER 指令设置用户名(或 UID)和可选的用户组(或 GID)以在运行镜像时使用,以及在 Dockerfile 中跟随它的任何 RUN、CMD 和 ENTRYPOINT 指令。
当用户没有主组时,镜像(或下一个指令)将与根组一起运行。 在 Windows 上,如果用户不是内置帐户,则必须先创建该用户。这可以通过作为 Dockerfile 的一部分调用的 net user 命令来完成。
WORKDIR
WORKDIR /path/to/workdir
WORKDIR 指令为 Dockerfile 中跟随它的任何 RUN、CMD、ENTRYPOINT、COPY 和 ADD 指令设置工作目录。如果 WORKDIR 不存在,即使它没有在任何后续 Dockerfile 指令中使用,它也会被创建。
WORKDIR 指令可以在 Dockerfile 中多次使用。如果提供了相对路径,它将相对于前一个 WORKDIR 指令的路径。例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
## 此 Dockerfile 中最终 pwd 命令的输出将是 /a/b/c .
WORKDIR 指令可以解析先前使用 ENV 设置的环境变量。您只能使用在 Dockerfile 中显式设置的环境变量。例如:
ENV DIRPATH=/path
WORKDIRLearn more about the "WORKDIR" Dockerfile command. $DIRPATH/$DIRNAME
RUN pwd
## 此 Dockerfile 中最终 pwd 命令的输出将是 /path/$DIRNAME.
ARG
ARG <name>[=<default value>]
ARG 指令定义了一个变量,用户可以在构建时使用 --build-arg
官方文档说会有警告,但是我自己测试时没有看到
一个 Dockerfile 可能包含一个或多个 ARG 指令。例如,以下是一个有效的 Dockerfile:
FROM centos:centos7
ARG user1
ARG buildno
不建议使用构建时变量来传递秘密,如 github 密钥、用户凭据等。使用 docker history 命令,任何图像用户都可以看到构建时变量值。 请参阅“使用 BuildKit 构建镜像”部分,了解在构建镜像时使用机密的安全方法
- Default values
ARG 指令可以选择包含默认值:
FROM centos:centos7
ARG user1=someuser
ARG buildno=1
如果 ARG 指令具有默认值并且在构建时没有传递任何值,则构建器将使用默认值。
- Scope
ARG 变量定义从它在 Dockerfile 中定义的行开始生效,而不是从参数在命令行或其他地方的使用中生效。例如,考虑这个 Dockerfile:
FROM centos:centos7
USER ${user:-some_user}
ARG user
USER $user
用户通过调用构建此文件:
docker build --build-arg user=what_user .
第 2 行的 USER 计算为 some_user,因为在随后的第 3 行定义了用户变量。第 4 行的 USER 计算为 what_user,因为定义了用户并且 what_user 值在命令行上传递。在由 ARG 指令定义之前,对变量的任何使用都会导致空字符串。
ARG 指令在定义它的构建阶段结束时超出范围。要在多个阶段使用 arg,每个阶段都必须包含 ARG 指令。
FROM centos:centos7
ARG SETTINGS
RUN ./run/setup $SETTINGS
FROM centos:centos7
ARG SETTINGS
RUN ./run/other $SETTINGS
Using ARG variables
您可以使用 ARG 或 ENV 指令来指定可用于 RUN 指令的变量。使用 ENV 指令定义的环境变量总是覆盖同名的 ARG 指令。考虑这个带有 ENV 和 ARG 指令的 Dockerfile。
FROM centos:centos7
ARG CONT_IMG_VER
ENV CONT_IMG_VER=v1.0.0
RUN echo $CONT_IMG_VER
然后,假设此镜像是使用以下命令构建的:
docker build --build-arg CONT_IMG_VER=v2.0.1 .
在这种情况下,RUN 指令使用 v1.0.0 而不是用户传递的 ARG 设置:v2.0.1 此行为类似于 shell 脚本,其中本地范围的变量覆盖作为参数传递或从环境继承的变量,从其定义点。
使用上面的示例但使用不同的 ENV 规范,您可以在 ARG 和 ENV 指令之间创建更有用的交互:
FROM centos:centos7
ARG CONT_IMG_VER
ENV CONT_IMG_VER=${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER
与 ARG 指令不同,ENV 值始终保留在构建的映像中。考虑一个没有 --build-arg 标志的 docker 构建:
docker build .
使用这个 Dockerfile 示例,CONT_IMG_VER 仍然保留在映像中,但它的值将是 v1.0.0,因为它是 ENV 指令在第 3 行中设置的默认值。
本示例中的变量扩展技术允许您从命令行传递参数,并通过利用 ENV 指令将它们保留在最终图像中。变量扩展仅支持有限的 Dockerfile 指令集。
Predefined ARGs
Docker 有一组预定义的 ARG 变量,您无需在 Dockerfile 中使用相应的 ARG 指令即可使用这些变量。
HTTP_PROXY
http_proxy
HTTPS_PROXY
https_proxy
FTP_PROXY
ftp_proxy
NO_PROXY
no_proxy
要使用这些,请使用 --build-arg 标志在命令行上传递它们,例如:
docker build --build-arg HTTPS_PROXY=https://my-proxy.example.com .
默认情况下,这些预定义变量被排除在 docker 历史输出之外。排除它们会降低在 HTTP_PROXY 变量中意外泄露敏感身份验证信息的风险。例如,考虑使用 --build-arg HTTP_PROXY=http://user:pass@proxy.lon.example.com 构建以下 Dockerfile:
FROM centos:centos7
RUN echo "Hello World"
在这种情况下,HTTP_PROXY 变量的值在 docker 历史记录中不可用,也不会被缓存。如果您要更改位置,并且您的代理服务器更改为 http://user:pass@proxy.sfo.example.com,则后续构建不会导致缓存未命中。
如果您需要覆盖此行为,则可以通过在 Dockerfile 中添加 ARG 语句来实现,如下所示:
FROM centos:centos7
ARG HTTP_PROXY
RUN echo "Hello World"
构建此 Dockerfile 时,HTTP_PROXY 会保留在 docker 历史记录中,更改其值会使构建缓存失效。
Automatic platform ARGs in the global scope
此功能仅在使用 BuildKit 后端时可用。
Docker 预定义了一组 ARG 变量,其中包含有关执行构建的节点的平台(构建平台)和生成的镜像的平台(目标平台)的信息。可以在 docker build 上使用 --platform 标志指定目标平台。
以下 ARG 变量是自动设置的:
TARGETPLATFORM
- 构建结果的平台。例如 linux/amd64、linux/arm/v7、windows/amd64TARGETOS
- 目标平台的操作系统组件TARGETARCH
-目标平台的架构组件TARGETVARIANT
- 目标平台的变体成分BUILDPLATFORM
-执行构建的节点的平台BUILDOS
- 构建平台操作系统组件BUILDARCH
-构建平台的架构组件BUILDVARIANT
-构建平台的变体成分
这些参数在全局范围内定义,因此不会在构建阶段或您的 RUN 命令中自动可用。你可以在构建阶段公开这些参数之一,但是重新定义它没有价值。
例如:
FROM centos:centos7
ARG TARGETPLATFORM
RUN echo "I'm building for $TARGETPLATFORM"
Impact on build caching (对构建缓存的影响)
ARG 变量不会像 ENV 变量那样持久化到构建的映像中。但是,ARG 变量确实以类似的方式影响构建缓存。如果 Dockerfile 定义了一个 ARG 变量,其值与之前的构建不同,那么“缓存未命中”发生在它第一次使用时,而不是它的定义。特别是,ARG 指令之后的所有 RUN 指令都隐式使用 ARG 变量(作为环境变量),因此可能导致缓存未命中。除非 Dockerfile 中有匹配的 ARG 语句,否则所有预定义的 ARG 变量都免于缓存。
例如,考虑这两个 Dockerfile:
FROM centos:centos7
ARG CONT_IMG_VER
RUN echo $CONT_IMG_VER
FROM centos:centos7
ARG CONT_IMG_VER
RUN echo hello
如果在命令行中指定 --build-arg CONT_IMG_VER=
考虑同一命令行下的另一个示例:
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=$CONT_IMG_VER
RUN echo $CONT_IMG_VER
在本例中,缓存未命中发生在第 3 行。 未命中是因为 ENV 中变量的值引用了 ARG 变量,并且该变量通过命令行更改。在本例中,ENV 命令使镜像包含该值。
如果 ENV 指令覆盖了同名的 ARG 指令,就像这个 Dockerfile:
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=hello
RUN echo $CONT_IMG_VER
第 3 行不会导致缓存未命中,因为 CONT_IMG_VER 的值是一个常量 (hello)。因此,在 RUN(第 4 行)上使用的环境变量和值在构建之间不会改变。
ONBUILD
STOPSIGNAL
HEALTCHECK
SHELL
完整的Dockerfile示例:
FROM centos:centos7
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
# 可以在build时使用--build-arg noDefault=Value传入参数
ARG noDefault
ARG homedir=/docker
# 添加一个docker用户 后续都使用命令都使用这个用户执行 -d 指定用户目录
RUN useradd -d ${homedir} docker
USER docker
RUN echo $noDefault >> $homedir/log
ENV HOME_DIR=${homedir:-/home}
## 工作目录为 /home
WORKDIR /home
RUN echo `pwd` >> $HOME_DIR/log
## 工作目录为 /home/user
WORKDIR user
RUN echo `pwd` >> $HOME_DIR/log
## 工作目录为 $HOME_DIR 这个变量
WORKDIR $HOME_DIR
RUN echo `pwd` >> $HOME_DIR/log
RUN mkdir $homedir/f1
# 这里没有引用到homedir的变量值,实际创建的目录在 WORKDIR下 /${homedir}/f2
RUN ["mkdir","-p","${homedir}/f2"]
# 这里正确引用到了值
RUN [ "sh", "-c", "mkdir ${homedir}/f3" ]
ADD c/in_c.docx in_root.txt ${homedir}/add/
COPY c/in_c.docx in_root.txt ${homedir}/copy/
EXPOSE 80/tcp
EXPOSE 80/udp
#这里不能使用ARG定义的变量,因为它只会在build时起作用,镜像构建好了后ARG变量不会保留
ENTRYPOINT ["sh","-c","cat ${HOME_DIR}/log && /bin/bash"]