构建上下文

docker build命令从Dockerfile和上下文docker buildx build构建 Docker 映像 。

什么是构建上下文?

构建上下文是您的构建可以访问的文件集。传递给构建命令的位置参数指定要用于构建的上下文:

$ docker build [OPTIONS] PATH | URL | -
                         ^^^^^^^^^^^^^^

您可以传递以下任何输入作为构建的上下文:

  • 本地目录的相对或绝对路径
  • Git 存储库、tarball 或纯文本文件的远程 URL
  • docker build通过标准输入通过管道传输到命令的纯文本文件或 tarball

文件系统上下文

当您的构建上下文是本地目录、远程 Git 存储库或 tar 文件时,它将成为构建器在构建期间可以访问的文件集。诸如COPY和 之类的构建指令ADD可以引用上下文中的任何文件和目录。

文件系统构建上下文是递归处理的:

  • 当您指定本地目录或 tarball 时,将包含所有子目录
  • 当您指定远程 Git 存储库时,将包含该存储库和所有子模块

有关可在构建中使用的不同类型的文件系统上下文的更多信息,请参阅:

文本文件上下文

当您的构建上下文是纯文本文件时,构建器会将该文件解释为 Dockerfile。通过这种方法,构建不使用文件系统上下文。

有关更多信息,请参阅 空构建上下文

当地背景

要使用本地构建上下文,您可以指定docker build命令的相对或绝对文件路径。以下示例显示了使用当前目录 ( .) 作为构建上下文的构建命令:

$ docker build .
...
#16 [internal] load build context
#16 sha256:23ca2f94460dcbaf5b3c3edbaaa933281a4e0ea3d92fe295193e4df44dc68f85
#16 transferring context: 13.16MB 2.2s done
...

这使得当前工作目录中的文件和目录可供构建器使用。构建器在需要时从构建上下文加载所需的文件。

您还可以通过将 tarball 内容通过管道传输到命令来使用本地 tarball 作为构建上下文docker build。请参阅 压缩包

本地目录

考虑以下目录结构:

.
├── index.ts
├── src/
├── Dockerfile
├── package.json
└── package-lock.json

如果您将此目录作为上下文传递,则 Dockerfile 指令可以在构建中引用并包含这些文件。

# syntax=docker/dockerfile:1
FROM node:latest
WORKDIR /src
COPY package.json package-lock.json .
RUN npm ci
COPY index.ts src .
$ docker build .

来自标准输入的 Dockerfile 的本地上下文

使用以下语法使用本地文件系统上的文件构建映像,同时使用标准输入中的 Dockerfile。

$ docker build -f- <PATH>

该语法使用 -f(或 --file)选项指定要使用的 Dockerfile,并使用连字符 (-) 作为文件名来指示 Docker 从 stdin 读取 Dockerfile。

以下示例使用当前目录 (.) 作为构建上下文,并使用通过 stdin 使用此处文档传递的 Dockerfile 来构建映像。

# create a directory to work in
mkdir example
cd example

# create an example file
touch somefile.txt

# build an image using the current directory as context
# and a Dockerfile passed through stdin
docker build -t myimage:latest -f- . <<EOF
FROM busybox
COPY somefile.txt ./
RUN cat /somefile.txt
EOF

本地 tarball

当您通过管道将 tarball 传递给构建命令时,构建会使用 tarball 的内容作为文件系统上下文。

例如,给定以下项目目录:

.
├── Dockerfile
├── Makefile
├── README.md
├── main.c
├── scripts
├── src
└── test.Dockerfile

您可以创建目录的 tarball 并将其通过管道传输到构建以用作上下文:

$ tar czf foo.tar.gz *
$ docker build - < foo.tar.gz

构建从 tarball 上下文中解析 Dockerfile。您可以使用该 --file标志来指定 Dockerfile 相对于 tarball 根目录的名称和位置。test.Dockerfile 在 tarball 中使用以下命令进行构建:

$ docker build --file test.Dockerfile - < foo.tar.gz

远程上下文

您可以指定远程 Git 存储库、tarball 或纯文本文件的地址作为构建上下文。

  • 对于 Git 存储库,构建器会自动克隆存储库。请参阅 Git 存储库
  • 对于 tarball,构建器会下载并提取 tarball 的内容。请参阅 压缩包

如果远程 tarball 是文本文件,则构建器不会收到 filesystem context,而是假设远程文件是 Dockerfile。请参阅 清空构建上下文

Git 存储库

当您将指向 Git 存储库位置的 URL 作为参数传递给 时docker build,构建器将使用该存储库作为构建上下文。

构建器执行存储库的浅层克隆,仅下载 HEAD 提交,而不是整个历史记录。

构建器递归地克隆存储库及其包含的任何子模块。

$ docker build https://github.com/user/myrepo.git

默认情况下,构建器会克隆您指定的存储库的默认分支上的最新提交。

网址片段

您可以将 URL 片段附加到 Git 存储库地址,以使构建器克隆存储库的特定分支、标签和子目录。

URL 片段的格式为#ref:dir,其中:

  • ref是分支、标签或远程引用的名称
  • dir是存储库内的子目录

例如,以下命令使用container分支以及docker该分支中的子目录作为构建上下文:

$ docker build https://github.com/user/myrepo.git#container:docker

下表列出了所有有效的后缀及其构建上下文:

构建语法后缀提交已使用构建使用的上下文
myrepo.gitrefs/heads/<default branch>/
myrepo.git#mytagrefs/tags/mytag/
myrepo.git#mybranchrefs/heads/mybranch/
myrepo.git#pull/42/headrefs/pull/42/head/
myrepo.git#:myfolderrefs/heads/<default branch>/myfolder
myrepo.git#master:myfolderrefs/heads/master/myfolder
myrepo.git#mytag:myfolderrefs/tags/mytag/myfolder
myrepo.git#mybranch:myfolderrefs/heads/mybranch/myfolder

保留 .git 目录

.git默认情况下,BuildKit在使用 Git 上下文时不保留目录。您可以通过设置BUILDKIT_CONTEXT_KEEP_GIT_DIRbuild 参数来配置 BuildKit 以保留该目录 。如果您想在构建期间检索 Git 信息,这可能很有用:

# syntax=docker/dockerfile:1
FROM alpine
WORKDIR /src
RUN --mount=target=. \
  make REVISION=$(git rev-parse HEAD) build
$ docker build \
  --build-arg BUILDKIT_CONTEXT_KEEP_GIT_DIR=1
  https://github.com/user/myrepo.git#main

私有存储库

当您指定同时也是​​私有存储库的 Git 上下文时,构建器需要您提供必要的身份验证凭据。您可以使用 SSH 或基于令牌的身份验证。

如果您指定的 Git 上下文是 SSH 或 Git 地址,Buildx 会自动检测并使用 SSH 凭据。默认情况下,这使用$SSH_AUTH_SOCK.您可以配置 SSH 凭据以与 --sshflag一起使用。

$ docker buildx build --ssh default git@github.com:user/private.git

--secret如果您想使用基于令牌的身份验证,则可以使用flag传递令牌 。

$ GIT_AUTH_TOKEN=<token> docker buildx build \
  --secret id=GIT_AUTH_TOKEN \
  https://github.com/user/private.git

笔记

不要用于--build-arg秘密。

来自标准输入的 Dockerfile 的远程上下文

使用以下语法使用本地文件系统上的文件构建映像,同时使用标准输入中的 Dockerfile。

$ docker build -f- <URL>

该语法使用 -f(或 --file)选项指定要使用的 Dockerfile,并使用连字符 (-) 作为文件名来指示 Docker 从 stdin 读取 Dockerfile。

当您想要从不包含 Dockerfile 的存储库构建映像的情况下,这可能很有用。或者,如果您想使用自定义 Dockerfile 进行构建,而不需要维护自己的存储库分支。

以下示例使用来自 stdin 的 Dockerfile 构建映像,并添加来自 GitHub 上的hello-worldhello.c存储库的文件 。

docker build -t myimage:latest -f- https://github.com/docker-library/hello-world.git <<EOF
FROM busybox
COPY hello.c ./
EOF

远程 tarball

如果将 URL 传递到远程 tarball,则 URL 本身将发送到构建器。

$ docker build http://server/context.tar.gz
#1 [internal] load remote build context
#1 DONE 0.2s

#2 copy /context /
#2 DONE 0.1s
...

下载操作将在运行 BuildKit 守护进程的主机上执行。请注意,如果您使用远程 Docker 上下文或远程构建器,则它不一定与您发出构建命令的计算机相同。 BuildKit 获取context.tar.gz并将其用作构建上下文。 Tarball 上下文必须是符合标准 Unix 格式的 tar 存档,并且可以使用、或( 无压缩)格式tar 中的任何一种进行压缩。xzbzip2gzipidentity

