构建上下文
docker build
和 docker buildx build
命令使用 Dockerfile 和上下文构建 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 内容管道传递给 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 .
使用来自 stdin 的 Dockerfile 的本地上下文
使用以下语法通过本地文件系统上的文件构建镜像,同时使用来自 stdin 的 Dockerfile。
$ docker build -f- <PATH>
该语法使用 -f(或 --file)选项指定要使用的 Dockerfile,并使用连字符 (-) 作为文件名指示 Docker 从 stdin 读取 Dockerfile。
以下示例使用当前目录 (.) 作为构建上下文,并使用通过 here-document 从 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 根目录的名称和位置。以下命令使用 tarball 中的 test.Dockerfile
进行构建:
$ docker build --file test.Dockerfile - < foo.tar.gz
远程上下文
您可以指定远程 Git 仓库、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.git | refs/heads/<default branch> | / |
myrepo.git#mytag | refs/tags/mytag | / |
myrepo.git#mybranch | refs/heads/mybranch | / |
myrepo.git#pull/42/head | refs/pull/42/head | / |
myrepo.git#:myfolder | refs/heads/<default branch> | /myfolder |
myrepo.git#master:myfolder | refs/heads/master | /myfolder |
myrepo.git#mytag:myfolder | refs/tags/mytag | /myfolder |
myrepo.git#mybranch:myfolder | refs/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
来传递 secrets。
使用来自 stdin 的 Dockerfile 的远程上下文
使用以下语法通过本地文件系统上的文件构建镜像,同时使用来自 stdin 的 Dockerfile。
$ docker build -f- <URL>
该语法使用 -f(或 --file)选项指定要使用的 Dockerfile,并使用连字符 (-) 作为文件名指示 Docker 从 stdin 读取 Dockerfile。
当您想从不包含 Dockerfile 的仓库构建镜像时,或者当您想使用自定义 Dockerfile 而无需维护自己的仓库分支时,这会很有用。
以下示例使用来自 stdin 的 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 daemon 的主机上执行。请注意,如果您使用远程 Docker 上下文或远程构建器,这不一定与您发出构建命令的机器是同一台。BuildKit 获取 context.tar.gz
并将其用作构建上下文。Tarball 上下文必须是符合标准 tar
Unix 格式的 tar 归档文件,并且可以使用 xz
、bzip2
、gzip
或 identity
(无压缩) 格式中的任何一种进行压缩。
空上下文
当您使用文本文件作为构建上下文时,构建器会将该文件解释为 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
文件都存在,则前者优先。
语法
.dockerignore
文件是一个由换行符分隔的模式列表,类似于 Unix shell 中的文件 glob。忽略模式中的前导和尾随斜杠将被忽略。以下所有模式都排除了构建上下文根目录下 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?
此文件会导致以下构建行为:
规则 | 行为 |
---|---|
# 注释 | 被忽略。 |
*/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
文件。这些文件仍然会发送到构建器,因为它们是运行构建所必需的。但您无法使用 ADD
、COPY
或绑定挂载将这些文件复制到镜像中。
否定匹配
您可以在行前加上 !
(感叹号)来指定排除的例外。以下是一个使用此机制的 .dockerignore
示例文件:
*.md
!README.md
上下文目录正下方除了 README.md
之外的所有 markdown 文件都被排除在上下文之外。请注意,子目录下的 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
并排在最后。
命名上下文
除了默认的构建上下文(即 docker build
命令的位置参数),你还可以向构建传递附加的命名上下文。
命名上下文使用 --build-context
标志指定,后跟一个名称-值对。这允许你在构建期间包含来自多个来源的文件和目录,同时保持它们在逻辑上是分离的。
$ docker build --build-context docs=./docs .
在这个例子中
- 命名上下文
docs
指向./docs
目录。 - 默认上下文 (
.
) 指向当前工作目录。
在 Dockerfile 中使用命名上下文
Dockerfile 指令可以像引用多阶段构建中的阶段一样引用命名上下文。
例如,下面的 Dockerfile
- 使用
COPY
指令将文件从默认上下文复制到当前构建阶段。 - 绑定挂载命名上下文中的文件,以便将这些文件作为构建的一部分进行处理。
# 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 文件中定义命名上下文
target "app" {
contexts = {
docs = "./docs"
}
}
这等同于以下的命令行调用
$ docker build --build-context docs=./docs .
使用命名上下文链接目标
除了使复杂构建更易于管理之外,Bake 还提供了在 CLI 上使用 docker build
所不具备的附加功能。你可以使用命名上下文创建构建管道,其中一个目标依赖于另一个目标并在其基础上构建。例如,考虑一个你有两个 Dockerfile 的 Docker 构建设置
base.Dockerfile
:用于构建基础镜像app.Dockerfile
:用于构建应用镜像
app.Dockerfile
使用由 base.Dockerfile
产生的镜像作为其基础镜像
FROM mybaseimage
通常,你必须先构建基础镜像,然后将其加载到 Docker Engine 的本地镜像存储中或推送到仓库。使用 Bake,你可以直接引用其他目标,从而在 app
目标和 base
目标之间创建依赖关系。
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
延伸阅读
有关使用命名上下文的更多信息,请参阅