建置環境

`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 作為建置上下文,方法是將 tarball 內容透過管道傳輸到 `docker build` 命令。請參閱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 透過管道傳輸到 build 命令時,build 會使用 tarball 的內容作為檔案系統上下文。

例如,假設有以下專案目錄

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

您可以建立目錄的 tarball 並將其透過管道傳輸到 build 以用作上下文

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

build 會從 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
refs/tags/mytagrefs/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,則 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 並將其用作建置上下文。壓縮檔上下文必須是符合標準 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 檔案都存在,則 Dockerfile 特定的忽略檔案優先。

語法

.dockerignore 檔案是一個以換行符號分隔的模式清單,類似於 Unix shell 的檔案萬用字元。忽略模式中的前導和尾隨斜線會被忽略。以下模式都會排除建置上下文根目錄下名為 bar 的檔案或目錄在子目錄`foo`中:

  • /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 函式filepath.Clean 函式 來修剪空格並移除 ...。預處理後為空白的行將被忽略。

注意事項

由於歷史原因,模式 . 會被忽略。

除了 Go 的 filepath.Match 規則之外,Docker 還支援一個特殊的萬用字元字串 **,它可以匹配任意數量的目錄(包括零)。例如,**/*.go 會排除建置上下文中任何位置找到的所有以 .go 結尾的檔案。

您可以使用 .dockerignore 檔案來排除 Dockerfile.dockerignore 檔案。這些檔案仍然會被傳送到建置器,因為它們是執行建置所需的。但是您無法使用 ADDCOPY 或繫結掛載將檔案複製到映像中。

否定匹配

您可以在行前加上 !(驚嘆號)來例外排除。以下是一個使用此機制的 .dockerignore 檔案範例:

*.md
!README.md

除了 README.md 之外,上下文目錄正下方的所有 Markdown 檔案都會從上下文中排除。請注意,子目錄下的 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 且位於最後。