空上下文

当您使用文本文件作为构建上下文时,构建器会将该文件解释为 Dockerfile。使用文本文件作为上下文意味着构建没有文件系统上下文。

当您的 Dockerfile 不依赖于任何本地文件时,您可以使用空的构建上下文进行构建。

如何在没有上下文的情况下构建

您可以使用标准输入流或通过指向远程文本文件的 URL 来传递文本文件。


$ docker build - < Dockerfile
Get-Content Dockerfile | docker build -
docker build -t myimage:latest - <<EOF
FROM busybox
RUN echo "hello world"
EOF
$ docker build https://raw.githubusercontent.com/dvdksn/clockbox/main/Dockerfile

当您在没有文件系统上下文的情况下构建时,Dockerfile 指令无法 COPY引用本地文件:

$ ls
main.c
$ docker build -<<< $'FROM scratch\nCOPY main.c .'
[+] Building 0.0s (4/4) FINISHED
 => [internal] load build definition from Dockerfile       0.0s
 => => transferring dockerfile: 64B                        0.0s
 => [internal] load .dockerignore                          0.0s
 => => transferring context: 2B                            0.0s
 => [internal] load build context                          0.0s
 => => transferring context: 2B                            0.0s
 => ERROR [1/1] COPY main.c .                              0.0s
------
 > [1/1] COPY main.c .:
------
Dockerfile:2
--------------------
   1 |     FROM scratch
   2 | >>> COPY main.c .
   3 |
--------------------
ERROR: failed to solve: failed to compute cache key: failed to calculate checksum of ref 7ab2bb61-0c28-432e-abf5-a4c3440bc6b6::4lgfpdf54n5uqxnv9v6ymg7ih: "/main.c": not found

.dockerignore 文件

您可以使用.dockerignore文件从构建上下文中排除文件或目录。

# .dockerignore
node_modules
bar

这有助于避免向构建器发送不需要的文件和目录,从而提高构建速度,尤其是在使用远程构建器时。

文件名和位置

.dockerignore当您运行构建命令时,构建客户端会查找上下文根目录中命名的文件 。如果此文件存在,则在将其发送到构建器之前,将从构建上下文中删除与文件中的模式匹配的文件和目录。

如果您使用多个 Dockerfile,则可以为每个 Dockerfile 使用不同的忽略文件。您可以使用忽略文件的特殊命名约定来执行此操作。将忽略文件与 Dockerfile 放在同一目录中,并使用 Dockerfile 的名称作为忽略文件的前缀,如以下示例所示。

.
├── index.ts
├── src/
├── docker
│   ├── build.Dockerfile
│   ├── build.Dockerfile.dockerignore
│   ├── lint.Dockerfile
│   ├── lint.Dockerfile.dockerignore
│   ├── test.Dockerfile
│   └── test.Dockerfile.dockerignore
├── package.json
└── package-lock.json

如果两者都存在,则特定于 Dockerfile 的忽略文件优先于.dockerignore 构建上下文根目录中的文件。

句法

.dockerignore文件是一个以换行符分隔的模式列表,类似于 Unix shell 的文件 glob。为了匹配的目的,上下文的根被视为工作目录和根目录。例如,模式/foo/bar和都排除在位于 的 Git 存储库的子目录或根目录中foo/bar命名的文件或目录。两者都不排除其他任何东西。barfooPATHURL

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

如果您有兴趣了解模式匹配逻辑的精确细节.dockerignore ,请查看 GitHub 上的moby/patternmatcher 存储库 ,其中包含源代码。

匹配

以下代码片段显示了一个示例.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以 find 结尾的所有文件。.go

您可以使用该.dockerignore文件来排除Dockerfile.dockerignore文件。这些文件仍会发送到构建器,因为运行构建需要它们。但您无法使用 、 或绑定安装将文件复制到映像 ADDCOPY

否定匹配

您可以在行前面添加!(感叹号)以排除例外。以下是.dockerignore使用此机制的示例文件:

*.md
!README.md

上下文目录下的所有 Markdown 文件都从上下文中排除 README.md请注意,子目录下的 markdown 文件仍然包含在内。

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

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

除了 .README 文件之外,上下文中不包含任何 Markdown 文件 README-secret.md

现在考虑这个例子:

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

包含所有自述文件。中间的线没有任何作用,因为 !README*.md匹配README-secret.md并且排在最后。