构建上下文

docker builddocker buildx build 命令使用 Dockerfile 和上下文构建 Docker 镜像。

什么是构建上下文?

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

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

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

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

文件系统上下文

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

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

  • 当您指定本地目录或 tarball 时,所有子目录都包含在内。
  • 当您指定远程 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 作为构建上下文。请参阅Tarball

本地目录

考虑以下目录结构:

.
├── 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 从标准输入读取 Dockerfile。

以下示例使用当前目录(.)作为构建上下文,并使用通过 here-document 通过标准输入传递的 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 的根目录。以下命令使用 tarball 中的 test.Dockerfile 进行构建:

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

远程上下文

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

  • 对于 Git 仓库,构建器会自动克隆仓库。请参阅Git 仓库
  • 对于 tarball,构建器会下载并解压 tarball 的内容。请参阅Tarball

如果远程 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/<默认分支>/
myrepo.git#mytagrefs/tags/mytag/
myrepo.git#mybranchrefs/heads/mybranch/
myrepo.git#pull/42/headrefs/pull/42/head/
myrepo.git#:myfolderrefs/heads/<默认分支>/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 目录

默认情况下,BuildKit 在使用 Git 上下文时不会保留 .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 或基于令牌的身份验证。

如果指定的 Git 上下文是 SSH 或 Git 地址,Buildx 会自动检测并使用 SSH 凭据。默认情况下,这会使用 $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 从标准输入读取 Dockerfile。

这在您想从不包含 Dockerfile 的仓库构建镜像时很有用。或者如果您想使用自定义 Dockerfile 进行构建,而无需维护自己的仓库分支。

以下示例使用来自标准输入的 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

如果您将 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 上下文必须是符合标准 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

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

$ 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 文件,则 Dockerfile 特定忽略文件优先。

语法

.dockerignore 文件是与 Unix shell 文件全局模式类似的换行符分隔的模式列表。忽略模式中的前导和尾随斜杠将被忽略。以下模式都排除了构建上下文根目录下 foo 子目录中名为 bar 的文件或目录:

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

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

#/this/is/a/comment

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

匹配

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

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

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

规则行为
# 注释被忽略。
*/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

README-secret.md 之外的 README 文件,没有 Markdown 文件包含在上下文中。

现在考虑这个例子:

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

所有 README 文件都包含在内。中间行没有效果,因为 !README*.md 匹配 README-secret.md 且位于最后。

命名上下文

除了默认构建上下文(docker build 命令的位置参数)之外,您还可以向构建传递额外的命名上下文。

命名上下文使用 --build-context 标志指定,后跟名称-值对。这允许您在构建期间包含来自多个来源的文件和目录,同时保持它们在逻辑上分离。

$ docker build --build-context docs=./docs .

在此示例中:

  • 命名 docs 上下文指向 ./docs 目录。
  • 默认上下文(.)指向当前工作目录。

在 Dockerfile 中使用命名上下文

Dockerfile 指令可以像多阶段构建中的阶段一样引用命名上下文。

例如,以下 Dockerfile:

  1. 使用 COPY 指令将文件从默认上下文复制到当前构建阶段。
  2. 绑定挂载命名上下文中的文件以将文件作为构建的一部分进行处理。
# syntax=docker/dockerfile:1
FROM buildbase
WORKDIR /app

# Copy all files from the default context into /app/src in the build container
COPY . /app/src
RUN make bin

# Mount the files from the named "docs" context to build the documentation
RUN --mount=from=docs,target=/app/docs \
    make manpages

命名上下文的用例

使用命名上下文在构建 Docker 镜像时提供了更大的灵活性和效率。以下是一些使用命名上下文可能很有用的场景:

示例:组合本地和远程源

您可以为不同类型的源定义单独的命名上下文。例如,考虑一个项目,其中应用程序源代码是本地的,但部署脚本存储在 Git 仓库中:

$ docker build --build-context scripts=https://github.com/user/deployment-scripts.git .

在 Dockerfile 中,您可以独立使用这些上下文:

# syntax=docker/dockerfile:1
FROM alpine:latest

# Copy application code from the main context
COPY . /opt/app

# Run deployment scripts using the remote "scripts" context
RUN --mount=from=scripts,target=/scripts /scripts/main.sh

示例:使用自定义依赖项的动态构建

在某些情况下,您可能需要从外部源动态地将配置文件或依赖项注入构建中。命名上下文通过允许您挂载不同的配置而无需修改默认构建上下文,使此过程变得简单。

$ docker build --build-context config=./configs/prod .

示例 Dockerfile:

# syntax=docker/dockerfile:1
FROM nginx:alpine

# Use the "config" context for environment-specific configurations
COPY --from=config nginx.conf /etc/nginx/nginx.conf

示例:固定或覆盖镜像

您可以在 Dockerfile 中引用命名上下文,就像引用镜像一样。这意味着您可以通过使用同名的命名上下文来覆盖 Dockerfile 中的镜像引用。例如,给定以下 Dockerfile:

FROM alpine:3.21

如果您想强制镜像引用解析到不同版本,而无需更改 Dockerfile,您可以将同名上下文传递给构建。例如:

docker buildx build --build-context alpine:3.21=docker-image://alpine:edge .

docker-image:// 前缀将上下文标记为镜像引用。该引用可以是本地镜像或您的注册表中的镜像。

使用 Bake 的命名上下文

Bake 是内置于 docker build 中的工具,可让您通过配置文件管理构建配置。Bake 完全支持命名上下文。

在 Bake 文件中定义命名上下文:

docker-bake.hcl
target "app" {
  contexts = {
    docs = "./docs"
  }
}

这等效于以下 CLI 调用:

$ docker build --build-context docs=./docs .

将目标与命名上下文链接

除了使复杂构建更易于管理之外,Bake 还提供了在 docker build CLI 上可以执行的功能之外的附加功能。您可以使用命名上下文创建构建管道,其中一个目标依赖于另一个目标并在其之上构建。例如,考虑一个 Docker 构建设置,其中您有两个 Dockerfile:

  • base.Dockerfile:用于构建基础镜像
  • app.Dockerfile:用于构建应用程序镜像

app.Dockerfile 使用 base.Dockerfile 生成的镜像作为其基础镜像:

app.Dockerfile
FROM mybaseimage

通常,您必须首先构建基础镜像,然后将其加载到 Docker Engine 的本地镜像存储或推送到注册表。使用 Bake,您可以直接引用其他目标,从而在 app 目标和 base 目标之间创建依赖关系。

docker-bake.hcl
target "base" {
  dockerfile = "base.Dockerfile"
}

target "app" {
  dockerfile = "app.Dockerfile"
  contexts = {
    # the target: prefix indicates that 'base' is a Bake target
    mybaseimage = "target:base"
  }
}

在此配置下,app.Dockerfile 中对 mybaseimage 的引用将使用构建 base 目标的结果。如果需要,构建 app 目标也将触发 mybaseimage 的重建:

$ docker buildx bake app

进一步阅读

有关使用命名上下文的更多信息,请参阅:

© . This site is unofficial and not affiliated with Kubernetes or Docker Inc.