構建上下文

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

進一步閱讀

有關使用命名上下文的更多資訊,請參閱: