Dockerfile参考

Docker可以通过阅读Dockerfile的指令来自动构建Docker映像。Dockerfile是一个文本文档,其中包含用户可以在命令行上调用以组合图像的所有命令。使用docker build 用户可以创建自动执行的构建,该构建可以连续执行多个命令行指令。

本页描述您可以在Dockerfile中使用的命令。阅读完此页面后,请参考Dockerfile最佳实践以获取有关技巧的指南。

Usage

docker build命令从一个Dockerfile和一个context构建图像。构建的上下文是指定位置的PATHURL的文件集。PATH是本地文件系统上的目录。URL是一个Git仓库的位置。

上下文是递归处理的。因此, PATH包括任何子目录,并且URL包括存储库及其子模块。此示例显示了一个使用当前目录作为上下文的构建命令:

$ docker build .
Sending build context to Docker daemon  6.51 MB
...

该构建由Docker守护程序运行,而不是由CLI运行。构建过程要做的第一件事是将整个上下文(递归)发送到守护程序。在大多数情况下,最好以空目录作为上下文,并将Dockerfile保留在该目录中。仅添加构建Dockerfile所需的文件。

警告:不要用你的根目录下,/作为PATH因为它会导致生成到您的硬盘驱动器的全部内容传输到Docker守护进程。

要在构建上下文中使用文件,Dockerfile引用指令中指定的文件,例如COPY指令。要提高构建的性能,请通过将.dockerignore文件添加到上下文目录来排除文件和目录。有关如何创建.dockerignore 文件的信息,请参阅此页面上的文档。

传统上,Dockerfile称为Dockerfile,并且位于上下文的根目录中。您可以使用docker build -f标志指向文件系统中任何位置的Dockerfile。

$ docker build -f /path/to/a/Dockerfile .

如果构建成功,则可以指定存储新映像的存储库和标记:

$ docker build -t shykes/myapp .

要在构建后将映像标记到多个存储库中,请在运行build命令时添加多个-t参数:

$ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .

在Docker守护程序运行Dockerfile中的指令之前,它会对其进行初步验证,Dockerfile如果语法不正确,则返回错误:

$ docker build -t test/myapp .
Sending build context to Docker daemon 2.048 kB
Error response from daemon: Unknown instruction: RUNCMD

Docker守护程序以一对一的方式运行Dockerfile指令,如有必要,将每个指令的结果提交到新映像,然后最终输出新映像的ID。Docker守护程序将自动清理您发送的上下文。

请注意,每条指令都是独立运行的,并会导致创建新的映像-因此RUN cd /tmp对下一条指令不会有任何影响。

Docker将尽可能重用中间映像(缓存),以docker build显着加速该过程。这由Using cache控制台输出中的消息指示。(有关详细信息,请参阅Dockerfile最佳实践指南

$ docker build -t svendowideit/ambassador .
Sending build context to Docker daemon 15.36 kB
Step 1/4 : FROM alpine:3.2
 ---> 31f630c65071
Step 2/4 : MAINTAINER SvenDowideit@home.org.au
 ---> Using cache
 ---> 2a1c91448f5f
Step 3/4 : RUN apk update &&      apk add socat &&        rm -r /var/cache/
 ---> Using cache
 ---> 21ed6e7fbb73
Step 4/4 : CMD env | grep _TCP= | (sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/' && echo wait) | sh
 ---> Using cache
 ---> 7ea8aef582cc
Successfully built 7ea8aef582cc

构建缓存仅用于具有本地父链的映像。这意味着这些图像是由以前的版本创建的,或者整个图像链都已由docker load加载。如果您希望使用特定映像的构建缓存,则可以使用--cache-from选项指定它。指定带有的图像 --cache-from不需要具有父链,并且可以从其他注册表中提取。

完成构建后,就可以考虑将存储库推送到其注册表

BuildKit

从版本18.09开始,Docker支持由moby / buildkit 项目提供的用于执行构建的新后端。与旧的实现相比,BuildKit后端提供了许多好处。例如,BuildKit可以:

  • 检测并跳过执行未使用的构建阶段
  • 并行构建独立构建阶段
  • 两次构建之间仅增量传输构建上下文中的更改文件
  • 在构建上下文中检测并跳过传输未使用的文件
  • 使用具有许多新功能的外部Dockerfile实现
  • 避免其他API的副作用(中间图像和容器)
  • 优先考虑构建缓存以进行自动修剪

要使用BuildKit后端,您需要DOCKER_BUILDKIT=1在CLI上设置环境变量 ,然后再调用docker build

要了解基于BuildKit的构建可用的实验性Dockerfile语法,请参阅BuildKit存储库中的文档

Format

这是的格式Dockerfile

# Comment
INSTRUCTION arguments

该指令不区分大小写。但是,约定是大写,以便更轻松地将它们与参数区分开。

Docker Dockerfile按顺序运行指令。一个Dockerfile 必须用`FROM`指令开始。这可能在解析器指令注释和全局范围的 ARG之后。该FROM指令指定要从父图像中构建。FROM 只能在一个或多个ARG指令之前,这些指令声明在中的FROM行中使用的参数Dockerfile

Docker将以#开头的行作为注释,除非该行是一个有效的解析器指令行中其他任何地方的#标记均被视为参数。这允许如下语句:

# Comment
RUN echo 'we are running some # of cool things'

注释中不支持连续行字符。

Parser directives

解析器指令是可选的,并且会影响Dockerfile中后续行的处理方式。解析器指令不会在构建中添加图层,也不会显示为构建步骤。解析器指令以形式写为特殊类型的注释# directive=value。单个指令只能使用一次。

一旦处理完注释,空行或构建 指令后,Docker不再寻找解析器指令。而是将格式为解析器指令的任何内容都视为注释,并且不会尝试验证它是否可能是解析器指令。因此,所有解析器指令必须位于的最顶部Dockerfile

解析器指令不区分大小写。但是,约定是小写的。约定还应在任何解析器指令之后包含一个空白行。解析器指令不支持行继续符。

由于这些规则,以下示例均无效:

由于行继续而无效:

# direc \
tive=value

由于出现两次而无效:

# directive=value1
# directive=value2

FROM ImageName

由于出现在构建器指令之后而被视为注释:

FROM ImageName
# directive=value

由于出现在不是解析器指令的注释之后,因此被视为注释:

# About my dockerfile
# directive=value
FROM ImageName

由于未被识别,未知指令被视为注释。另外,由于在不是解析器指令的注释之后出现,所以已知指令被视为注释。

# unknowndirective=value
# knowndirective=value

解析器指令中允许非换行空格。因此,以下各行都被相同地对待:

#directive=value
# directive =value
#	directive= value
# directive = value
#	  dIrEcTiVe=value

支持以下解析器指令:

  • syntax
  • escape

syntax

# syntax=[remote image reference]

例如:

# syntax=docker/dockerfile
# syntax=docker/dockerfile:1.0
# syntax=docker.io/docker/dockerfile:1
# syntax=docker/dockerfile:1.0.0-experimental
# syntax=example.com/user/repo:tag@sha256:abcdef...

仅当使用BuildKit后端时才启用此功能。

语法指令定义了用于构建当前Dockerfile的Dockerfile构建器的位置。BuildKit后端允许无缝使用构建器的外部实现,这些构建器作为Docker映像分发并在容器沙箱环境中执行。

自定义Dockerfile实现使您能够:

  • 自动获取错误修正,而无需更新守护程序
  • 确保所有用户都使用相同的实现来构建您的Dockerfile
  • 使用最新功能而无需更新守护程序
  • 试用新的实验性或第三方功能

Official releases

Docker分发映像的正式版本,这些映像可用于在Docker Hub上的docker/dockerfile存储库下构建Dockerfile 。有两个发布新图像的渠道:稳定版和实验版。

稳定的通道遵循语义版本控制。例如:

  • docker / dockerfile:1.0.0-仅允许不可变版本1.0.0
  • docker / dockerfile:1.0-允许版本1.0.*
  • docker / dockerfile:1-允许版本1..
  • docker / dockerfile:latest-稳定通道上的最新版本

在发布之时,实验频道使用稳定版本中主要和次要组件的增量版本控制。例如:

  • docker / dockerfile:1.0.1-experimental-仅允许不可变版本1.0.1-experimental
  • docker / dockerfile:1.0-experimental-1.0之后的最新实验版本
  • docker / dockerfile:experimental-实验频道上的最新版本

您应该选择最适合自己需求的渠道。如果您只想修正错误,则应使用docker/dockerfile:1.0.如果您想从实验功能中受益,则应使用实验频道。如果您正在使用实验性频道,则较新的版本可能无法向后兼容,因此建议使用不可变的完整版本。

有关主版本和每晚发布的功能,请参考源存储库中的描述。

escape

# escape=\ (backslash)

# escape=` (backtick)

escape指令设置Dockerfile用来转义的字符。如果未指定,则默认转义字符为\

转义字符用于转义一行中的字符和转义换行符。这允许一条Dockerfile指令跨越多行。请注意,无论Dockerfile中是否包含escape解析器指令,都不会在RUN命令中执行转义,除非在行末。

在 Windows中将转义符设置`为尤其有用,其中\为目录路径分隔符。`Windows PowerShell一致。

考虑以下示例,该示例在Windows上将以非显而易见的方式失败。第二行末尾的第二个\将被解释为换行符的转义符,而不是第一行\的转义目标。同样,假设第三行末尾的\实际上是作为指令处理的,则将其视为行的延续。此dockerfile的结果是第二和第三行被视为一条指令:

FROM microsoft/nanoserver
COPY testfile.txt c:\\
RUN dir c:\

结果是:

PS C:\John> docker build -t cmd .
Sending build context to Docker daemon 3.072 kB
Step 1/2 : FROM microsoft/nanoserver
 ---> 22738ff49c6d
Step 2/2 : COPY testfile.txt c:\RUN dir c:
GetFileAttributesEx c:RUN: The system cannot find the file specified.
PS C:\John>

上述解决方案之一是将/用作COPY指令和dir的目标。但是,此语法充其量是令人困惑的,因为Windows上的路径并不自然,而最糟糕的是,由于Windows上的所有命令都不支持/作为路径分隔符,因此容易出错。

通过添加转义解析器指令,以下Dockerfile可以通过在Windows上对文件路径使用自然平台语义来成功完成:

# escape=`

FROM microsoft/nanoserver
COPY testfile.txt c:\
RUN dir c:\

结果是:

PS C:\John> docker build -t succeeds --no-cache=true .
Sending build context to Docker daemon 3.072 kB
Step 1/3 : FROM microsoft/nanoserver
 ---> 22738ff49c6d
Step 2/3 : COPY testfile.txt c:\
 ---> 96655de338de
Removing intermediate container 4db9acbb1682
Step 3/3 : RUN dir c:\
 ---> Running in a2c157f842f5
 Volume in drive C has no label.
 Volume Serial Number is 7E6D-E0F7

 Directory of c:\

10/05/2016  05:04 PM             1,894 License.txt
10/05/2016  02:22 PM    <DIR>          Program Files
10/05/2016  02:14 PM    <DIR>          Program Files (x86)
10/28/2016  11:18 AM                62 testfile.txt
10/28/2016  11:20 AM    <DIR>          Users
10/28/2016  11:20 AM    <DIR>          Windows
           2 File(s)          1,956 bytes
           4 Dir(s)  21,259,096,064 bytes free
 ---> 01c7f3bef04f
Removing intermediate container a2c157f842f5
Successfully built 01c7f3bef04f
PS C:\John>

Environment replacement

环境变量(用ENV语句声明)也可以在某些指令中用作Dockerfile解释的变量。转义也可以通过在字面上将类似变量的语法包含到语句中来处理。

环境变量Dockerfile用 $variable_name${variable_name}表示。它们被同等对待,并且大括号语法通常用于解决变量名不带空格的问题,例如${foo}_bar

${variable_name}语法还支持bash 以下指定的一些标准修饰符:

  • ${variable:-word}表示如果variable设置,则结果将是该值。如果variable未设置,则为word结果。
  • ${variable:+word}表示如果variable设置了 ,那么结果将是word,否则结果是空字符串。

在所有情况下,word都可以是任何字符串,包括其他环境变量。

通过在变量前添加\可以进行转义:例如,\ $ foo或\ $ {foo}将分别转换为$ foo和$ {foo}文字。

示例(解析的表示形式显示在之后#):

FROM busybox
ENV foo /bar
WORKDIR ${foo}   # WORKDIR /bar
ADD . $foo       # ADD . /bar
COPY \$foo /quux # COPY $foo /quux

以下变量中的指令列表支持环境变量Dockerfile

  • ADD
  • COPY
  • ENV
  • EXPOSE
  • FROM
  • LABEL
  • STOPSIGNAL
  • USER
  • VOLUME
  • WORKDIR

以及:

  • ONBUILD (当与以上支持的说明之一结合使用时)

注意:在1.4之前,ONBUILD指令不支持环境变量,即使与上面列出的任何指令结合使用也是如此。

在整个指令中,环境变量替换将对每个变量使用相同的值。换句话说,在此示例中:

ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc

将导致def的值为hello,而不是bye。但是,ghi的值将为bye,因为它不是将abc设置为bye的同一指令的一部分。

.dockerignore文件

在Docker CLI将上下文发送到Docker守护程序之前,它将在上下文的根目录中查找名为.dockerignore的文件。如果此文件存在,则CLI会修改上下文以排除与其中的模式匹配的文件和目录。这有助于避免不必要地将较大或敏感的文件和目录发送到守护程序,并避免使用ADD或COPY将它们添加到映像中。

CLI将.dockerignore文件解释为以换行符分隔的模式列表,类似于Unix shell的文件组。为了匹配,上下文的根被认为是工作目录和根目录。例如,模式 /foo/barfoo/bar都是排除PATH目录或位于Git仓库的URL根下的foo子目录中的命名为bar 的文件或子目录。两者都不排除其他任何东西。

如果.dockerignore文件中的行以第1列中的#开头,则该行将被视为注释,并且在CLI解释之前将被忽略。

这是一个示例.dockerignore文件:

# comment
*/temp*
*/*/temp*
temp?

此文件导致以下生成行为:

规则行为
# comment忽略了。
*/temp*排除名称以temp根的任何直接子目录开头的文件和目录。例如,/somedir/temporary.txt排除纯文件,排除目录/somedir/temp
*/*/temp*排除temp从根以下两级的任何子目录开始的文件和目录。例如,/somedir/subdir/temporary.txt被排除。
temp?排除根目录中名称为的一个字符扩展名的文件和目录temp。例如,/tempa/tempb被排除。

匹配是通过Go的filepath.Match规则完成的。预处理步骤将删除前导和尾随空格并消除.和..元素使用Go的filepath.Clean。预处理后空白的行将被忽略。

除了Go的filepath.Match规则之外,Docker还支持特殊的通配符字符串**,该字符串可以匹配任意数量的目录(包括零个)。例如,** / *.go将排除在所有目录(包括构建上下文的根目录)中找到的所有以.go结尾的文件。

以!开头的行(感叹号)可用于排除例外。以下是使用此机制的示例.dockerignore文件:

*.md
!README.md

 README.md上下文,所有md文件都排除在外。

!异常规则的位置会影响行为:.dockerignore与特定文件匹配的的最后一行确定是包含还是排除该文件。考虑以下示例:

    *.md
    !README*.md
    README-secret.md

包括除了README-secret.md以外的README文件,并且上下文中不包括其他的markdown文件。

现在考虑以下示例:

    *.md
    README-secret.md
    !README*.md

包括所有README文件。中间行没有任何作用,因为!README*.md匹配README-secret.md并排在最后。

您甚至可以使用该.dockerignore文件排除Dockerfile 和.dockerignore文件。这些文件仍被发送到守护程序,因为它需要它们来完成其工作。但是ADDCOPY指令不会将它们复制到映像中。

最后,您可能需要指定要包含在上下文中的文件,而不是要排除的文件。为此,请指定*为第一个模式,然后指定一个或多个!异常模式。

注意:由于历史原因,该.模式将被忽略。

From

FROM [--platform=<platform>] <image> [AS <name>]

FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]

FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

FROM指令初始化一个新的构建阶段,并为后续指令设置 基本映像。因此,有效的Dockerfile必须从FROM指令开始。该镜像可以是任何有效的图像- 从公共存储库中拉出镜像特别容易启动。

  • ARG是Dockerfile中可能在FROM之前的唯一指令。请参阅了解ARG和FROM之间的相互作用
  • FROM可以在单个Dockerfile中多次出现,以创建多个映像或将一个构建阶段用作对另一个构建阶段的依赖。只需在每个新的FROM指令之前记录一次提交输出的最后一个图像ID。每个FROM指令清除由先前指令创建的任何状态。
  • 通过将AS Name添加到FROM指令中,可以选择为新的构建阶段指定名称。该名称可以在后续的FROM和COPY --from = <名称|索引>指令中使用,以引用在此阶段构建的映像。
  • tagdigest值是可选的。如果您忽略其中任何一个,那么缺省情况下构建器将采用latest标签。如果构建器找不到该tag值,则返回错误。

在FROM引用多平台图像的情况下,可选的--platform标志可用于指定图像的平台。例如,linux/amd64, linux/arm64,或windows/amd64。默认情况下,使用构建请求的目标平台。可以在此标志的值中使用全局构建参数,例如,自动平台ARG 允许您将阶段强制到本机构建平台(--platform=$BUILDPLATFORM),并使用它来交叉编译到阶段内部的目标平台。

了解ARG和FROM之间的交互方式

FROM指令支持由第一个FROM之前的任何ARG指令声明的变量。

ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD  /code/run-app

FROM extras:${CODE_VERSION}
CMD  /code/run-extras

ARGFROM之前的声明位于构建阶段之外,因此不能在FROM之后的任何指令中使用它。要ARG在第一次FROM使用声明之前使用默认值,请在ARG构建阶段使用不带值的指令:

ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version

RUN

RUN有2种形式:

  • RUN <command>shell形式,命令在shell中运行,默认情况下/bin/sh -c在Linux或cmd /S /C在Windows 上运行)
  • RUN ["executable", "param1", "param2"](exec表单)

RUN指令将在当前图像顶部的新层中执行所有命令,并提交结果。生成的提交图像将用于Dockerfile中的下一步。

分层运行RUN指令并生成提交符合Docker的核心概念,在Docker上,提交很方便,并且可以从映像历史记录的任何位置创建容器,就像源代码控制一样。

exec表单可以避免破坏shell字符串,并使用不包含指定shell可执行文件的基本映像运行RUN命令。

可以使用以下 命令更改shell形式的默认shell SHELL

可以使用SHELL命令更改shell形式的默认shell。

RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'

它们在一起等效于以下这一行:

RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'

注意:要使用'/bin/sh'以外的其他shell,请使用exec表单传入所需的shell。例如, RUN ["/bin/bash", "-c", "echo hello"]

注意exec表单被解析为JSON数组,这意味着您必须在单词周围使用双引号(“)而非单引号(')。

注意:与shell表单不同,exec表单不会调用命令shell。这意味着不会进行常规的外壳处理。例如, RUN [ "echo", "$HOME" ]将不会对$HOME进行变量替换。如果要进行shell处理,则可以使用shell形式或直接执行shell,例如:RUN [ "sh", "-c", "echo $HOME" ]。当使用exec表单并直接执行shell时(例如在shell表单中),是由shell进行环境变量扩展,而不是docker。

注意:在JSON格式中,必须转义反斜杠。这在Windows中特别有用,在Windows中反斜杠是路径分隔符。否则,由于无效的JSON,以下行将被视为shell形式,并且会以意外的方式失败: RUN ["c:\windows\system32\tasklist.exe"] 此示例的正确语法为: RUN ["c:\\windows\\system32\\tasklist.exe"]

RUN指令缓存在下一次构建期间,不会自动失效。诸如RUN apt-get dist-upgrade -y之类的指令的缓存将在下一次构建中重用。可以使用--no-cache标志使RUN指令的缓存无效,例如docker build --no-cache。

有关更多信息,请参见Dockerfile最佳实践指南

RUN指令的高速缓存可以被ADD指令无效。有关详情,请参见 下文

(运行)已知问题

  • 问题783是有关使用AUFS文件系统时可能发生的文件权限问题。例如,您在尝试rm文件过程中可能会注意到它。

    对于具有最新aufs版本的系统(即,dirperm1可以设置安装选项),docker将尝试通过使用dirperm1 options挂载层来尝试自动修复问题。有关docker将尝试通过使用dirperm1选项安装各层来自动修复该问题。有关irperm1选项的更多详细信息,请参见aufs手册页

    如果您的系统不支持dirperm1,则该问题描述了一种解决方法。

CMD

CMD指令具有三种形式:

  • CMD ["executable","param1","param2"]exec表单,这是首选形式)
  • CMD ["param1","param2"](作为ENTRYPOINT的默认参数
  • CMD command param1 param2外壳形式)

Dockerfile中只能有一条CMD指令。如果您列出多个CMD指令, 则只有最后一个CMD指令才会生效。

CMD的主要目的是为执行中的容器提供默认值。这些默认值可以包含可执行文件,也可以省略可执行文件,在这种情况下,您还必须指定ENTRYPOINT指令。

注意:如果CMD用于提供ENTRYPOINT 指令的默认参数,则CMDENTRYPOINT指令均应使用JSON数组格式指定。

注意exec表单被解析为JSON数组,这意味着您必须在单词周围使用双引号(“)而非单引号(')。

注意:与shell表单,exec表单不会调用shell命令。这意味着不会进行常规的外壳处理。例如, CMD [ "echo", "$HOME" ]将不会对$HOME进行变量替换。如果要进行shell处理,则可以使用shell形式或直接执行shell,例如:CMD [ "sh", "-c", "echo $HOME" ]。当使用exec表单并直接执行shell时(例如在shell表单中),是由shell进行环境变量扩展,而不是docker。

当以shell或exec表单使用时,该CMD指令设置运行映像时要执行的命令。

如果使用外壳形式CMD,则将在中<command>执行 /bin/sh -c

FROM ubuntu
CMD echo "This is a test." | wc -

如果您想没有外壳情况下运行<command> 则必须将命令表示为JSON数组,并提供可执行文件的完整路径。 此数组形式是CMD的首选格式。任何其他参数必须在数组中分别表示为字符串:

FROM ubuntu
CMD ["/usr/bin/wc","--help"]

如果您希望容器每次都运行相同的可执行文件,则应考虑ENTRYPOINT与结合使用CMD。请参阅 ENTRYPOINT

如果用户指定docker run的参数,则它们将覆盖中指定的默认值CMD

注意:请勿混淆RUNCMDRUN实际上运行命令并提交结果;CMD在生成时不执行任何操作,但指定映像的预期命令。

LABEL

LABEL <key>=<value> <key>=<value> <key>=<value> ...

LABEL指令将元数据添加到图像。LABEL是键值对。若要在LABEL值中包含空格,请像在命令行分析中一样使用引号和反斜杠。一些用法示例:

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."

一幅图像可以有多个标签。您可以在一行上指定多个标签。在Docker 1.10之前的版本中,这减小了最终映像的大小,但是情况不再如此。您仍然可以通过以下两种方式之一选择在一条指令中指定多个标签:

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

基本或父图像(“ FROM”行中的图像)中包含的标签由您的图像继承。如果标签已经存在但具有不同的值,则最近应用的值将覆盖任何先前设置的值。

要查看图像的标签,请使用docker inspect命令。

"Labels": {
    "com.example.vendor": "ACME Incorporated"
    "com.example.label-with-value": "foo",
    "version": "1.0",
    "description": "This text illustrates that label-values can span multiple lines.",
    "multi.label1": "value1",
    "multi.label2": "value2",
    "other": "value3"
},

MAAINAINER(已弃用)

MAINTAINER <name>

MAINTAINER指令设置所生成图像的“作者”字段。LABEL指令是此指令的更灵活的版本,您应该改用它,因为它可以设置所需的任何元数据,并且可以轻松查看,例如使用docker inspect。要设置与MAINTAINER字段相对应的标签,可以使用:

LABEL maintainer="SvenDowideit@home.org.au"

这将从docker inspect其他标签上可见。

EXPOSE

EXPOSE <port> [<port>/<protocol>...]

EXPOSE指令通知Docker容器在运行时监听指定的网络端口。您可以指定端口是侦听TCP还是UDP,如果未指定协议,则默认值为TCP。

EXPOSE指令实际上并未发布端口。它充当构建映像的人与运行容器的人之间的一种文档类型,有关打算发布哪些端口的信息。要在运行容器时实际发布端口,请在docker run上使用-p标志发布并映射一个或多个端口,或使用-P标志发布所有公开的端口并将其映射到高阶端口。

默认情况下,EXPOSE假定为TCP。您还可以指定UDP:

EXPOSE 80/udp

要同时在TCP和UDP上公开,请包括以下两行:

EXPOSE 80/tcp
EXPOSE 80/udp

在这种情况下,如果-P与配合使用docker run,则该端口仅对TCP公开一次,对于UDP公开一次。请记住,-P该端口在主机上使用临时的高阶主机端口,因此该端口对于TCP和UDP将是不同的。

无论EXPOSE设置如何,都可以在运行时使用该-p标志覆盖它们。例如

docker run -p 80:80/tcp -p 80:80/udp ...

要在主机系统上设置端口重定向,请参阅使用-P标志。该docker network命令支持创建网络以在容器之间进行通信,而无需暴露或发布特定端口,因为连接到网络的容器可以通过任何端口相互通信。有关详细信息,请参阅此功能的 概述)。

ENV

ENV <key> <value>
ENV <key>=<value> ...

ENV指令将环境变量<key>设置为value <value>。此值将在构建阶段中所有后续指令的环境中使用,并且在许多情况下也可以内联替换

ENV指令有两种形式。第一种形式ENV <key> <value>会将一个变量设置为一个值。第一个空格之后的整个字符串将被视为<value>-包括空格字符。该值将为其他环境变量解释,因此如果不对引号字符进行转义,则将其删除。

第二种形式 ENV <key>=<value> ...允许一次设置多个变量。请注意,第二种形式在语法中使用等号(=),而第一种形式则不使用等号(=)。像命令行解析一样,引号和反斜杠可用于在值中包含空格。

例如:

ENV myName="John Doe" myDog=Rex\ The\ Dog \
    myCat=fluffy

ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy

将在最终图像中产生相同的结果。

当从结果映像运行容器时,使用ENV设置的环境变量将保留。您可以使用docker inspect查看值,并使用docker run --env <key>=<value>更改它们。

注意:环境持久性可能导致意外的副作用。例如,设置ENV DEBIAN_FRONTEND noninteractive可能会使基于Debian的映像上的apt-get用户感到困惑。要为单个命令设置值,请使用 RUN <key>=<value> <command>

ADD

ADD有两种形式:

  • ADD [--chown=<user>:<group>] <src>... <dest>
  • ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] (此格式对于包含空格的路径是必需的)

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

ADD指令从<src>复制新文件,目录或远程文件URL,并将它们添加到映像的文件系统中的路径<dest>。

<src>可以指定多个资源,但是如果它们是文件或目录,则将其路径解释为相对于构建上下文源的路径。

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

ADD hom* /mydir/        # adds all files starting with "hom"
ADD hom?.txt /mydir/    # ? is replaced with any single character, e.g., "home.txt"

<dest>目录是一个绝对路径,或相对于WORKDIR的一个相对路径,源将复制到目标容器中。

ADD test relativeDir/          # adds "test" to `WORKDIR`/relativeDir/
ADD test /absoluteDir/         # adds "test" to /absoluteDir/

添加包含特殊字符(例如[ 和])的文件或目录时,您需要按照Golang规则对这些路径进行转义,以防止将它们视为匹配模式。例如,要添加名为的文件arr[0].txt,请使用以下命令;

ADD arr[[]0].txt /mydir/    # copy a file named "arr[0].txt" to /mydir/

除非可选的--chown标志指定给定的用户名,组名或UID / GID组合以请求对所添加内容的特定所有权,否则所有新文件和目录均使用UID和GID为0创建。--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/

如果容器根文件系统不包含/etc/passwd或 /etc/group文件,并且在--chown 标志中使用了用户名或组名,则该构建将在该ADD操作上失败。使用数字ID不需要查找,并且不依赖于容器根文件系统内容。

如果<src>是远程文件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 wgetRUN curl或从容器中使用其他工具,因为该ADD指令不支持身份验证。

注意如果<src>的内容已更改,则遇到的第一个ADD指令将使Dockerfile中所有后续指令的缓存无效。这包括使RUN指令的缓存无效。有关更多信息,请参见Dockerfile最佳实践指南

ADD 遵守以下规则:

  • <src>路径必须是构建的内部上下文; 您不能添加../something / something,因为docker构建的第一步是将上下文目录(和子目录)发送到docker守护程序。

  • 如果<src>是URL,并且<dest>不以斜杠结尾,则从URL下载文件并将其复制到<dest>。

  • 如果<src>是URL,而<dest>确实以斜杠结尾,则从URL推断文件名,并将文件下载到<dest> / <filename>。例如,添加http://example.com/foobar /将创建文件/ foobar。该URL必须具有不平凡的路径,以便在这种情况下可以找到适当的文件名(http://example.com将不起作用)。

  • 如果<src>是目录,则将复制目录的整个内容,包括文件系统元数据。

注意:目录本身不被复制,仅被复制其内容。

  • 如果<src>是采用公认压缩格式(身份,gzip,bzip2或xz)的本地tar归档文件,则将其解压缩为目录。来自远程URL的资源不会被解压缩。复制或解压缩目录时,其行为与tar -x相同,结果是以下各项的并集:

    1. 目标路径上存在的任何内容
    2. 源树的内容,以逐个文件为基础,以“ 2.”为基础解决了冲突。

    注意:仅根据文件的内容(而不是文件名)来判断文件是否被识别为公认的压缩格式。例如,如果一个空文件碰巧以.tar.gz结尾,则该文件将不会被识别为压缩文件,并且不会生成任何类型的解压缩错误消息,而是会将文件简单地复制到目标位置。

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

  • 如果<src>直接或由于使用通配符而指定了多个资源,则该资源<dest>必须是目录,并且必须以斜杠结尾/

  • 如果<dest>不以斜杠结尾,则将其视为常规文件,并将其内容<src>写入<dest>

  • 如果<dest>不存在,它将与路径中所有缺少的目录一起创建。

复制

COPY有两种形式:

  • COPY [--chown=<user>:<group>] <src>... <dest>
  • COPY [--chown=<user>:<group>] ["<src>",... "<dest>"] (此格式对于包含空格的路径是必需的)

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

COPY指令从<src>中复制新文件或目录 ,并将它们添加到路径中容器的文件系统中<dest>

<src>可以指定多个资源,但是文件和目录的路径将被解释为相对于构建上下文的源。

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

COPY hom* /mydir/        # adds all files starting with "hom"
COPY hom?.txt /mydir/    # ? is replaced with any single character, e.g., "home.txt"

<dest>是绝对路径或相对于WORKDIR的路径,源将在目标容器内复制到该路径。

COPY test relativeDir/   # adds "test" to `WORKDIR`/relativeDir/
COPY test /absoluteDir/  # adds "test" to /absoluteDir/

复制包含特殊字符(例如[ 和])的文件或目录时,您需要按照Golang规则转义那些路径,以防止将它们视为匹配模式。例如,要复制名为的文件arr[0].txt,请使用以下命令;

COPY arr[[]0].txt /mydir/    # copy a file named "arr[0].txt" to /mydir/

除非可选的--chown标志指定给定的用户名,组名或UID / GID组合以请求对复制内容的特定所有权,否则所有新文件和目录都将使用UID和GID为0来创建。--chown标志的格式允许使用用户名和组名字符串,或直接整数UID和GID的任意组合。提供不带组名的用户名或不带GID的UID将使用与UID相同的数字GID。如果提供了用户名或组名,则将使用容器的根文件系统 /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无法使用。

(可选)COPY接受一个标志--from=<name|index>,该标志可用于将源位置设置为将使用的先前构建阶段(使用创建FROM .. AS <name>),而不是用户发送的构建上下文。该标志还接受为以FROM指令开头的所有先前构建阶段分配的数字索引 。如果找不到具有指定名称的构建阶段,则尝试使用具有相同名称的映像代替。

COPY 遵守以下规则:

  • <src>路径必须是构建的内部语境; 您不能这样做COPY ../something /something,因为docker build的第一步是将上下文目录(和子目录)发送到docker守护程序。

  • 如果<src>是目录,则将复制目录的整个内容,包括文件系统元数据。

注意:目录本身不被复制,仅复制其内容。

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

  • 如果<src>直接或由于使用通配符而指定了多个资源,则该资源<dest>必须是目录,并且必须以斜杠结尾/

  • 如果<dest>不以斜杠结尾,则将其视为常规文件,并将其内容<src>写入<dest>

  • 如果<dest>不存在,它将与路径中所有缺少的目录一起创建。

ENTRYPOINT

ENTRYPOINT有两种形式:

  • ENTRYPOINT ["executable", "param1", "param2"] (执行表单,首选)
  • ENTRYPOINT command param1 param2 (外壳表单

ENTRYPOINT允许您配置将作为可执行文件运行的容器。

例如,以下将使用其默认内容启动nginx,并监听端口80:

docker run -i -t --rm -p 80:80 nginx

docker run <image>的命令行参数将附加在exec形式ENTRYPOINT的所有元素之后,并将覆盖使用CMD指定的所有元素。这允许将参数传递给入口点,即docker run <image> -d将-d参数传递给入口点。您可以使用docker run --entrypoint标志覆盖ENTRYPOINT指令。

shell形式可防止使用任何CMD或运行命令行参数,但其缺点是ENTRYPOINT将作为/ bin / sh -c的子命令启动,该子命令不传递信号。这意味着可执行文件将不是容器的PID 1,并且不会接收Unix信号,因此您的可执行文件将不会从docker stop <container>接收到SIGTERM。只有Dockerfile中的最后一条ENTRYPOINT指令才会生效。

执行表单ENTRYPOINT示例

您可以使用exec表单ENTRYPOINT来设置相当稳定的默认命令和参数,然后使用这两种形式的CMD来设置更可能被更改的其他默认值。

FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

运行容器时,可以看到这top是唯一的过程:

$ docker run -it --rm --name test  top -H
top - 08:25:00 up  7:27,  0 users,  load average: 0.00, 0.01, 0.05
Threads:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.1 us,  0.1 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:   2056668 total,  1616832 used,   439836 free,    99352 buffers
KiB Swap:  1441840 total,        0 used,  1441840 free.  1324440 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
    1 root      20   0   19744   2336   2080 R  0.0  0.1   0:00.04 top

要进一步检查结果,可以使用docker exec

$ docker exec -it test ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  2.6  0.1  19752  2352 ?        Ss+  08:24   0:00 top -b -H
root         7  0.0  0.1  15572  2164 ?        R+   08:25   0:00 ps aux

您可以使用docker stop test优雅地请求top关闭。

下面Dockerfile显示了使用ENTRYPOINT来在前台运行Apache(即,作为PID 1):

FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

如果需要为单个可执行文件编写启动脚本,则可以使用execgosu 命令确保最终的可执行文件接收Unix信号:

#!/usr/bin/env bash
set -e

if [ "$1" = 'postgres' ]; then
    chown -R postgres "$PGDATA"

    if [ -z "$(ls -A "$PGDATA")" ]; then
        gosu postgres initdb
    fi

    exec gosu postgres "$@"
fi

exec "$@"

最后,如果您需要在关闭时进行一些额外的清理(或与其他容器通信),或者协调多个可执行文件,则可能需要确保ENTRYPOINT脚本接收Unix信号,将其传递,然后执行一些更多的工作:

#!/bin/sh
# Note: I've written this using sh so it works in the busybox container too

# USE the trap if you need to also do manual cleanup after the service is stopped,
#     or need to start multiple services in the one container
trap "echo TRAPed signal" HUP INT QUIT TERM

# start service in background here
/usr/sbin/apachectl start

echo "[hit enter key to exit] or run 'docker stop <container>'"
read

# stop service and clean up here
echo "stopping apache"
/usr/sbin/apachectl stop

echo "exited $0"

如果使用docker run -it --rm -p 80:80 --name test apache来运行此映像,则可以使用docker execdocker top来检查容器的进程,然后要求脚本停止Apache:

$ docker exec -it test ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.1  0.0   4448   692 ?        Ss+  00:42   0:00 /bin/sh /run.sh 123 cmd cmd2
root        19  0.0  0.2  71304  4440 ?        Ss   00:42   0:00 /usr/sbin/apache2 -k start
www-data    20  0.2  0.2 360468  6004 ?        Sl   00:42   0:00 /usr/sbin/apache2 -k start
www-data    21  0.2  0.2 360468  6000 ?        Sl   00:42   0:00 /usr/sbin/apache2 -k start
root        81  0.0  0.1  15572  2140 ?        R+   00:44   0:00 ps aux
$ docker top test
PID                 USER                COMMAND
10035               root                {run.sh} /bin/sh /run.sh 123 cmd cmd2
10054               root                /usr/sbin/apache2 -k start
10055               33                  /usr/sbin/apache2 -k start
10056               33                  /usr/sbin/apache2 -k start
$ /usr/bin/time docker stop test
test
real	0m 0.27s
user	0m 0.03s
sys	0m 0.03s

注意:您可以--entrypoint使用来覆盖ENTRYPOINT设置,但这只能将二进制文件设置为execsh -c将不使用)。

注意exec表单被解析为JSON数组,这意味着您必须在单词周围使用双引号(“)而非单引号(')。

注意:与shell表单不同,exec表单不会调用命令shell。这意味着不会进行常规的外壳处理。例如, ENTRYPOINT [ "echo", "$HOME" ]将不会对$HOME进行变量替换。如果要进行shell处理,则可以使用shell形式或直接执行shell,例如:ENTRYPOINT [ "sh", "-c", "echo $HOME" ]。当使用exec表单并直接执行shell时(例如在shell表单中),是由shell进行环境变量扩展,而不是docker。

Shell形式ENTRYPOINT示例

您可以为ENTRYPOINT指定一个纯字符串,它将在中执行/bin/sh -c。这种形式将使用外壳处理来替代外壳环境变量,并且将忽略任何CMDdocker run命令行参数。为了确保能够正确docker stop发出任何长期运行的ENTRYPOINT可执行文件信号,您需要记住以以下命令启动它exec

FROM ubuntu
ENTRYPOINT exec top -b

运行此图像时,您将看到单个PID 1过程:

$ docker run -it --rm --name test top
Mem: 1704520K used, 352148K free, 0K shrd, 0K buff, 140368121167873K cached
CPU:   5% usr   0% sys   0% nic  94% idle   0% io   0% irq   0% sirq
Load average: 0.08 0.03 0.05 2/98 6
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
    1     0 root     R     3164   0%   0% top -b

它将在docker stop上干净地退出:

$ /usr/bin/time docker stop test
test
real	0m 0.20s
user	0m 0.02s
sys	0m 0.04s

如果您忘记将exec添加到ENTRYPOINT的开头:

FROM ubuntu
ENTRYPOINT top -b
CMD --ignored-param1

如果您忘记将exec添加到ENTRYPOINT的开头:

$ docker run -it --name test top --ignored-param2
Mem: 1704184K used, 352484K free, 0K shrd, 0K buff, 140621524238337K cached
CPU:   9% usr   2% sys   0% nic  88% idle   0% io   0% irq   0% sirq
Load average: 0.01 0.02 0.05 2/101 7
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
    1     0 root     S     3168   0%   0% /bin/sh -c top -b cmd cmd2
    7     1 root     R     3164   0%   0% top -b

从顶部的输出中可以看到,指定的ENTRYPOINT不是PID 1。

如果您随后运行docker stop test,则容器不会干净退出-超时后,将强制执行stop命令发送SIGKILL:

$ docker exec -it test ps aux
PID   USER     COMMAND
    1 root     /bin/sh -c top -b cmd cmd2
    7 root     top -b
    8 root     ps aux
$ /usr/bin/time docker stop test
test
real	0m 10.19s
user	0m 0.04s
sys	0m 0.03s

了解CMD和ENTRYPOINT如何相互作用

CMD和ENTRYPOINT指令均定义运行容器时执行的命令。很少有规则描述他们的合作。

  1. Dockerfile应该指定CMDENTRYPOINT命令中的至少一个。

  2. 使用容器作为可执行文件时,应定义ENTRYPOINT。

  3. CMD应该用作定义ENTRYPOINT命令或在容器中执行即席命令的默认参数的方式。

  4. 使用替代参数运行容器时,CMD将被覆盖。

下表显示了针对不同ENTRYPOINTCMD组合执行的命令:

 没有入口点ENTRYPOINT exec_entry p1_entryENTRYPOINT [“ exec_entry”,“ p1_entry”]
没有CMD错误,不允许/ bin / sh -c exec_entry p1_entryexec_entry p1_entry
CMD [“ exec_cmd”,“ p1_cmd”]exec_cmd p1_cmd/ bin / sh -c exec_entry p1_entryexec_entry p1_entry exec_cmd p1_cmd
CMD [“ p1_cmd”,“ p2_cmd”]p1_cmd p2_cmd/ bin / sh -c exec_entry p1_entryexec_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_entryexec_entry p1_entry / bin / sh -c exec_cmd p1_cmd

注意:如果CMD是从基础图像定义的,则设置ENTRYPOINT将重置CMD为空值。在这种情况下,CMD必须在当前图像中定义一个值。

VOLUME

VOLUME ["/data"]

VOLUME指令创建具有指定名称的安装点,并将其标记为保存来自本地主机或其他容器的外部安装的卷。该值可以是JSON数组,也可以是VOLUME ["/var/log/"]具有多个参数的纯字符串,例如VOLUME /var/logVOLUME /var/log /var/db。有关通过Docker客户端的更多信息/示例和安装说明,请参阅 通过Volumes共享目录 。

docker run命令使用基本映像内指定位置上存在的任何数据初始化新创建的卷。例如,考虑以下Dockerfile片段:

FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol

该Dockerfile生成一个映像,该映像使docker run在/ myvol处创建一个新的挂载点并将问候文件复制到新创建的卷中。

有关指定卷的注意事项

关于Dockerfile中的卷,请注意以下几点。

  • 基于Windows的容器上的卷:使用基于Windows的容器时,容器内的卷的目的地必须是以下之一:

    • 不存在或空目录
    • C:以外的驱动器 
  • 从Dockerfile中更改卷:如果在声明了卷后有任何构建步骤更改了卷中的数据,则这些更改将被丢弃。

  • JSON格式:列表被解析为JSON数组。您必须用双引号(")而不是单引号(')括住单词。

  • 主机目录是在容器运行时声明的:主机目录(挂载点)从本质上说是依赖于主机的。这是为了保留图像的可移植性,因为不能保证给定的主机目录在所有主机上都可用。因此,您无法从Dockerfile中挂载主机目录。VOLUME指令不支持指定host-dir参数。创建或运行容器时,必须指定安装点。

USER

USER <user>[:<group>] or
USER <UID>[:<GID>]

USER指令设置运行映像时要使用的用户名(或UID)以及可选的用户组(或GID),以及Dockerfile中跟随该映像的所有RUN,CMD和ENTRYPOINT指令。

警告:当用户没有主要组时,该映像(或以下说明)将与该root组一起运行。

在Windows上,如果不是内置帐户,则必须首先创建用户。这可以通过net user作为Dockerfile的一部分调用的命令来完成。

    FROM microsoft/windowsservercore
    # Create Windows user in the container
    RUN net user /add patrick
    # Set it for subsequent commands
    USER patrick

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

最终的输出pwd命令这Dockerfile将是 /a/b/c

WORKDIR指令可以解析以前使用ENV设置的环境变量。您只能使用在Dockerfile中显式设置的环境变量。例如:

ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

最终pwd命令的输出Dockerfile将是 /path/$DIRNAME

ARG

ARG <name>[=<default value>]

ARG指令定义了一个变量,用户可以在构建时docker build使用带有--build-arg <varname>=<value> 标志的命令将其传递给构建器。如果用户指定了未在Dockerfile中定义的构建参数,则构建会输出警告。

[警告]没有使用一个或多个build-args [foo]。

Dockerfile可能包含一个或多个ARG指令。例如,以下是有效的Dockerfile:

FROM busybox
ARG user1
ARG buildno
...

警告:不建议使用构建时变量来传递诸如github密钥,用户凭据等机密。构建时变量值对于使用docker history命令的映像的任何用户都是可见的。

Default values

ARG指令可以可选地包括一个默认值:

FROM busybox
ARG user1=someuser
ARG buildno=1
...

如果ARG指令具有缺省值,并且在构建时未传递任何值,那么构建器将使用缺省值。

Scope

ARG变量定义从Dockerfile中定义的行开始生效,而不是该参数在命令行或其他地方的使用。例如,考虑以下Dockerfile:

1 FROM busybox
2 USER ${user:-some_user}
3 ARG user
4 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 busybox
ARG SETTINGS
RUN ./run/setup $SETTINGS

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

使用ARG变量

您可以使用ARGENV指令来指定该RUN指令可用的变量。使用ENV指令定义的环境变量 始终会覆盖ARG同名指令。考虑此Dockerfile和ENVand ARG指令。

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER v1.0.0
4 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规范,可以在ARGENV指令之间创建更有用的交互:

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

ARG指令不同,ENV值始终保留在生成的映像中。考虑不带--build-arg标志的Docker构建:

$ docker build .

使用此Dockerfile示例,CONT_IMG_VER它仍然保留在映像中,但是其值将是指令v1.0.0第3行中的默认设置ENV

在此示例中,变量扩展技术使您可以从命令行传递参数,并利用ENV指令将其保留在最终映像中 。仅有限的一组Dockerfile指令支持变量扩展

预定义的ARG

Docker具有一组预定义的ARG变量,您可以在Dockerfile中使用它们而无需相应的ARG指令。

  • HTTP_PROXY
  • http_proxy
  • HTTPS_PROXY
  • https_proxy
  • FTP_PROXY
  • ftp_proxy
  • NO_PROXY
  • no_proxy

要使用它们,只需使用以下标志在命令行中传递它们:

--build-arg <varname>=<value>

默认情况下,这些预定义变量从Docker历史记录的输出中排除。排除它们可以减少意外泄漏HTTP_PROXY变量中的敏感身份验证信息的风险。

例如,考虑使用以下命令构建以下Dockerfile --build-arg HTTP_PROXY=http://user:pass@proxy.lon.example.com

FROM ubuntu
RUN echo "Hello World"

在这种情况下,HTTP_PROXY变量的值在 docker history中不可用,也不被缓存。如果要更改位置,并且代理服务器已更改为http://user:pass@proxy.sfo.example.com,则后续的构建不会导致高速缓存未命中。

如果您需要覆盖此行为,则可以通过ARG 在Dockerfile中添加如下语句来做到这一点:

FROM ubuntu
ARG HTTP_PROXY
RUN echo "Hello World"

构建此Dockerfile时,将HTTP_PROXY保留在中 docker history,并且更改其值会使构建缓存无效。

全球范围内的自动平台ARG

仅当使用BuildKit后端时,此功能才可用。

Docker在执行构建的节点的平台(构建平台)和结果映像的平台(目标平台)上预定义了一组ARG变量。可以使用docker build上的--platform标志指定目标平台。

以下ARG变量是自动设置的:

  • TARGETPLATFORM-构建结果的平台。例如linux/amd64linux/arm/v7windows/amd64
  • TARGETOS -TARGETPLATFORM的OS组件
  • TARGETARCH -TARGETPLATFORM的体系结构组件
  • TARGETVARIANT -TARGETPLATFORM的变体组件
  • BUILDPLATFORM -执行构建的节点的平台。
  • BUILDOS -BUILDPLATFORM的OS组件
  • BUILDARCH -BUILDPLATFORM的体系结构组件
  • BUILDVARIANT -BUILDPLATFORM的变体组件

这些参数是在全局范围内定义的,因此在构建阶段或您的RUN命令中不会自动提供。为了在构建阶段公开这些参数之一,请重新定义它而没有价值。

例如:

FROM alpine
ARG TARGETPLATFORM
RUN echo "I'm building for $TARGETPLATFORM"

对构建缓存的影响

ARG变量不会像ENV变量那样持久保存到生成的映像中。但是,ARG变量确实以类似的方式影响构建缓存。如果Dockerfile定义了一个值与先前版本不同的ARG变量,则首次使用时会发生“缓存未命中”,而不是其定义。特别是,所有在ARG指令之后的RUN指令都隐式地使用ARG变量(作为环境变量),因此可能导致高速缓存未命中。除非Dockerfile中有匹配的ARG语句,否则所有预定义的ARG变量均免于缓存。

例如,考虑以下两个Dockerfile:

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 RUN echo $CONT_IMG_VER
1 FROM ubuntu
2 ARG CONT_IMG_VER
3 RUN echo hello

如果--build-arg CONT_IMG_VER=<value>在命令行上指定,则在两种情况下,第2行上的指定都不会导致高速缓存未命中。第3行确实会导致缓存未命中。ARG CONT_IMG_VER导致RUN行被标识为与正在运行的CONT_IMG_VER=<value>echo hello 相同,因此,如果进行了<value> 更改,则会遇到缓存未命中的情况。

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

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER $CONT_IMG_VER
4 RUN echo $CONT_IMG_VER

在此示例中,高速缓存未命中发生在第3行。之所以发生未命中,是因为该变量在ENV引用中的值引用了该ARG变量,并且该变量通过命令行进行了更改。在此示例中,该ENV 命令使图像包含该值。

如果一条ENV指令覆盖ARG了同名指令,例如Dockerfile:

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER hello
4 RUN echo $CONT_IMG_VER

第3行不会导致缓存未命中,因为的值CONT_IMG_VER是一个常量(hello)。结果,RUN(第4行)使用的环境变量和值在两次构建之间不会改变。

ONBUILD

ONBUILD [INSTRUCTION]

当将映像用作另一个构建的基础时,ONBUILD指令会将触发指令添加到映像中。触发器将在下游构建的上下文中执行,就像它已被插入到下游Dockerfile中的FROM指令之后一样。

任何构建指令都可以注册为触发器。

如果要构建的图像将用作构建其他图像的基础,例如应用程序构建环境或可以使用用户特定配置自定义的守护程序,则此功能很有用。

例如,如果您的映像是可重用的Python应用程序构建器,则将需要在特定目录中添加应用程序源代码,并且此后可能需要调用构建脚本。您不能立即调用ADD和RUN,因为您还没有访问应用程序源代码的权限,并且每个应用程序构建的源代码都不同。您可以简单地为应用程序开发人员提供一个样板Dockerfile,以将其复制粘贴到他们的应用程序中,但这效率低下,容易出错并且难以更新,因为它与特定于应用程序的代码混合在一起。

解决方案是使用ONBUILD注册预先的指令以在下一个构建阶段中稍后运行。

运作方式如下:

  1. 当遇到ONBUILD指令时,构建器将触发器添加到正在构建的图像的元数据中。该指令不会影响当前版本。
  2. 在构建结束时,所有触发器的列表都存储在映像清单中的OnBuild键下。可以使用docker inspect命令检查它们。
  3. 稍后,可以使用FROM指令将该图像用作新构建的基础。作为处理FROM指令的一部分,下游构建器将查找ONBUILD触发器,并以与注册时相同的顺序执行它们。如果任何触发器失败,那么FROM指令将中止,从而导致构建失败。如果所有触发器都成功,则FROM指令完成,并且构建照常继续。
  4. 执行完触发器后,将从最终图像中清除触发器。换句话说,它们不是“孙子代”版本所继承的。

例如,您可以添加以下内容:

[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

警告:不允许使用ONBUILD ONBUILD形式,来链接ONBUILD指令。

警告:该ONBUILD指令可能不会触发FROM或执行MAINTAINER

STOPSIGNAL

STOPSIGNAL signal

STOPSIGNAL指令设置将被发送到容器退出的系统调用信号。该信号可以是与内核syscall表中的位置匹配的有效无符号数字(例如9),也可以是SIGNAME格式的信号名称(例如SIGKILL)。

HEALTHCHECK

HEALTHCHECK指令有两种形式:

  • HEALTHCHECK [OPTIONS] CMD command (通过在容器内部运行命令来检查容器的运行状况)
  • HEALTHCHECK NONE (禁用从基本映像继承的任何运行状况检查)

HEALTHCHECK指令告诉Docker如何测试容器以检查其是否仍在工作。这样可以检测到诸如Web服务器陷入无限循环并且无法处理新连接的情况,即使服务器进程仍在运行。

指定容器的运行状况检查后,除了其正常状态外,它还具有运行状况。此状态最初为starting。只要运行状况检查通过,它将变为healthy(以前处于任何状态)。在一定数量的连续失败之后,它变为unhealthy

之前可能出现的选项CMD是:

  • --interval=DURATION(默认值:30s
  • --timeout=DURATION(默认值:30s
  • --start-period=DURATION(默认值:0s
  • --retries=N(默认值:3

运行状况检查将首先在容器启动后的间隔秒数内运行,然后在每次之前的检查完成后的间隔秒数内运行。

如果单次检查花费的时间超过超时秒数,则认为检查失败。

需要重新尝试连续进行的健康检查失败,才能将容器视为不健康。

开始时间段为需要时间进行引导的容器提供了初始化时间。在此期间的探针故障将不计入最大重试次数。但是,如果运行状况检查在启动期间成功通过,则认为该容器已启动,并且所有连续失败将计入最大重试次数。

Dockerfile中只能有一条HEALTHCHECK指令。如果您列出多个,则只有最后一个HEALTHCHECK才会生效。

CMD关键字后面的命令可以是shell命令(例如HEALTHCHECK CMD /bin/check-running)或exec数组(与其他Dockerfile命令一样;ENTRYPOINT有关详细信息,请参见例如)。

该命令的退出状态指示容器的健康状态。可能的值为:

  • 0:成功-容器健康且可以使用
  • 1:不健康-容器无法正常工作
  • 2:保留-请勿使用此退出代码

例如,要每五分钟检查一次,以便网络服务器能够在三秒钟内为站点的主页提供服务:

HEALTHCHECK --interval=5m --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

为了帮助调试失败的探针,命令在stdout或stderr上写入的任何输出文本(UTF-8编码)将存储在运行状况中,并可以通过查询 docker inspect。此类输出应保持简短(当前仅存储前4096个字节)。

当容器的健康状态发生变化时,将health_status生成具有新状态的事件。

HEALTHCHECK功能已在Docker 1.12中添加。

Shell

SHELL ["executable", "parameters"]

SHELL指令允许覆盖用于命令的shell形式的默认shell 。在Linux上默认的shell是["/bin/sh", "-c"],在Windows 上,默认的shell 是["cmd", "/S", "/C"]。该SHELL指令必须以JSON格式编写在Dockerfile中。

SHELL指令在Windows上特别有用,在Windows上有两个常用且完全不同的本机shell:cmdpowershell,以及可用的替代shell包括sh

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

在Dockerfile中使用它们的外壳形式时,以下指令可能会受到SHELL指令的影响:RUN,CMD和ENTRYPOINT。

以下示例是Windows上常见的模式,可以使用SHELL指令进行精简:

...
RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
...

docker调用的命令将是:

cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"

这效率低下有两个原因。首先,有一个不必要的cmd.exe命令处理器(也称为外壳程序)被调用。其次,shell 形式的每条RUN指令都需要在命令前加上前缀。powershell -command

为了使其更有效,可以采用两种机制之一。一种是使用RUN命令的JSON形式,例如:

...
RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]
...

尽管JSON形式是明确的,并且不使用不必要的cmd.exe,但它确实需要通过双引号和转义来实现更多的详细信息。另一种机制是使用SHELL指令和外壳程序形式,使Windows用户的语法更加自然,尤其是与escapeparser指令结合使用时:

# escape=`

FROM microsoft/nanoserver
SHELL ["powershell","-command"]
RUN New-Item -ItemType Directory C:\Example
ADD Execute-MyCmdlet.ps1 c:\example\
RUN c:\example\Execute-MyCmdlet -sample 'hello world'

导致:

PS E:\docker\build\shell> docker build -t shell .
Sending build context to Docker daemon 4.096 kB
Step 1/5 : FROM microsoft/nanoserver
 ---> 22738ff49c6d
Step 2/5 : SHELL powershell -command
 ---> Running in 6fcdb6855ae2
 ---> 6331462d4300
Removing intermediate container 6fcdb6855ae2
Step 3/5 : RUN New-Item -ItemType Directory C:\Example
 ---> Running in d0eef8386e97


    Directory: C:\


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       10/28/2016  11:26 AM                Example


 ---> 3f2fbf1395d9
Removing intermediate container d0eef8386e97
Step 4/5 : ADD Execute-MyCmdlet.ps1 c:\example\
 ---> a955b2621c31
Removing intermediate container b825593d39fc
Step 5/5 : RUN c:\example\Execute-MyCmdlet 'hello world'
 ---> Running in be6d8e63fe75
hello world
 ---> 8e559e9bf424
Removing intermediate container be6d8e63fe75
Successfully built 8e559e9bf424
PS E:\docker\build\shell>

SHELL指令还可用于修改外壳的操作方式。例如,SHELL cmd /S /C /V:ON|OFF在Windows上使用,可以修改延迟的环境变量扩展语义。

如果需要备用外壳(例如zsh,csh,tcsh等),也可以在Linux上使用SHELL指令。

SHELL功能已在Docker 1.12中添加。

外部实施功能

仅当使用BuildKit后端时,此功能才可用 。

Docker构建支持实验性功能,例如缓存安装,构建机密和ssh转发,这些功能通过使用带有语法指令的构建器的外部实现启用。要了解这些功能,请参阅BuildKit信息库中的文档

Dockerfile示例

在下面,您可以看到Dockerfile语法的一些示例。

# Nginx
#
# VERSION               0.0.1

FROM      ubuntu
LABEL Description="This image is used to start the foobar executable" Vendor="ACME Products" Version="1.0"
RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server
# Firefox over VNC
#
# VERSION               0.3

FROM ubuntu

# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN apt-get update && apt-get install -y x11vnc xvfb firefox
RUN mkdir ~/.vnc
# Setup a password
RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way, but it does the trick)
RUN bash -c 'echo "firefox" >> /.bashrc'

EXPOSE 5900
CMD    ["x11vnc", "-forever", "-usepw", "-create"]
# Multiple images example
#
# VERSION               0.1

FROM ubuntu
RUN echo foo > bar
# Will output something like ===> 907ad6c2736f

FROM ubuntu
RUN echo moo > oink
# Will output something like ===> 695d7793cbe4

# You'll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with
# /oink.

 

<think> 我们正在回答用户关于Dockerfile的详细介绍及使用方法的问题。根据提供的参考资料,我们可以整理出以下内容: 1. Dockerfile是什么? - Dockerfile是一个文本文件,包含构建镜像的指令。 - 指令自上而下排列,每条指令都会在镜像上创建一个层(分层存储结构)。 - 具有可读性,支持注释。 2. Dockerfile的基本结构和使用方法: - 通过编写Dockerfile,然后使用`docker build`命令构建镜像。 - 构建命令示例:`docker build -t image_name:tag .` 3. Dockerfile常用指令(最佳实践): - FROM:指定基础镜像。 - RUN:执行命令,创建新的镜像层。 - COPY:将文件从构建上下文复制到容器中。 - ADD:类似于COPY,但支持自动解压和URL下载(不推荐,除非需要解压功能)。 - ENV:设置环境变量。 - EXPOSE:声明容器运行时监听的端口(实际暴露端口需在运行时指定)。 - CMD:指定容器启动时默认执行的命令(可被覆盖)。 - ENTRYPOINT:配置容器启动时运行的命令(不可被覆盖,通常与CMD配合使用)。 - WORKDIR:设置工作目录。 - USER:指定运行后续命令的用户和用户组。 - VOLUME:定义匿名卷,用于持久化数据。 4. 最佳实践建议(来自参考内容及常见经验): - 使用.dockerignore文件忽略不需要的文件,加速构建过程。 - 尽量减少镜像层数(合理合并RUN指令)。 - 使用多阶段构建(multi-stage builds)来减小最终镜像大小。 - 避免安装不必要的包。 - 一个容器只运行一个进程。 - 使用官方镜像作为基础镜像。 - 将变化频率高的指令放在Dockerfile的后面,以利用构建缓存。 5. 示例Dockerfile: ```Dockerfile # 使用官方Python运行时作为基础镜像 FROM python:3.8-slim # 设置工作目录 WORKDIR /app # 将当前目录内容复制到容器的/app目录下 COPY . /app # 安装项目依赖 RUN pip install --trusted-host pypi.python.org -r requirements.txt # 暴露端口 EXPOSE 80 # 定义环境变量 ENV NAME World # 在容器启动时运行app.py CMD ["python", "app.py"] ``` 6. 构建镜像: - 在Dockerfile所在目录执行:`docker build -t my-python-app .` 7. 运行容器: - `docker run -p 4000:80 my-python-app` 我们将按照上述结构组织回答,并生成相关问题。 </think> ### Dockerfile 详细介绍及使用方法 #### 一、Dockerfile 核心概念 1. **定义** Dockerfile 是包含构建 Docker 镜像指令的文本文件,采用声明式语法描述镜像的组成要素。每条指令在镜像中创建一个可复用的分层(Layer)[^1][^3]。 2. **核心特点** - 📦 **分层构建**:每条指令生成一个只读层,提升构建效率和存储复用性 - 🔍 **可追溯性**:可通过版本控制工具(如 Git)管理变更历史 - ⚙️ **环境一致性**:确保开发、测试、生产环境完全一致 - 🚀 **快速构建**:利用缓存机制加速镜像生成过程 #### 二、Dockerfile 基础结构 ```Dockerfile # 指定基础镜像(必须为首条指令) FROM ubuntu:22.04 # 设置元数据 LABEL maintainer="your-email@example.com" # 安装依赖 RUN apt-get update && apt-get install -y \ python3 \ nginx \ && rm -rf /var/lib/apt/lists/* # 复制文件到镜像 COPY ./app /usr/src/app # 设置工作目录 WORKDIR /usr/src/app # 声明容器运行时暴露的端口 EXPOSE 8080 # 设置容器启动命令 CMD ["python3", "app.py"] ``` #### 三、关键指令详解 | 指令 | 用途 | 最佳实践示例 | |-------------|-----------------------------------|----------------------------------| | `FROM` | 指定基础镜像 | `FROM python:3.9-slim` | | `RUN` | 执行命令 | `RUN pip install --no-cache-dir -r requirements.txt` | | `COPY` | 复制本地文件到镜像 | `COPY . /app` | | `ADD` | 增强版复制(支持URL和解压) | `ADD https://example.com/file.tar.gz /tmp` | | `ENV` | 设置环境变量 | `ENV NODE_ENV=production` | | `EXPOSE` | 声明容器监听端口 | `EXPOSE 80/tcp` | | `CMD` | 容器启动命令(可被覆盖) | `CMD ["nginx", "-g", "daemon off;"]` | | `ENTRYPOINT`| 入口命令(不易被覆盖) | `ENTRYPOINT ["/app/start.sh"]` | | `VOLUME` | 创建数据卷挂载点 | `VOLUME /var/log` | #### 四、进阶最佳实践 1. **优化镜像体积** - 使用多阶段构建(Multi-stage builds): ```Dockerfile # 构建阶段 FROM golang:1.19 AS builder WORKDIR /app COPY . . RUN go build -o myapp . # 运行阶段 FROM alpine:latest COPY --from=builder /app/myapp / CMD ["/myapp"] ``` - 删除不必要的文件(如缓存、临时文件) 2. **提升构建效率** - 将高频变动的指令放在文件末尾 - 使用`.dockerignore`文件排除无关文件 - 合理安排指令顺序以复用构建缓存 3. **安全性增强** - 避免以root用户运行:`USER 1001` - 定期更新基础镜像修补漏洞 - 使用可信源下载依赖 #### 五、完整构建流程 1. 创建 Dockerfile 文件 2. 执行构建命令: ```bash docker build -t my-custom-image:1.0 . ``` 3. 验证镜像: ```bash docker run -d -p 8080:80 my-custom-image:1.0 ``` #### 六、调试技巧 - 检查镜像历史:`docker history <image_id>` - 进入临时容器:`docker run -it --entrypoint /bin/sh <image_id>` - 分析镜像层级:`dive <image_name>` --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值