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 可以是任何有效的镜像(从公共存储库中提取图像)

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 指令从 复制新文件、目录或远程文件 URL,并将它们添加到路径 处的image文件系统中.且可以指定多个 资源,但如果它们是文件或目录,则它们的路径被解释为相对于构建上下文的源。

包含空格的路径需要后一种形式。

--chown 功能仅在用于构建 Linux 容器的 Dockerfile 上受支持,不适用于 Windows 容器。由于用户和组所有权概念不会在 Linux 和 Windows 之间转换,因此使用 /etc/passwd 和 /etc/group 将用户和组名转换为 ID 限制了此功能仅适用于基于 Linux 操作系统的容器。

每个 可能包含通配符,匹配将使用 Go 的 filepath.Match 规则完成。例如:

添加所有以“hom”开头的文件:

ADD hom* /mydir/

在下面的例子中,?被任何单个字符替换,例如“home.txt”:

ADD hom?.txt /mydir/

是绝对路径,或相对于 WORKDIR 的路径,源将在目标容器内复制到该路径中。 下面的示例使用相对路径,并将“test.txt”添加到 /relativeDir/:

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/

是远程文件 URL 的情况下,目标将具有 600 的权限。如果正在检索的远程文件具有 HTTP Last-Modified 标头,则该标头中的时间戳将用于设置目标上的 mtime文件。但是,与在 ADD 期间处理的任何其他文件一样,在确定文件是否已更改以及是否应更新缓存时,将不包括 mtime。

如果通过 STDIN (docker build - < somefile) 传递 Dockerfile 进行构建,则没有构建上下文,因此 Dockerfile 只能包含基于 URL 的 ADD 指令。

您还可以通过 STDIN 传递压缩存档:(docker build - < archive.tar.gz),存档根目录下的 Dockerfile 和存档的其余部分将用作构建的上下文。

如果您的 URL 文件使用身份验证保护,则需要使用 RUN wget、RUN curl 或使用容器内的其他工具,因为 ADD 指令不支持身份验证。

如果 的内容发生变化,第一个遇到的 ADD 指令将使来自 Dockerfile 的所有后续指令的缓存无效。这包括使 RUN 指令的缓存无效。有关详细信息,请参阅 Dockerfile 最佳实践指南 - 利用构建缓存。

ADD 遵循以下规则:

  • 路径必须在构建的上下文中;您不能添加 ../something /something,因为 docker 构建的第一步是将上下文目录(和子目录)发送到 docker 守护进程。
  • 如果 是一个 URL 并且 不以斜杠结尾,那么文件将从 URL 下载并复制到
  • 如果 是一个 URL 并且 确实以斜杠结尾,则从 URL 推断文件名并将文件下载到 /。例如,添加 http://example.com/foobar / 将创建文件 /foobar。 URL 必须有一个重要的路径,以便在这种情况下可以发现适当的文件名(http://example.com 将不起作用)。
  • 如果 是目录,则复制目录的全部内容,包括文件系统元数据。

不会复制目录本身,只会复制其内容。

如果 是采用可识别压缩格式(identity、gzip、bzip2 或 xz)的本地 tar 存档,则将其解压缩为目录。来自远程 URL 的资源不会被解压缩。当一个目录被复制或解压时,它的行为与 tar -x 相同,结果是:

  1. 目标路径中存在的任何内容和
  2. The contents of the source tree, with conflicts resolved in favor of “2.” on a file-by-file basis.

文件是否被识别为可识别的压缩格式完全取决于文件的内容,而不是文件的名称。例如,如果一个空文件恰好以 .tar.gz 结尾,这将不会被识别为压缩文件,也不会生成任何类型的解压缩错误消息,而只会将该文件复制到目标位置。

  • 如果 是任何其他类型的文件,则将其与其元数据一起单独复制。在这种情况下,如果 以斜杠 / 结尾,它将被视为一个目录,并且 的内容将写入 /base()。

  • 如果直接指定了多个 资源,或者由于使用了通配符,则 必须是一个目录,并且必须以斜杠 / 结尾。

  • 如果 不以斜杠结尾,它将被视为一个普通文件,并且 的内容将写入

  • 如果 不存在,它会与路径中所有缺失的目录一起创建。

练习:
  • 目录结构:目录结构

  • 文件内容:

文件内容

  1. #  复制多个文件到/home/t/ 目录中去
    ADD in_root.txt a/in_a.txt /home/t/ 
    
  2. # a目录中的文件都会被复制到/home/adest目录中去,即使 adest不是以 / 为结尾。
    ADD ./a  /home/adest
    
  3. # in_b.txt会被复制为/home目录下的bdest文件,此时bdest不是以 / 为结尾,所以被推断为一个文件。
    ADD ./b/in_b.txt  /home/bdest
    
  4. # /home/er不是以 / 为结尾,c/in_c.docx 文件的内容被写入到了一个文件 er 中去. 但是格式不是文本,所以 er 文件我没法打开
    ADD in_root.txt c/in_c.docx /home/er
    
  5. # 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 指令从 复制新文件或目录,并将它们添加到路径 处的容器文件系统中。 可以指定多个 资源,但文件和目录的路径将被解释为相对于构建上下文的源。

每个 可能包含通配符,匹配将使用 Go 的 filepath.Match 规则完成。例如:

添加所有以“hom”开头的文件:

COPY hom* /mydir/

在下面的例子中,?被任何单个字符替换,例如“home.txt”

COPY hom?.txt /mydir/

是绝对路径,或相对于 WORKDIR 的路径,源将在目标容器内复制到该路径中。 下面的示例使用相对路径,并将“test.txt”添加到 /relativeDir/

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 收到 SIGTERM。

只有 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 指令都定义了运行容器时执行的命令。很少有规则可以描述他们的合作

  1. Dockerfile 应至少指定 CMD 或 ENTRYPOINT 命令之一。
  2. 将容器用作可执行文件时,应定义 ENTRYPOINT。
  3. CMD 应该用作定义 ENTRYPOINT 命令的默认参数的一种方式或用于在容器中执行临时命令。
  4. 使用替代参数运行容器时,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 = 标志使用 docker build 命令将其传递给构建器。如果用户指定了未在 Dockerfile 中定义的构建参数,则构建会输出警告。

官方文档说会有警告,但是我自己测试时没有看到

一个 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/amd64
  • TARGETOS - 目标平台的操作系统组件
  • 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= ,在这两种情况下,第 2 行的指定都不会导致缓存未命中;第 3 行确实导致缓存未命中。 ARG CONT_IMG_VER 导致 RUN 行被标识为与运行 CONT_IMG_VER= echo hello 相同,因此如果 更改,我们会得到缓存未命中。

考虑同一命令行下的另一个示例:

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"]
posted @ 2023-02-12 20:27  菜阿  阅读(60)  评论(0编辑  收藏  举报