构建上下文

docker builddocker buildx build 命令从 一个 Dockerfile 和上下文构建 Docker 镜像。

什么是构建上下文?

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

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

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

  • 本地目录的相对或绝对路径
  • 一个远程URL,可以是Git仓库、压缩包或纯文本文件
  • 通过标准输入管道到 docker build 命令的纯文本文件或 tarball

文件系统上下文

当您的构建上下文是本地目录、远程Git仓库或tar文件时,这些文件将成为构建器在构建过程中可以访问的文件集合。构建指令如COPYADD可以引用上下文中的任何文件和目录。

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

  • 当你指定一个本地目录或tar包时,所有子目录都会被包含
  • 当你指定一个远程Git仓库时,仓库及其所有子模块都会被包含

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

文本文件上下文

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

欲了解更多信息,请参阅 空构建上下文

本地上下文

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

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

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

你还可以通过将本地 tarball 的内容管道到 docker build 命令来使用本地 tarball 作为构建上下文。参见 Tarballs

本地目录

考虑以下目录结构:

.
├── 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 构建镜像,使用的是 here-document。

# 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 标志指定相对于 tarball 根目录的 Dockerfile 名称和位置。以下命令使用 tarball 中的 test.Dockerfile 进行构建:

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

远程上下文

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

  • 对于 Git 仓库,构建器会自动克隆仓库。参见 Git 仓库
  • 对于 tarball,构建器会下载并提取 tarball 的内容。 参见 Tarballs

如果远程 tarball 是一个文本文件,构建器不会接收任何 文件系统 上下文,而是假设远程文件是一个 Dockerfile。参见 空构建上下文

Git 仓库

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

构建器对仓库执行浅克隆,只下载 HEAD 提交,而不是整个历史记录。

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

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

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

URL 片段

您可以将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

当你在URL片段中使用提交哈希作为ref时,请使用完整的、40个字符的SHA-1哈希字符串。不支持短哈希,例如截断为7个字符的哈希。

# ✅ The following works:
docker build github.com/docker/buildx#d4f088e689b41353d74f1a0bfcd6d7c0b213aed2
# ❌ The following doesn't work because the commit hash is truncated:
docker build github.com/docker/buildx#d4f088e

保持 .git 目录

默认情况下,使用 Git 上下文时,BuildKit 不会保留 .git 目录。 您可以通过设置 BUILDKIT_CONTEXT_KEEP_GIT_DIR 构建参数 来配置 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 或基于令牌的身份验证。

Buildx 会自动检测并使用 SSH 凭据,如果指定的 Git 上下文是 SSH 或 Git 地址。默认情况下,这使用 $SSH_AUTH_SOCK。 您可以通过 --ssh 标志 配置要使用的 SSH 凭据。

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

如果你想要使用基于令牌的身份验证,可以通过 --secret 标志传递令牌。

$ 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 构建,而无需维护仓库的自己的 fork。

以下示例使用标准输入中的 Dockerfile 构建镜像,并从 GitHub 上的 hello-world 仓库添加 hello.c 文件。

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

远程 tarball

如果你传递了一个远程 tarball 的 URL,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 上下文必须是符合标准 tar 的 Unix 格式的 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 文件通配符的以换行符分隔的模式列表。忽略模式中的前导和尾随斜杠将被忽略。以下模式都会排除构建上下文根目录下的 foo 子目录中名为 bar 的文件或目录:

  • /foo/bar/
  • /foo/bar
  • foo/bar/
  • foo/bar

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

#/this/is/a/comment

如果你对学习.dockerignore 模式匹配逻辑的精确细节感兴趣,请查看GitHub上的 moby/patternmatcher仓库 ,其中包含源代码。

匹配

以下代码片段显示了一个示例 .dockerignore 文件。

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

此文件会导致以下构建行为:

规则行为
# commentIgnored.
*/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 文件来排除 Dockerfile.dockerignore 文件。这些文件仍会被发送到构建器,因为它们对运行构建是必需的。但是您不能使用 ADDCOPY 或绑定挂载将这些文件复制到镜像中。

取消匹配

你可以用 !(感叹号)前置行,以使排除项成为例外。以下是一个使用此机制的示例 .dockerignore 文件:

*.md
!README.md

上下文目录下的所有markdown文件,除了 README.md之外,都被排除在上下文之外。请注意,子目录下的markdown文件仍然被包含。

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

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

上下文中不包含任何 markdown 文件,除了 README 文件以外,还有 README-secret.md

现在考虑这个示例:

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

所有 README 文件均已包含。中间的行没有效果,因为 !README*.md 匹配 README-secret.md 并且排在最后。