Dockerfile 參考

Docker 可以透過讀取 Dockerfile 中的指令來自動建置映像檔。Dockerfile 是一個文字文件,包含使用者可以在命令列上呼叫以組裝映像檔的所有指令。本頁面描述您可以在 Dockerfile 中使用的指令。

概覽

Dockerfile 支援以下指令:

指令描述
ADD新增本機或遠端檔案和目錄。
ARG使用建置時變數。
CMD指定預設指令。
COPY複製檔案和目錄。
ENTRYPOINT指定預設可執行檔。
ENV設定環境變數。
EXPOSE描述您的應用程式正在監聽哪些埠。
FROM從基礎映像檔建立新的建置階段。
HEALTHCHECK在啟動時檢查容器的健康狀態。
LABEL為映像檔新增中繼資料。
MAINTAINER指定映像檔的作者。
ONBUILD指定映像檔在建置中被使用時的指令。
RUN執行建置指令。
SHELL設定映像檔的預設 Shell。
STOPSIGNAL指定用於結束容器的系統呼叫訊號。
USER設定使用者和群組 ID。
VOLUME建立磁碟區掛載。
WORKDIR變更工作目錄。

格式

Dockerfile 的格式如下:

# Comment
INSTRUCTION arguments

指令不區分大小寫。然而,慣例上會使用大寫來更容易區分指令和引數。

Docker 會依序執行 Dockerfile 中的指令。Dockerfile 必須以 FROM 指令開頭。這可以在 解析器指令註解 和全域範圍的 ARG 之後。FROM 指令指定您正在建置的 基礎映像檔FROM 之前只能有一個或多個 ARG 指令,這些指令宣告了在 Dockerfile 的 FROM 行中使用的引數。

BuildKit 將以 # 開頭的行視為註解,除非該行是有效的 解析器指令。行中其他任何地方的 # 標記都被視為引數。這允許類似以下的語句:

# Comment
RUN echo 'we are running some # of cool things'

註解行會在 Dockerfile 指令執行前被移除。以下範例中的註解會在 Shell 執行 echo 命令前被移除。

RUN echo hello \
# comment
world

以下範例是等效的。

RUN echo hello \
world

註解不支援行延續字元。

注意

空白字元的注意事項

為了向後相容,註解 (#) 和指令 (例如 RUN) 前導的空白字元會被忽略,但不建議這樣做。在這些情況下,前導空白字元不會被保留,因此以下範例是等效的:

        # this is a comment-line
    RUN echo hello
RUN echo world
# this is a comment-line
RUN echo hello
RUN echo world

然而,指令引數中的空白字元不會被忽略。以下範例會依指定列印帶有前導空白字元的 hello world

RUN echo "\
     hello\
     world"

解析器指令

解析器指令是可選的,並會影響 Dockerfile 中後續行的處理方式。解析器指令不會為建置新增層,也不會顯示為建置步驟。解析器指令以特殊類型的註解形式撰寫,格式為 # directive=value。單一指令只能使用一次。

支援以下解析器指令:

一旦處理完註解、空行或建置器指令後,BuildKit 就不再尋找解析器指令。相反地,它會將任何格式為解析器指令的內容視為註解,並且不會嘗試驗證它是否可能是解析器指令。因此,所有解析器指令都必須位於 Dockerfile 的頂部。

解析器指令的鍵(例如 syntaxcheck)不區分大小寫,但慣例上使用小寫。指令的值區分大小寫,並且必須以該指令的適當大小寫形式撰寫。例如,#check=skip=jsonargsrecommended 是無效的,因為檢查名稱必須使用 Pascal 命名法,而不是小寫。慣例上,在任何解析器指令之後包含一個空行。解析器指令不支援行延續字元。

根據這些規則,以下所有範例都是無效的:

因行延續而無效

# direc \
tive=value

因出現兩次而無效

# directive=value1
# directive=value2

FROM ImageName

由於出現在建置器指令之後,被視為註解

FROM ImageName
# directive=value

由於出現在非解析器指令的註解之後,被視為註解

# About my dockerfile
# directive=value
FROM ImageName

以下 unknowndirective 被視為註解,因為它無法被識別。已知的 syntax 指令也被視為註解,因為它出現在一個非解析器指令的註解之後。

# unknowndirective=value
# syntax=value

解析器指令中允許非換行空白字元。因此,以下行都被視為相同:

#directive=value
# directive =value
#	directive= value
# directive = value
#	  dIrEcTiVe=value

語法

使用 syntax 解析器指令來宣告建置時使用的 Dockerfile 語法版本。如果未指定,BuildKit 會使用捆綁的 Dockerfile 前端版本。宣告語法版本可讓您自動使用最新的 Dockerfile 版本,而無需升級 BuildKit 或 Docker Engine,甚至可以使用自訂的 Dockerfile 實作。

大多數使用者會希望將此解析器指令設定為 docker/dockerfile:1,這會導致 BuildKit 在建置前拉取 Dockerfile 語法的最新穩定版本。

# syntax=docker/dockerfile:1

有關解析器指令如何運作的更多資訊,請參閱 自訂 Dockerfile 語法

跳脫字元

# escape=\

# escape=`

escape 指令設定在 Dockerfile 中用於跳脫字元的字元。如果未指定,預設跳脫字元是 \

跳脫字元既用於跳脫行中的字元,也用於跳脫換行符號。這允許 Dockerfile 指令跨多行。請注意,無論 Dockerfile 中是否包含 escape 解析器指令,在 RUN 命令中都不會執行跳脫,除非在行的末尾。

將跳脫字元設定為 `Windows 上特別有用,因為 \ 是目錄路徑分隔符號。`Windows PowerShell 一致。

考慮以下範例,它在 Windows 上會以不明顯的方式失敗。第二行末尾的第二個 \ 將被解釋為換行符號的跳脫字元,而不是第一個 \ 的跳脫目標。同樣地,第三行末尾的 \,如果它確實被視為指令處理,將導致其被視為行延續。這個 Dockerfile 的結果是第二行和第三行被視為單一指令:

FROM microsoft/nanoserver
COPY testfile.txt c:\\
RUN dir c:\

結果是

PS E:\myproject> docker build -t cmd .

Sending build context to Docker daemon 3.072 kB
Step 1/2 : FROM microsoft/nanoserver
 ---> 22738ff49c6d
Step 2/2 : COPY testfile.txt c:\RUN dir c:
GetFileAttributesEx c:RUN: The system cannot find the file specified.
PS E:\myproject>

上述問題的一個解決方案是使用 / 作為 COPY 指令及 dir 的目標。然而,這種語法充其量只是令人困惑,因為它對於 Windows 上的路徑來說並不自然;最糟糕的情況是容易出錯,因為並非所有 Windows 命令都支援 / 作為路徑分隔符號。

透過新增 escape 解析器指令,以下 Dockerfile 在 Windows 上使用自然平台語義的檔案路徑,並按預期成功:

# escape=`

FROM microsoft/nanoserver
COPY testfile.txt c:\
RUN dir c:\

結果是

PS E:\myproject> docker build -t succeeds --no-cache=true .

Sending build context to Docker daemon 3.072 kB
Step 1/3 : FROM microsoft/nanoserver
 ---> 22738ff49c6d
Step 2/3 : COPY testfile.txt c:\
 ---> 96655de338de
Removing intermediate container 4db9acbb1682
Step 3/3 : RUN dir c:\
 ---> Running in a2c157f842f5
 Volume in drive C has no label.
 Volume Serial Number is 7E6D-E0F7

 Directory of c:\

10/05/2016  05:04 PM             1,894 License.txt
10/05/2016  02:22 PM    <DIR>          Program Files
10/05/2016  02:14 PM    <DIR>          Program Files (x86)
10/28/2016  11:18 AM                62 testfile.txt
10/28/2016  11:20 AM    <DIR>          Users
10/28/2016  11:20 AM    <DIR>          Windows
           2 File(s)          1,956 bytes
           4 Dir(s)  21,259,096,064 bytes free
 ---> 01c7f3bef04f
Removing intermediate container a2c157f842f5
Successfully built 01c7f3bef04f
PS E:\myproject>

檢查

# check=skip=<checks|all>
# check=error=<boolean>

check 指令用於設定如何評估 建置檢查。預設情況下,所有檢查都會執行,失敗會被視為警告。

您可以使用 #check=skip=<check-name> 停用特定的檢查。若要指定多個要跳過的檢查,請用逗號分隔它們:

# check=skip=JSONArgsRecommended,StageNameCasing

若要停用所有檢查,請使用 #check=skip=all

預設情況下,即使出現警告,建置檢查失敗的建置也會以零狀態碼退出。若要在警告時使建置失敗,請設定 #check=error=true

# check=error=true
注意

使用 check 指令並啟用 error=true 選項時,建議將 Dockerfile 語法 固定到特定版本。否則,未來版本新增檢查時,您的建置可能會開始失敗。

若要結合 skiperror 選項,請使用分號分隔它們:

# check=skip=JSONArgsRecommended;error=true

若要查看所有可用的檢查,請參閱建置檢查參考。請注意,可用的檢查取決於 Dockerfile 語法版本。為確保您獲得最新的檢查,請使用 syntax 指令將 Dockerfile 語法版本指定為最新穩定版本。

環境變數替換

環境變數(透過 ENV 語句 宣告)也可以在某些指令中用作變數,供 Dockerfile 解譯。跳脫字元也用於將類似變數的語法按字面包含到語句中。

在 Dockerfile 中,環境變數以 $variable_name${variable_name} 表示。它們被視為等效,並且通常使用大括號語法來解決變數名稱沒有空白字元的問題,例如 ${foo}_bar

${variable_name} 語法也支援以下幾個標準 bash 修飾符:

  • ${variable:-word} 表示如果 variable 已設定,結果將是該值。如果 variable 未設定,則結果將是 word
  • ${variable:+word} 表示如果 variable 已設定,則結果將是 word,否則結果是空字串。

當您在 Dockerfile 中使用 # syntax=docker/dockerfile-upstream:master 語法指令時,以下變數替換在 Dockerfile 語法的預發行版本中受支援:

  • ${variable#pattern} 從字串開頭尋找,移除 variable 中與 pattern 最短匹配的部分。

    str=foobarbaz echo ${str#f*b}     # arbaz
  • ${variable##pattern} 從字串開頭尋找,移除 variable 中與 pattern 最長匹配的部分。

    str=foobarbaz echo ${str##f*b}    # az
  • ${variable%pattern} 從字串結尾向後尋找,移除 variable 中與 pattern 最短匹配的部分。

    string=foobarbaz echo ${string%b*}    # foobar
  • ${variable%%pattern} 從字串結尾向後尋找,移除 variable 中與 pattern 最長匹配的部分。

    string=foobarbaz echo ${string%%b*}   # foo
  • ${variable/pattern/replacement}variablepattern 的第一個出現替換為 replacement

    string=foobarbaz echo ${string/ba/fo}  # fooforbaz
  • ${variable//pattern/replacement}variablepattern 的所有出現替換為 replacement

    string=foobarbaz echo ${string//ba/fo}  # fooforfoz

在所有情況下,word 可以是任何字串,包括額外的環境變數。

pattern 是一個萬用字元模式,其中 ? 匹配任何單一字元,* 匹配任意數量的字元(包括零個)。若要匹配字面上的 ?*,請使用反斜線跳脫字元:\?\*

您可以透過在變數前加上 \ 來跳脫整個變數名稱:例如,\$foo\${foo} 將分別轉譯為字面上的 $foo${foo}

範例(解析後的表示會顯示在 # 之後)

FROM busybox
ENV FOO=/bar
WORKDIR ${FOO}   # WORKDIR /bar
ADD . $FOO       # ADD . /bar
COPY \$FOO /quux # COPY $FOO /quux

Dockerfile 中的以下指令列表支援環境變數:

  • ADD
  • COPY
  • ENV
  • EXPOSE
  • FROM
  • LABEL
  • STOPSIGNAL
  • USER
  • VOLUME
  • WORKDIR
  • ONBUILD (當與上述支援的指令之一結合使用時)

您也可以將環境變數與 RUNCMDENTRYPOINT 指令一起使用,但在這些情況下,變數替換是由命令 Shell 處理,而不是建置器。請注意,使用 exec 形式的指令不會自動呼叫命令 Shell。請參閱 變數替換

環境變數替換在整個指令中為每個變數使用相同的值。變更變數的值只會在後續指令中生效。考慮以下範例:

ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc
  • def 的值變為 hello
  • ghi 的值變為 bye

.dockerignore 檔案

您可以使用 .dockerignore 檔案將檔案和目錄從建置上下文中排除。如需更多資訊,請參閱 .dockerignore 檔案

Shell 形式和 Exec 形式

RUNCMDENTRYPOINT 指令都有兩種可能的格式:

  • INSTRUCTION ["可執行檔","參數1","參數2"] (exec 形式)
  • INSTRUCTION 命令 參數1 參數2 (shell 形式)

exec 形式可以避免 Shell 字串混淆,並使用特定的命令 Shell 或任何其他可執行檔來呼叫命令。它使用 JSON 陣列語法,其中陣列中的每個元素都是一個命令、旗標或引數。

Shell 形式更為寬鬆,強調易用性、彈性和可讀性。Shell 形式會自動使用命令 Shell,而 exec 形式則不會。

Exec 形式

exec 形式被解析為 JSON 陣列,這意味著您必須在單字周圍使用雙引號 ("),而不是單引號 (')。

ENTRYPOINT ["/bin/bash", "-c", "echo hello"]

exec 形式最適合用於指定 ENTRYPOINT 指令,並結合 CMD 來設定可在執行時覆寫的預設引數。如需更多資訊,請參閱 ENTRYPOINT

變數替換

使用 exec 形式不會自動呼叫命令 Shell。這意味著正常的 Shell 處理(例如變數替換)不會發生。例如,RUN [ "echo", "$HOME" ] 不會處理 $HOME 的變數替換。

如果您想要 Shell 處理,則可以使用 Shell 形式,或直接使用 exec 形式執行 Shell,例如:RUN [ "sh", "-c", "echo $HOME" ]。當使用 exec 形式並直接執行 Shell 時,就像 Shell 形式的情況一樣,是由 Shell 執行環境變數替換,而不是建置器。

反斜線

在 exec 形式中,您必須跳脫反斜線。這在 Windows 上特別相關,因為反斜線是路徑分隔符號。否則,以下這行將因為不是有效的 JSON 而被視為 Shell 形式,並以意外的方式失敗:

RUN ["c:\windows\system32\tasklist.exe"]

此範例的正確語法是

RUN ["c:\\windows\\system32\\tasklist.exe"]

Shell 形式

與 exec 形式不同,使用 Shell 形式的指令總是使用命令 Shell。Shell 形式不使用 JSON 陣列格式,而是使用常規字串。Shell 形式字串允許您使用 跳脫字元(預設為反斜線)來跳脫換行符號,以將單一指令延續到下一行。這使得處理較長的命令更容易,因為它允許您將它們分成多行。例如,考慮這兩行:

RUN source $HOME/.bashrc && \
echo $HOME

它們等效於以下這行:

RUN source $HOME/.bashrc && echo $HOME

您也可以使用內聯文件 (heredocs) 配合 Shell 形式來分解支援的命令。

RUN <<EOF
source $HOME/.bashrc && \
echo $HOME
EOF

有關內聯文件 (heredocs) 的更多資訊,請參閱 內聯文件 (Here-documents)

使用不同的 Shell

您可以使用 SHELL 命令變更預設 Shell。例如:

SHELL ["/bin/bash", "-c"]
RUN echo hello

如需更多資訊,請參閱 SHELL

FROM

FROM [--platform=<platform>] <image> [AS <name>]

FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]

FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

FROM 指令初始化一個新的建置階段,並設定後續指令的 基礎映像檔。因此,有效的 Dockerfile 必須以 FROM 指令開頭。映像檔可以是任何有效的映像檔。

  • ARG 是 Dockerfile 中唯一可以出現在 FROM 之前的指令。請參閱 理解 ARG 和 FROM 如何互動
  • FROM 可以多次出現在單一 Dockerfile 中,以建立多個映像檔或將一個建置階段用作另一個的依賴項。只需在每個新的 FROM 指令之前記下 commit 輸出的最後一個映像檔 ID 即可。每個 FROM 指令都會清除先前指令建立的任何狀態。
  • 可以透過在 FROM 指令中新增 AS name 選項,為新的建置階段指定名稱。該名稱可以在後續的 FROM <name>COPY --from=<name>RUN --mount=type=bind,from=<name> 指令中使用,以參考在此階段建置的映像檔。
  • tagdigest 值是可選的。如果您省略其中任何一個,建置器預設會假設為 latest 標籤。如果建置器找不到 tag 值,則會傳回錯誤。

可選的 --platform 旗標可用於指定映像檔的平台,以防 FROM 引用多平台映像檔。例如,linux/amd64linux/arm64windows/amd64。預設情況下,使用建置請求的目標平台。全域建置引數可用於此旗標的值中,例如 自動平台 ARG 允許您強制某個階段使用本機建置平台 (--platform=$BUILDPLATFORM),並使用它在該階段內進行交叉編譯到目標平台。

理解 ARG 和 FROM 如何互動

FROM 指令支援任何在第一個 FROM 之前出現的 ARG 指令所宣告的變數。

ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD  /code/run-app

FROM extras:${CODE_VERSION}
CMD  /code/run-extras

FROM 之前宣告的 ARG 位於建置階段之外,因此不能在 FROM 之後的任何指令中使用。若要使用在第一個 FROM 之前宣告的 ARG 的預設值,請在建置階段內使用不帶值的 ARG 指令:

ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version

RUN

RUN 指令將執行任何命令,以在當前映像檔之上建立一個新層。新增的層會用於 Dockerfile 中的下一步。RUN 有兩種形式:

# Shell form:
RUN [OPTIONS] <command> ...
# Exec form:
RUN [OPTIONS] [ "<command>", ... ]

有關這兩種形式差異的更多資訊,請參閱 Shell 形式或 Exec 形式

Shell 形式最常用,它允許您將較長的指令分解成多行,可以透過換行 跳脫字元,或者使用 內聯文件 (heredocs)

RUN <<EOF
apt-get update
apt-get install -y curl
EOF

RUN 指令可用的 [OPTIONS] 如下:

選項最低 Dockerfile 版本
--device1.14-labs
--mount1.2
--network1.3
--security1.1.2-labs

RUN 指令的快取失效

RUN 指令的快取在下次建置時不會自動失效。像 RUN apt-get dist-upgrade -y 這樣的指令的快取會在下次建置時重複使用。RUN 指令的快取可以透過使用 --no-cache 旗標來失效,例如 docker build --no-cache

如需更多資訊,請參閱 Dockerfile 最佳實踐指南

RUN 指令的快取可以透過 ADDCOPY 指令來失效。

RUN --device

注意

在穩定語法中尚未提供,請使用 docker/dockerfile:1-labs 版本。它還需要 BuildKit 0.20.0 或更高版本。

RUN --device=name,[required]

RUN --device 允許建置請求 CDI 裝置 可用於建置步驟。

警告

--device 的使用受 device 授權保護,這需要在啟動 buildkitd 守護程式時使用 --allow-insecure-entitlement device 旗標或在 buildkitd 設定 中啟用,並針對建置請求使用 --allow device 旗標

裝置 name 由在 BuildKit 中註冊的 CDI 規範提供。

在以下範例中,多個裝置已在 CDI 規範中為 vendor1.com/device 供應商註冊。

cdiVersion: "0.6.0"
kind: "vendor1.com/device"
devices:
  - name: foo
    containerEdits:
      env:
        - FOO=injected
  - name: bar
    annotations:
      org.mobyproject.buildkit.device.class: class1
    containerEdits:
      env:
        - BAR=injected
  - name: baz
    annotations:
      org.mobyproject.buildkit.device.class: class1
    containerEdits:
      env:
        - BAZ=injected
  - name: qux
    annotations:
      org.mobyproject.buildkit.device.class: class2
    containerEdits:
      env:
        - QUX=injected
annotations:
  org.mobyproject.buildkit.device.autoallow: true

裝置名稱格式靈活,並接受各種模式以支援多種裝置配置:

  • vendor1.com/device:請求為此供應商找到的第一個裝置
  • vendor1.com/device=foo:請求特定裝置
  • vendor1.com/device=*:請求此供應商的所有裝置
  • class1:根據 org.mobyproject.buildkit.device.class 註釋請求裝置
注意

自 0.6.0 起,CDI 規範支援註釋。

注意

若要自動允許 CDI 規範中註冊的所有裝置,您可以設定 org.mobyproject.buildkit.device.autoallow 註釋。您也可以為特定裝置設定此註釋。

範例:CUDA 驅動的 LLaMA 推論

在此範例中,我們使用 --device 旗標透過 CDI 使用 NVIDIA GPU 裝置執行 llama.cpp 推論

# syntax=docker/dockerfile:1-labs

FROM scratch AS model
ADD https://huggingface.tw/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/Llama-3.2-1B-Instruct-Q4_K_M.gguf /model.gguf

FROM scratch AS prompt
COPY <<EOF prompt.txt
Q: Generate  a list of 10 unique biggest countries by population in JSON with their estimated poulation in 1900 and 2024. Answer only newline formatted JSON with keys "country", "population_1900", "population_2024" with 10 items.
A:
[
    {

EOF

FROM ghcr.io/ggml-org/llama.cpp:full-cuda-b5124
RUN --device=nvidia.com/gpu=all \
    --mount=from=model,target=/models \
    --mount=from=prompt,target=/tmp \
    ./llama-cli -m /models/model.gguf -no-cnv -ngl 99 -f /tmp/prompt.txt

RUN --mount

RUN --mount=[type=<TYPE>][,option=<value>[,option=<value>]...]

RUN --mount 允許您建立建置可以存取的檔案系統掛載。這可以用於:

  • 建立到主機檔案系統或其他建置階段的綁定掛載
  • 存取建置秘密或 SSH 代理程式通訊端
  • 使用持久性套件管理快取來加速您的建置

支援的掛載類型有:

類型描述
bind (預設)綁定掛載上下文目錄(唯讀)。
快取掛載一個臨時目錄,用於編譯器和套件管理器的快取目錄。
tmpfs在建置容器中掛載一個 tmpfs
secret允許建置容器存取安全檔案,例如私鑰,而無需將它們烘焙到映像檔或建置快取中。
ssh允許建置容器透過 SSH 代理程式存取 SSH 金鑰,支援密碼。

RUN --mount=type=bind

此掛載類型允許將檔案或目錄綁定到建置容器。綁定掛載預設是唯讀的。

選項描述
targetdstdestination1掛載路徑。
來源from 中的來源路徑。預設為 from 的根目錄。
from建置階段、上下文或映像檔名稱,作為來源的根目錄。預設為建置上下文。
rw,readwrite允許在掛載上寫入。寫入的資料將會被丟棄。

RUN --mount=type=cache

此掛載類型允許建置容器為編譯器和套件管理器快取目錄。

選項描述
id用於識別獨立/不同快取的可選 ID。預設為 target 的值。
targetdstdestination1掛載路徑。
ro,readonly如果設定,則為唯讀。
共享sharedprivatelocked 之一。預設為 sharedshared 快取掛載可以由多個寫入者同時使用。如果有多個寫入者,private 會建立一個新的掛載。locked 會暫停第二個寫入者,直到第一個寫入者釋放掛載。
from建置階段、上下文或映像檔名稱,用作快取掛載的基礎。預設為空目錄。
來源from 中要掛載的子路徑。預設為 from 的根目錄。
模式新快取目錄的檔案模式(八進位)。預設為 0755
uid新快取目錄的使用者 ID。預設為 0
gid新快取目錄的群組 ID。預設為 0

快取目錄的內容在建置器呼叫之間持續存在,而不會使指令快取失效。快取掛載僅應用於提升性能。您的建置應能處理快取目錄的任何內容,因為另一個建置可能會覆寫檔案,或者如果需要更多儲存空間,GC 可能會清理它。

範例:快取 Go 套件

# syntax=docker/dockerfile:1
FROM golang
RUN --mount=type=cache,target=/root/.cache/go-build \
  go build ...

範例:快取 apt 套件

# syntax=docker/dockerfile:1
FROM ubuntu
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
  --mount=type=cache,target=/var/lib/apt,sharing=locked \
  apt update && apt-get --no-install-recommends install -y gcc

Apt 需要獨佔存取其資料,因此快取使用 sharing=locked 選項,這將確保使用相同快取掛載的多個平行建置會彼此等待,而不會同時存取相同的快取檔案。如果您希望每個建置在此情況下建立另一個快取目錄,您也可以使用 sharing=private

RUN --mount=type=tmpfs

此掛載類型允許在建置容器中掛載 tmpfs

選項描述
targetdstdestination1掛載路徑。
大小指定檔案系統大小的上限。

RUN --mount=type=secret

此掛載類型允許建置容器存取秘密值,例如權杖或私鑰,而無需將它們烘焙到映像檔中。

預設情況下,秘密會以檔案形式掛載。您也可以透過設定 env 選項,將秘密掛載為環境變數。

選項描述
id秘密的 ID。預設為目標路徑的基本名稱。
targetdstdestination將秘密掛載到指定路徑。如果未設定且 env 也未設定,則預設為 /run/secrets/ + id
環境變數將秘密掛載到環境變數而不是檔案,或兩者皆可。(自 Dockerfile v1.10.0 起)
required如果設定為 true,當秘密不可用時,指令會產生錯誤。預設為 false
模式秘密檔案的檔案模式(八進位)。預設為 0400
uid秘密檔案的使用者 ID。預設為 0
gid秘密檔案的群組 ID。預設為 0

範例:存取 S3

# syntax=docker/dockerfile:1
FROM python:3
RUN pip install awscli
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials \
  aws s3 cp s3://... ...
$ docker buildx build --secret id=aws,src=$HOME/.aws/credentials .

範例:以環境變數形式掛載

以下範例將秘密 API_KEY 掛載為同名的環境變數。

# syntax=docker/dockerfile:1
FROM alpine
RUN --mount=type=secret,id=API_KEY,env=API_KEY \
    some-command --token-from-env $API_KEY

假設 API_KEY 環境變數已在建置環境中設定,您可以使用以下命令進行建置:

$ docker buildx build --secret id=API_KEY .

RUN --mount=type=ssh

此掛載類型允許建置容器透過 SSH 代理程式存取 SSH 金鑰,支援密碼。

選項描述
idSSH 代理程式通訊端或金鑰的 ID。預設為「default」。
targetdstdestinationSSH 代理程式通訊端路徑。預設為 /run/buildkit/ssh_agent.${N}
required如果設定為 true,當金鑰不可用時,指令會產生錯誤。預設為 false
模式通訊端檔案模式(八進位)。預設為 0600
uid通訊端使用者 ID。預設為 0
gid通訊端群組 ID。預設為 0

範例:存取 GitLab

# syntax=docker/dockerfile:1
FROM alpine
RUN apk add --no-cache openssh-client
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan gitlab.com >> ~/.ssh/known_hosts
RUN --mount=type=ssh \
  ssh -q -T git@gitlab.com 2>&1 | tee /hello
# "Welcome to GitLab, @GITLAB_USERNAME_ASSOCIATED_WITH_SSHKEY" should be printed here
# with the type of build progress is defined as `plain`.
$ eval $(ssh-agent)
$ ssh-add ~/.ssh/id_rsa
(Input your passphrase here)
$ docker buildx build --ssh default=$SSH_AUTH_SOCK .

您也可以直接指定主機上的 *.pem 檔案路徑,而不是 $SSH_AUTH_SOCK。然而,不支援帶有密碼的 pem 檔案。

RUN --network

RUN --network=<TYPE>

RUN --network 允許控制命令在哪個網路環境中執行。

支援的網路類型有:

類型描述
default (預設)在預設網路中執行。
在沒有網路存取的情況下執行。
主機在主機的網路環境中執行。

RUN --network=default

等同於完全不提供旗標,命令會在建置的預設網路中執行。

RUN --network=none

命令在沒有網路存取的情況下執行(lo 仍然可用,但僅限於此程序)。

範例:隔離外部影響

# syntax=docker/dockerfile:1
FROM python:3.6
ADD mypackage.tgz wheels/
RUN --network=none pip install --find-links wheels mypackage

pip 將只能安裝 tar 檔案中提供的套件,這可以由較早的建置階段控制。

RUN --network=host

命令在主機的網路環境中執行(類似於 docker build --network=host,但基於每個指令)。

警告

--network=host 的使用受 network.host 授權保護,這需要在啟動 buildkitd 守護程式時使用 --allow-insecure-entitlement network.host 旗標或在 buildkitd 設定 中啟用,並針對建置請求使用 --allow network.host 旗標

RUN --security

注意

在穩定語法中尚未提供,請使用 docker/dockerfile:1-labs 版本。

RUN --security=<sandbox|insecure>

預設安全模式為 sandbox。使用 --security=insecure 時,建置器會在不安全模式下運行命令而不使用沙箱,這允許運行需要提升權限的流程(例如 containerd)。這等同於運行 docker run --privileged

警告

為了存取此功能,在啟動 buildkitd 守護程式時,應使用 --allow-insecure-entitlement security.insecure 旗標或在 buildkitd 設定 中啟用 security.insecure 授權,並針對建置請求使用 --allow security.insecure 旗標

預設的沙箱模式可以透過 --security=sandbox 啟動,但這是一個無操作。

範例:檢查授權

# syntax=docker/dockerfile:1-labs
FROM ubuntu
RUN --security=insecure cat /proc/self/status | grep CapEff
#84 0.093 CapEff:	0000003fffffffff

CMD

CMD 指令設定了從映像檔執行容器時要執行的命令。

您可以使用 Shell 形式或 Exec 形式 指定 CMD 指令:

  • CMD ["可執行檔","參數1","參數2"] (exec 形式)
  • CMD ["參數1","參數2"] (exec 形式,作為 ENTRYPOINT 的預設參數)
  • CMD 命令 參數1 參數2 (shell 形式)

Dockerfile 中只能有一個 CMD 指令。如果您列出多個 CMD,只有最後一個會生效。

CMD 的目的是為正在執行的容器提供預設值。這些預設值可以包含可執行檔,也可以省略可執行檔,在這種情況下,您也必須指定一個 ENTRYPOINT 指令。

如果您希望您的容器每次都運行相同的可執行檔,那麼您應該考慮將 ENTRYPOINTCMD 結合使用。請參閱 ENTRYPOINT。如果使用者向 docker run 指定引數,則這些引數將覆寫 CMD 中指定的預設值,但仍使用預設的 ENTRYPOINT

如果 CMD 用於為 ENTRYPOINT 指令提供預設引數,則 CMDENTRYPOINT 指令都應以 exec 形式 指定。

注意

不要混淆 RUNCMDRUN 實際執行命令並提交結果;CMD 不會在建置時執行任何操作,而是指定映像檔的預期命令。

LABEL

LABEL <key>=<value> [<key>=<value>...]

LABEL 指令會為映像檔新增中繼資料。LABEL 是一個鍵值對。若要在 LABEL 值中包含空格,請像在命令列解析中一樣使用引號和反斜線。以下是一些使用範例:

LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."

一個映像檔可以有多個標籤。您可以在單行中指定多個標籤。在 Docker 1.10 之前,這會減少最終映像檔的大小,但現在不再如此。您仍然可以選擇以以下兩種方式之一,在單一指令中指定多個標籤:

LABEL multi.label1="value1" multi.label2="value2" other="value3"
LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"
注意

請務必使用雙引號而不是單引號。特別是當您使用字串內插時(例如 LABEL example="foo-$ENV_VAR"),單引號會按原樣處理字串,而不會解開變數的值。

基礎映像檔中包含的標籤(FROM 行中的映像檔)會被您的映像檔繼承。如果標籤已經存在但值不同,則最近應用的值會覆寫任何先前設定的值。

若要查看映像檔的標籤,請使用 docker image inspect 命令。您可以使用 --format 選項只顯示標籤;

$ docker image inspect --format='{{json .Config.Labels}}' myimage
{
  "com.example.vendor": "ACME Incorporated",
  "com.example.label-with-value": "foo",
  "version": "1.0",
  "description": "This text illustrates that label-values can span multiple lines.",
  "multi.label1": "value1",
  "multi.label2": "value2",
  "other": "value3"
}

MAINTAINER (已淘汰)

MAINTAINER <name>

MAINTAINER 指令設定生成映像檔的「作者」欄位。LABEL 指令是此功能更靈活的版本,您應該改用它,因為它允許設定您需要的任何中繼資料,並且可以輕鬆查看,例如使用 docker inspect。若要設定與 MAINTAINER 欄位對應的標籤,您可以使用:

LABEL org.opencontainers.image.authors="SvenDowideit@home.org.au"

這將隨其他標籤一起顯示在 docker inspect 中。

EXPOSE

EXPOSE <port> [<port>/<protocol>...]

EXPOSE 指令通知 Docker,容器在執行時監聽指定的網路埠。您可以指定埠是監聽 TCP 還是 UDP,如果未指定協定,預設為 TCP。

EXPOSE 指令實際上並不會發佈埠。它作為建置映像檔者和執行容器者之間的一種文件,說明哪些埠預期會被發佈。若要在執行容器時發佈埠,請在 docker run 上使用 -p 旗標來發佈和映射一個或多個埠,或使用 -P 旗標來發佈所有公開的埠並將其映射到高序埠。

預設情況下,EXPOSE 假定為 TCP。您也可以指定 UDP:

EXPOSE 80/udp

若要同時公開 TCP 和 UDP,請包含兩行:

EXPOSE 80/tcp
EXPOSE 80/udp

在此情況下,如果您對 docker run 使用 -P,該埠將分別為 TCP 和 UDP 各公開一次。請記住,-P 會在主機上使用一個暫時性的高序主機埠,因此 TCP 和 UDP 不會使用相同的埠。

無論 EXPOSE 設定為何,您都可以透過使用 -p 旗標在執行時覆寫它們。例如:

$ docker run -p 80:80/tcp -p 80:80/udp ...

若要在主機系統上設定埠重定向,請參閱 使用 -P 旗標docker network 命令支援建立網路以供容器之間通訊,而無需公開或發佈特定埠,因為連接到網路的容器可以透過任何埠彼此通訊。如需詳細資訊,請參閱 此功能的概覽

ENV

ENV <key>=<value> [<key>=<value>...]

ENV 指令將環境變數 <key> 設定為 <value>。此值將在建置階段的所有後續指令的環境中可用,並且也可以在許多指令中內聯替換。該值將被解譯為其他環境變數,因此如果引號字元未被跳脫,它們將被移除。與命令列解析一樣,引號和反斜線可用於在值中包含空格。

範例

ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy

ENV 指令允許一次設定多個 <key>=<value> ... 變數,以下範例將在最終映像檔中產生相同的最終結果:

ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \
    MY_CAT=fluffy

使用 ENV 設定的環境變數,在從結果映像檔運行容器時將持續存在。您可以使用 docker inspect 查看這些值,並使用 docker run --env <key>=<value> 更改它們。

一個階段會繼承其父階段或任何祖先階段使用 ENV 設定的任何環境變數。如需更多資訊,請參閱手冊中的 多階段建置部分

環境變數的持久性可能會導致意外的副作用。例如,設定 ENV DEBIAN_FRONTEND=noninteractive 會改變 apt-get 的行為,並可能讓您的映像檔使用者感到困惑。

如果環境變數僅在建置期間需要,而不需要在最終映像檔中,請考慮為單一命令設定值:

RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ...

或者使用 ARG,它不會在最終映像檔中持久保存:

ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y ...
注意

替代語法

ENV 指令也允許使用替代語法 ENV <key> <value>,省略 =。例如:

ENV MY_VAR my-value

這種語法不允許在單一 ENV 指令中設定多個環境變數,並且可能令人困惑。例如,以下設定了一個環境變數 (ONE),其值為 "TWO= THREE=world"

ENV ONE TWO= THREE=world

替代語法是為了向後相容而支援的,但由於上述原因不建議使用,並可能在未來版本中移除。

ADD

ADD 有兩種形式。後者形式對於包含空白字元的路徑是必需的。

ADD [OPTIONS] <src> ... <dest>
ADD [OPTIONS] ["<src>", ... "<dest>"]

可用的 [OPTIONS] 如下:

選項最低 Dockerfile 版本
--keep-git-dir1.1
--checksum1.6
--chown
--chmod1.2
--link1.4
--exclude1.7-labs

ADD 指令會將新的檔案或目錄從 <src> 複製,並將它們新增到映像檔的檔案系統中的 <dest> 路徑。檔案和目錄可以從建置上下文、遠端 URL 或 Git 儲存庫複製。

ADDCOPY 指令在功能上相似,但用途略有不同。了解更多關於 ADDCOPY 之間的差異

來源

您可以使用 ADD 指定多個來源檔案或目錄。最後一個引數必須始終是目的地。例如,要將兩個檔案 file1.txtfile2.txt 從建置上下文新增到建置容器中的 /usr/src/things/

ADD file1.txt file2.txt /usr/src/things/

如果您指定多個來源檔案,無論是直接指定還是使用萬用字元,目的地都必須是目錄(必須以斜線 / 結尾)。

若要從遠端位置新增檔案,您可以指定 URL 或 Git 儲存庫的位址作為來源。例如:

ADD https://example.com/archive.zip /usr/src/things/
ADD git@github.com:user/repo.git /usr/src/things/

BuildKit 會偵測 <src> 的類型並相應地處理。

從建置上下文新增檔案

任何不以 http://https://git@ 協定前綴開頭的相對或本機路徑都被視為本機檔案路徑。本機檔案路徑相對於建置上下文。例如,如果建置上下文是目前目錄,ADD file.txt / 會將 ./file.txt 處的檔案新增到建置容器檔案系統的根目錄。

指定帶有前導斜線或導航到建置上下文之外的來源路徑(例如 ADD ../something /something)會自動移除任何父目錄導航 (../)。來源路徑中的尾隨斜線也會被忽略,使得 ADD something/ /something 等效於 ADD something /something

如果來源是目錄,則目錄的內容會被複製,包括檔案系統中繼資料。目錄本身不會被複製,只有其內容會被複製。如果它包含子目錄,這些子目錄也會被複製,並與目的地現有的任何目錄合併。任何衝突都會以新增的內容為優先,以檔案為單位解決,但如果您嘗試將目錄複製到現有檔案上,則會引發錯誤。

如果來源是檔案,則該檔案及其中繼資料會複製到目的地。檔案權限會被保留。如果來源是檔案,且目的地存在同名目錄,則會引發錯誤。

如果您透過 stdin 將 Dockerfile 傳遞給建置 (docker build - < Dockerfile),則沒有建置上下文。在此情況下,您只能使用 ADD 指令來複製遠端檔案。您也可以透過 stdin 傳遞 tar 存檔 (docker build - < archive.tar),存檔根目錄下的 Dockerfile 和存檔的其餘部分將用作建置的上下文。

模式匹配

對於本機檔案,每個 <src> 可能包含萬用字元,匹配將使用 Go 的 filepath.Match 規則完成。

例如,要在建置上下文的根目錄中新增所有以 .png 結尾的檔案和目錄:

ADD *.png /dest/

在以下範例中,? 是單字元萬用字元,匹配例如 index.jsindex.ts

ADD index.?s /dest/

新增包含特殊字元(例如 [])的檔案或目錄時,您需要依照 Golang 規則跳脫這些路徑,以防止它們被視為匹配模式。例如,要新增名為 arr[0].txt 的檔案,請使用以下方法:

ADD arr[[]0].txt /dest/

新增本機 tar 存檔

當使用本機 tar 存檔作為 ADD 的來源,並且該存檔採用可識別的壓縮格式(gzipbzip2xz,或未壓縮)時,該存檔會被解壓縮並提取到指定目的地。只有本機 tar 存檔會被提取。如果 tar 存檔是遠端 URL,則該存檔不會被提取,而是下載並放置在目的地。

當目錄被提取時,其行為與 tar -x 相同。結果是以下兩者的聯集:

  1. 目的地路徑上已有的任何內容,以及
  2. 來源樹的內容,衝突會以新增的內容為優先,以檔案為單位解決。
注意

檔案是否被識別為已識別的壓縮格式,完全是根據檔案的內容而非檔案名稱來判斷。例如,如果一個空檔碰巧以 .tar.gz 結尾,這不會被識別為壓縮檔,也不會產生任何解壓縮錯誤訊息,而是該檔案將會直接複製到目的地。

從 URL 新增檔案

如果來源是遠端檔案 URL,則目的地的權限將為 600。如果 HTTP 回應包含 Last-Modified 標頭,則該標頭中的時間戳將用於設定目的地檔案的 mtime。然而,就像在 ADD 期間處理的任何其他檔案一樣,mtime 不會納入判斷檔案是否已變更以及是否應更新快取的考量。

如果目的地以尾隨斜線結尾,則檔名將從 URL 路徑推斷。例如,ADD http://example.com/foobar / 將建立檔案 /foobar。URL 必須具有非平凡路徑,以便可以找到適當的檔名 (http://example.com 無效)。

如果目的地不以斜線結尾,則目的地路徑將成為從 URL 下載檔案的檔名。例如,ADD http://example.com/foo /bar 會建立檔案 /bar

如果您的 URL 檔案受到認證保護,您需要使用 RUN wgetRUN curl 或從容器內部使用其他工具,因為 ADD 指令不支援認證。

從 Git 儲存庫新增檔案

若要將 Git 儲存庫用作 ADD 的來源,您可以將儲存庫的 HTTP 或 SSH 位址作為來源。儲存庫會複製到映像檔中指定的目的地。

ADD https://github.com/user/repo.git /mydir/

您可以使用 URL 片段來指定特定的分支、標籤、提交或子目錄。例如,要新增 buildkit 儲存庫 v0.14.1 標籤的 docs 目錄:

ADD git@github.com:moby/buildkit.git#v0.14.1:docs /buildkit-docs

有關 Git URL 片段的更多資訊,請參閱 URL 片段

從 Git 儲存庫新增時,檔案的權限位元為 644。如果儲存庫中的檔案設定了可執行位元,則其權限將設定為 755。目錄的權限設定為 755。

當使用 Git 儲存庫作為來源時,儲存庫必須可從建置上下文存取。若要透過 SSH 新增儲存庫,無論是公共還是私人儲存庫,您都必須傳遞 SSH 金鑰進行認證。例如,給定以下 Dockerfile:

# syntax=docker/dockerfile:1
FROM alpine
ADD git@git.example.com:foo/bar.git /bar

要建置此 Dockerfile,請將 --ssh 旗標傳遞給 docker build 以將 SSH 代理程式通訊端掛載到建置中。例如:

$ docker build --ssh default .

有關使用秘密進行建置的更多資訊,請參閱 建置秘密

目的地

如果目的地路徑以正斜線開頭,則它被解釋為絕對路徑,並且來源檔案會相對於目前建置階段的根目錄複製到指定目的地。

# create /abs/test.txt
ADD test.txt /abs/

尾隨斜線很重要。例如,ADD test.txt /abs 會在 /abs 建立一個檔案,而 ADD test.txt /abs/ 會建立 /abs/test.txt

如果目的地路徑不以開頭斜線開始,則它會被解釋為相對於建置容器的工作目錄。

WORKDIR /usr/src/app
# create /usr/src/app/rel/test.txt
ADD test.txt rel/

如果目的地不存在,它將與其路徑中所有缺失的目錄一起被建立。

如果來源是檔案,且目的地不以尾隨斜線結尾,則來源檔案將作為檔案寫入目的地路徑。

ADD --keep-git-dir

ADD [--keep-git-dir=<boolean>] <src> ... <dir>

<src> 是遠端 Git 儲存庫的 HTTP 或 SSH 位址時,BuildKit 預設會將 Git 儲存庫的內容新增到映像檔中,但不包括 .git 目錄。

--keep-git-dir=true 旗標可讓您保留 .git 目錄。

# syntax=docker/dockerfile:1
FROM alpine
ADD --keep-git-dir=true https://github.com/moby/buildkit.git#v0.10.1 /buildkit

ADD --checksum

ADD [--checksum=<hash>] <src> ... <dir>

--checksum 旗標可讓您驗證遠端資源的校驗和。校驗和格式為 sha256:<hash>。SHA-256 是唯一支援的雜湊演算法。

ADD --checksum=sha256:24454f830cdb571e2c4ad15481119c43b3cafd48dd869a9b2945d1036d1dc68d https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/linux-0.01.tar.gz /

--checksum 旗標僅支援 HTTP(S) 來源。

ADD --chown --chmod

請參閱 COPY --chown --chmod

請參閱 COPY --link

ADD --exclude

請參閱 COPY --exclude

COPY

COPY 有兩種形式。後者形式對於包含空白字元的路徑是必需的。

COPY [OPTIONS] <src> ... <dest>
COPY [OPTIONS] ["<src>", ... "<dest>"]

可用的 [OPTIONS] 如下:

選項最低 Dockerfile 版本
--from
--chown
--chmod1.2
--link1.4
--parents1.7-labs
--exclude1.7-labs

COPY 指令會將新的檔案或目錄從 <src> 複製,並將它們新增到映像檔的檔案系統中的 <dest> 路徑。檔案和目錄可以從建置上下文、建置階段、命名上下文或映像檔複製。

ADDCOPY 指令在功能上相似,但用途略有不同。了解更多關於 ADDCOPY 之間的差異

來源

您可以使用 COPY 指定多個來源檔案或目錄。最後一個引數必須始終是目的地。例如,要將兩個檔案 file1.txtfile2.txt 從建置上下文複製到建置容器中的 /usr/src/things/

COPY file1.txt file2.txt /usr/src/things/

如果您指定多個來源檔案,無論是直接指定還是使用萬用字元,目的地都必須是目錄(必須以斜線 / 結尾)。

COPY 接受 --from=<name> 旗標,讓您可以指定來源位置為建置階段、上下文或映像檔。以下範例從名為 build 的階段複製檔案:

FROM golang AS build
WORKDIR /app
RUN --mount=type=bind,target=. go build -o /myapp ./cmd

COPY --from=build /myapp /usr/bin/

有關從命名來源複製的更多資訊,請參閱 --from 旗標

從建置上下文複製

從建置上下文複製來源檔案時,路徑會被解釋為相對於上下文的根目錄。

指定帶有前導斜線或導航到建置上下文之外的來源路徑(例如 COPY ../something /something)會自動移除任何父目錄導航 (../)。來源路徑中的尾隨斜線也會被忽略,使得 COPY something/ /something 等效於 COPY something /something

如果來源是目錄,則目錄的內容會被複製,包括檔案系統中繼資料。目錄本身不會被複製,只有其內容會被複製。如果它包含子目錄,這些子目錄也會被複製,並與目的地現有的任何目錄合併。任何衝突都會以新增的內容為優先,以檔案為單位解決,但如果您嘗試將目錄複製到現有檔案上,則會引發錯誤。

如果來源是檔案,則該檔案及其中繼資料會複製到目的地。檔案權限會被保留。如果來源是檔案,且目的地存在同名目錄,則會引發錯誤。

如果您透過 stdin 將 Dockerfile 傳遞給建置 (docker build - < Dockerfile),則沒有建置上下文。在此情況下,您只能使用 COPY 指令透過 --from 旗標 從其他階段、命名上下文或映像檔複製檔案。您也可以透過 stdin 傳遞 tar 存檔 (docker build - < archive.tar),存檔根目錄下的 Dockerfile 和存檔的其餘部分將用作建置的上下文。

當使用 Git 儲存庫作為建置上下文時,複製檔案的權限位元為 644。如果儲存庫中的檔案設定了可執行位元,則其權限將設定為 755。目錄的權限設定為 755。

模式匹配

對於本機檔案,每個 <src> 可能包含萬用字元,匹配將使用 Go 的 filepath.Match 規則完成。

例如,要在建置上下文的根目錄中新增所有以 .png 結尾的檔案和目錄:

COPY *.png /dest/

在以下範例中,? 是單字元萬用字元,匹配例如 index.jsindex.ts

COPY index.?s /dest/

新增包含特殊字元(例如 [])的檔案或目錄時,您需要依照 Golang 規則跳脫這些路徑,以防止它們被視為匹配模式。例如,要新增名為 arr[0].txt 的檔案,請使用以下方法:

COPY arr[[]0].txt /dest/

目的地

如果目的地路徑以正斜線開頭,則它被解釋為絕對路徑,並且來源檔案會相對於目前建置階段的根目錄複製到指定目的地。

# create /abs/test.txt
COPY test.txt /abs/

尾隨斜線很重要。例如,COPY test.txt /abs 會在 /abs 建立一個檔案,而 COPY test.txt /abs/ 會建立 /abs/test.txt

如果目的地路徑不以開頭斜線開始,則它會被解釋為相對於建置容器的工作目錄。

WORKDIR /usr/src/app
# create /usr/src/app/rel/test.txt
COPY test.txt rel/

如果目的地不存在,它將與其路徑中所有缺失的目錄一起被建立。

如果來源是檔案,且目的地不以尾隨斜線結尾,則來源檔案將作為檔案寫入目的地路徑。

COPY --from

預設情況下,COPY 指令會從建置上下文複製檔案。COPY --from 旗標可讓您改從映像檔、建置階段或命名上下文複製檔案。

COPY [--from=<image|stage|context>] <src> ... <dest>

若要從 多階段建置 中的建置階段複製,請指定您要複製的階段名稱。您可以使用 FROM 指令中的 AS 關鍵字來指定階段名稱。

# syntax=docker/dockerfile:1
FROM alpine AS build
COPY . .
RUN apk add clang
RUN clang -o /hello hello.c

FROM scratch
COPY --from=build /hello /

您也可以直接從命名上下文(使用 --build-context <name>=<source> 指定)或映像檔複製檔案。以下範例從官方 Nginx 映像檔複製 nginx.conf 檔案。

COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf

COPY --from 的來源路徑始終是從您指定的映像檔或階段的檔案系統根目錄解析的。

COPY --chown --chmod

注意

目前僅支援八進位表示法。非八進位支援在 moby/buildkit#1951 中追蹤。

COPY [--chown=<user>:<group>] [--chmod=<perms> ...] <src> ... <dest>

--chown--chmod 功能僅支援用於建置 Linux 容器的 Dockerfile,不適用於 Windows 容器。由於使用者和群組所有權的概念在 Linux 和 Windows 之間無法轉換,因此使用 /etc/passwd/etc/group 將使用者和群組名稱轉換為 ID,將此功能限制為僅適用於基於 Linux 作業系統的容器。

從建置上下文複製的所有檔案和目錄都以 UID 和 GID 為 0 建立,除非可選的 --chown 旗標指定了給定的使用者名稱、群組名稱或 UID/GID 組合,以請求複製內容的特定所有權。--chown 旗標的格式允許使用者名稱和群組名稱字串或直接的整數 UID 和 GID 的任意組合。提供使用者名稱而沒有群組名稱,或提供 UID 而沒有 GID,將使用與 GID 相同的數字 UID。如果提供了使用者名稱或群組名稱,容器的根檔案系統 /etc/passwd/etc/group 檔案將分別用於將名稱轉換為整數 UID 或 GID。以下範例顯示了 --chown 旗標的有效定義:

COPY --chown=55:mygroup files* /somedir/
COPY --chown=bin files* /somedir/
COPY --chown=1 files* /somedir/
COPY --chown=10:11 files* /somedir/
COPY --chown=myuser:mygroup --chmod=644 files* /somedir/

如果容器根檔案系統不包含 /etc/passwd/etc/group 檔案,並且在 --chown 旗標中使用了使用者或群組名稱,則建置將在 COPY 操作中失敗。使用數字 ID 無需查詢,也不依賴於容器根檔案系統內容。

使用 Dockerfile 語法版本 1.10.0 及更高版本,--chmod 旗標支援變數內插,讓您可以使用建置引數定義權限位元:

# syntax=docker/dockerfile:1.10
FROM alpine
WORKDIR /src
ARG MODE=440
COPY --chmod=$MODE . .
COPY [--link[=<boolean>]] <src> ... <dest>

COPYADD 命令中啟用此旗標,您可以複製具有增強語義的檔案,您的檔案將獨立存在於其自身的層中,並且當先前層上的命令更改時不會失效。

當使用 --link 時,您的原始檔案會被複製到一個空的目標目錄中。該目錄會變成一個層,連結在您先前的狀態之上。

# syntax=docker/dockerfile:1
FROM alpine
COPY --link /foo /bar

相當於執行兩次建置

FROM alpine

以及

FROM scratch
COPY /foo /bar

並將兩個映像檔的所有層合併在一起。

使用 --link 可在後續搭配 --cache-from 的建置中重複使用已建置的層,即使先前的層已更改。這對於多階段建置尤其重要,在多階段建置中,如果同一階段中的任何先前命令發生更改,COPY --from 語句先前會失效,導致需要重新建置中間階段。使用 --link,先前建置產生的層會被重複使用並合併到新層之上。這也意味著當基礎映像檔接收更新時,您可以輕鬆地重新設定(rebase)您的映像檔,而無需再次執行整個建置。在支援它的後端中,BuildKit 無需在客戶端和註冊表之間推送或拉取任何層即可執行此重新設定操作。BuildKit 將偵測到這種情況,並只創建包含新層和舊層的正確順序的新映像檔清單。

當使用 --link 且沒有其他需要存取基礎映像檔中檔案的命令時,BuildKit 也可以避免拉取基礎映像檔,這種行為也會發生。在這種情況下,BuildKit 將只為 COPY 命令建置層,並將它們直接推送到註冊表,放置在基礎映像檔的層之上。

--link=false 的不相容性

當使用 --link 時,COPY/ADD 命令不允許從先前狀態讀取任何檔案。這意味著如果在先前狀態下,目標目錄是一個包含符號連結的路徑,COPY/ADD 無法追蹤它。在最終映像檔中,使用 --link 創建的目標路徑將永遠是一個只包含目錄的路徑。

如果您不依賴於在目標路徑中追蹤符號連結的行為,始終建議使用 --link--link 的性能與預設行為相同或更好,並且它為快取重用創造了更好的條件。

COPY --parents

注意

在穩定語法中尚未提供,請使用 docker/dockerfile:1-labs 版本。

COPY [--parents[=<boolean>]] <src> ... <dest>

--parents 旗標會保留 src 條目的父目錄。此旗標預設為 false

# syntax=docker/dockerfile:1-labs
FROM scratch

COPY ./x/a.txt ./y/a.txt /no_parents/
COPY --parents ./x/a.txt ./y/a.txt /parents/

# /no_parents/a.txt
# /parents/x/a.txt
# /parents/y/a.txt

此行為類似於 Linux cp 工具的 --parents 旗標或 rsync --relative 旗標。

如同 Rsync,可以透過在來源路徑中插入一個點和一個斜線(./)來限制保留哪些父目錄。如果存在這樣一個點,則只會保留其後的父目錄。這對於使用 --from 在不同階段之間複製特別有用,因為此時來源路徑需要是絕對路徑。

# syntax=docker/dockerfile:1-labs
FROM scratch

COPY --parents ./x/./y/*.txt /parents/

# Build context:
# ./x/y/a.txt
# ./x/y/b.txt
#
# Output:
# /parents/y/a.txt
# /parents/y/b.txt

請注意,如果未指定 --parents 旗標,任何檔名衝突都會導致 Linux cp 操作失敗並顯示明確的錯誤訊息(cp: will not overwrite just-created './x/a.txt' with './y/a.txt'),而 Buildkit 將會靜默地覆寫目標位置的目標檔案。

雖然可以為僅包含一個 src 條目的 COPY 指令保留目錄結構,但通常來說,盡可能減少最終映像檔中的層數更為有利。因此,透過 --parents 旗標,Buildkit 能夠將多個 COPY 指令打包在一起,同時保持目錄結構完整。

COPY --exclude

注意

在穩定語法中尚未提供,請使用 docker/dockerfile:1-labs 版本。

COPY [--exclude=<path> ...] <src> ... <dest>

--exclude 旗標可讓您指定要排除的檔案路徑表達式。

路徑表達式遵循與 <src> 相同的格式,支援萬用字元並使用 Go 的 filepath.Match 規則進行匹配。例如,要新增所有以「hom」開頭的檔案,但不包括副檔名為 .txt 的檔案

# syntax=docker/dockerfile:1-labs
FROM scratch

COPY --exclude=*.txt hom* /mydir/

您可以為一個 COPY 指令多次指定 --exclude 選項。多個 --excludes 表示符合其模式的檔案不會被複製,即使這些檔案路徑符合 <src> 中指定的模式。要新增所有以「hom」開頭的檔案,但不包括副檔名為 .txt.md 的檔案

# syntax=docker/dockerfile:1-labs
FROM scratch

COPY --exclude=*.txt --exclude=*.md hom* /mydir/

ENTRYPOINT

ENTRYPOINT 允許您將容器配置為可執行檔。

ENTRYPOINT 有兩種可能的格式

  • exec 格式,這是首選格式

    ENTRYPOINT ["executable", "param1", "param2"]
  • shell 格式

    ENTRYPOINT command param1 param2

有關不同格式的更多資訊,請參閱 Shell 和 exec 格式

以下命令會從 nginx 啟動一個容器,使用其預設內容,並監聽埠 80

$ docker run -i -t --rm -p 80:80 nginx

傳給 docker run <image> 的命令列參數將附加到 exec 格式 ENTRYPOINT 中的所有元素之後,並會覆寫使用 CMD 指定的所有元素。

這允許將參數傳遞給進入點,例如,docker run <image> -d 會將 -d 參數傳遞給進入點。您可以使用 docker run --entrypoint 旗標來覆寫 ENTRYPOINT 指令。

ENTRYPOINT 的 shell 格式會阻止使用任何 CMD 命令列參數。它還會將您的 ENTRYPOINT 作為 /bin/sh -c 的子命令啟動,這不會傳遞信號。這表示可執行檔不會是容器的 PID 1,也不會接收 Unix 信號。在這種情況下,您的可執行檔不會從 docker stop <container> 接收到 SIGTERM

Dockerfile 中只有最後一個 ENTRYPOINT 指令才會生效。

Exec 形式 ENTRYPOINT 範例

您可以使用 ENTRYPOINT 的 exec 格式來設定相當穩定的預設命令和參數,然後使用 CMD 的任何一種格式來設定更可能更改的其他預設值。

FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

當您執行容器時,您會看到 top 是唯一一個行程

$ docker run -it --rm --name test  top -H

top - 08:25:00 up  7:27,  0 users,  load average: 0.00, 0.01, 0.05
Threads:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.1 us,  0.1 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:   2056668 total,  1616832 used,   439836 free,    99352 buffers
KiB Swap:  1441840 total,        0 used,  1441840 free.  1324440 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
    1 root      20   0   19744   2336   2080 R  0.0  0.1   0:00.04 top

若要進一步檢查結果,您可以使用 docker exec

$ docker exec -it test ps aux

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  2.6  0.1  19752  2352 ?        Ss+  08:24   0:00 top -b -H
root         7  0.0  0.1  15572  2164 ?        R+   08:25   0:00 ps aux

您可以使用 docker stop test 來優雅地請求 top 關閉。

以下 Dockerfile 顯示如何使用 ENTRYPOINT 在前景執行 Apache(即,作為 PID 1

FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

如果您需要為單一可執行檔編寫啟動腳本,可以透過使用 execgosu 命令來確保最終的可執行檔接收到 Unix 信號

#!/usr/bin/env bash
set -e

if [ "$1" = 'postgres' ]; then
    chown -R postgres "$PGDATA"

    if [ -z "$(ls -A "$PGDATA")" ]; then
        gosu postgres initdb
    fi

    exec gosu postgres "$@"
fi

exec "$@"

最後,如果您需要在關機時進行額外清理(或與其他容器通訊),或者協調多個可執行檔,您可能需要確保 ENTRYPOINT 腳本接收到 Unix 信號,將它們傳遞下去,然後執行更多工作

#!/bin/sh
# Note: I've written this using sh so it works in the busybox container too

# USE the trap if you need to also do manual cleanup after the service is stopped,
#     or need to start multiple services in the one container
trap "echo TRAPed signal" HUP INT QUIT TERM

# start service in background here
/usr/sbin/apachectl start

echo "[hit enter key to exit] or run 'docker stop <container>'"
read

# stop service and clean up here
echo "stopping apache"
/usr/sbin/apachectl stop

echo "exited $0"

如果您使用 docker run -it --rm -p 80:80 --name test apache 執行此映像檔,您隨後可以使用 docker execdocker top 檢查容器的行程,然後要求腳本停止 Apache

$ docker exec -it test ps aux

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.1  0.0   4448   692 ?        Ss+  00:42   0:00 /bin/sh /run.sh 123 cmd cmd2
root        19  0.0  0.2  71304  4440 ?        Ss   00:42   0:00 /usr/sbin/apache2 -k start
www-data    20  0.2  0.2 360468  6004 ?        Sl   00:42   0:00 /usr/sbin/apache2 -k start
www-data    21  0.2  0.2 360468  6000 ?        Sl   00:42   0:00 /usr/sbin/apache2 -k start
root        81  0.0  0.1  15572  2140 ?        R+   00:44   0:00 ps aux

$ docker top test

PID                 USER                COMMAND
10035               root                {run.sh} /bin/sh /run.sh 123 cmd cmd2
10054               root                /usr/sbin/apache2 -k start
10055               33                  /usr/sbin/apache2 -k start
10056               33                  /usr/sbin/apache2 -k start

$ /usr/bin/time docker stop test

test
real	0m 0.27s
user	0m 0.03s
sys	0m 0.03s
注意

您可以使用 --entrypoint 覆寫 ENTRYPOINT 設定,但這只能設定要執行的二進位檔(不會使用 sh -c)。

Shell 形式 ENTRYPOINT 範例

您可以為 ENTRYPOINT 指定一個純字串,它將在 /bin/sh -c 中執行。這種形式將使用 shell 處理來替換 shell 環境變數,並且會忽略任何 CMDdocker run 命令列參數。為了確保 docker stop 能正確地向任何長時間執行的 ENTRYPOINT 可執行檔發出信號,您需要記住以 exec 開頭啟動它

FROM ubuntu
ENTRYPOINT exec top -b

當您執行此映像檔時,您將看到單一的 PID 1 行程

$ docker run -it --rm --name test top

Mem: 1704520K used, 352148K free, 0K shrd, 0K buff, 140368121167873K cached
CPU:   5% usr   0% sys   0% nic  94% idle   0% io   0% irq   0% sirq
Load average: 0.08 0.03 0.05 2/98 6
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
    1     0 root     R     3164   0%   0% top -b

docker stop 時乾淨地退出

$ /usr/bin/time docker stop test

test
real	0m 0.20s
user	0m 0.02s
sys	0m 0.04s

如果您忘記在 ENTRYPOINT 的開頭新增 exec

FROM ubuntu
ENTRYPOINT top -b
CMD -- --ignored-param1

然後您可以執行它(為下一步驟命名)

$ docker run -it --name test top --ignored-param2

top - 13:58:24 up 17 min,  0 users,  load average: 0.00, 0.00, 0.00
Tasks:   2 total,   1 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s): 16.7 us, 33.3 sy,  0.0 ni, 50.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   1990.8 total,   1354.6 free,    231.4 used,    404.7 buff/cache
MiB Swap:   1024.0 total,   1024.0 free,      0.0 used.   1639.8 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
    1 root      20   0    2612    604    536 S   0.0   0.0   0:00.02 sh
    6 root      20   0    5956   3188   2768 R   0.0   0.2   0:00.00 top

top 的輸出中您可以看到,指定的 ENTRYPOINT 不是 PID 1

如果您隨後執行 docker stop test,容器將不會乾淨地退出 - stop 命令將在逾時後被迫發送 SIGKILL

$ docker exec -it test ps waux

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.4  0.0   2612   604 pts/0    Ss+  13:58   0:00 /bin/sh -c top -b --ignored-param2
root         6  0.0  0.1   5956  3188 pts/0    S+   13:58   0:00 top -b
root         7  0.0  0.1   5884  2816 pts/1    Rs+  13:58   0:00 ps waux

$ /usr/bin/time docker stop test

test
real	0m 10.19s
user	0m 0.04s
sys	0m 0.03s

理解 CMD 和 ENTRYPOINT 如何互動

CMDENTRYPOINT 指令都定義了在執行容器時將執行的命令。有一些規則描述了它們的協同作用。

  1. Dockerfile 應至少指定一個 CMDENTRYPOINT 命令。

  2. 當將容器用作可執行檔時,應定義 ENTRYPOINT

  3. CMD 應用於為 ENTRYPOINT 命令定義預設參數,或用於在容器中執行臨時命令。

  4. 當使用其他參數執行容器時,CMD 將會被覆寫。

下表顯示了不同 ENTRYPOINT / CMD 組合所執行的命令

無 ENTRYPOINTENTRYPOINT exec_entry p1_entryENTRYPOINT ["exec_entry", "p1_entry"]
無 CMD錯誤,不允許/bin/sh -c exec_entry p1_entryexec_entry p1_entry
CMD ["exec_cmd", "p1_cmd"]exec_cmd p1_cmd/bin/sh -c exec_entry p1_entryexec_entry p1_entry exec_cmd p1_cmd
CMD exec_cmd p1_cmd/bin/sh -c exec_cmd p1_cmd/bin/sh -c exec_entry p1_entryexec_entry p1_entry /bin/sh -c exec_cmd p1_cmd
注意

如果 CMD 是從基礎映像檔中定義的,設定 ENTRYPOINT 將會把 CMD 重設為空值。在這種情況下,CMD 必須在目前的映像檔中定義才能有值。

VOLUME

VOLUME ["/data"]

VOLUME 指令會創建一個具有指定名稱的掛載點,並將其標記為從本機主機或其他容器掛載的外部卷。該值可以是 JSON 陣列,例如 VOLUME ["/var/log/"],或是一個帶有多個參數的純字串,例如 VOLUME /var/logVOLUME /var/log /var/db。有關更多資訊/範例以及透過 Docker 客戶端進行掛載的說明,請參閱 透過卷共享目錄 文件。

docker run 命令會使用基礎映像檔中指定位置存在的任何資料來初始化新創建的卷。例如,考慮以下 Dockerfile 片段

FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol

此 Dockerfile 會產生一個映像檔,使得 docker run/myvol 處創建一個新的掛載點,並將 greeting 檔案複製到新創建的卷中。

關於指定磁碟區的注意事項

請記住 Dockerfile 中關於卷的以下事項。

  • Windows 容器上的卷:當使用基於 Windows 的容器時,容器內部卷的目標必須是以下之一

    • 一個不存在或空的目錄
    • 除了 C: 之外的磁碟機
  • 從 Dockerfile 內部更改卷:如果在宣告卷之後,任何建置步驟更改了卷內的資料,使用舊版建置器時這些更改將會被捨棄。使用 Buildkit 時,這些更改將會被保留。

  • JSON 格式:列表會被解析為 JSON 陣列。您必須使用雙引號(")而非單引號(')將單字括起來。

  • 主機目錄在容器執行時宣告:主機目錄(掛載點)本質上是與主機相關的。這是為了保留映像檔的可攜性,因為無法保證給定的主機目錄在所有主機上都可用。因此,您無法從 Dockerfile 內部掛載主機目錄。VOLUME 指令不支援指定 host-dir 參數。您必須在創建或執行容器時指定掛載點。

USER

USER <user>[:<group>]

USER <UID>[:<GID>]

USER 指令設定使用者名稱(或 UID)以及可選的使用者群組(或 GID),以作為目前階段其餘部分的預設使用者和群組。指定的用戶用於 RUN 指令,並在運行時執行相關的 ENTRYPOINTCMD 命令。

請注意,當為使用者指定群組時,該使用者將具有指定的群組成員資格。任何其他配置的群組成員資格將被忽略。

警告

當使用者沒有主要群組時,映像檔(或下一個指令)將以 root 群組執行。

在 Windows 上,如果使用者不是內建帳戶,則必須先創建。這可以透過在 Dockerfile 中呼叫 net user 命令來完成。

FROM microsoft/windowsservercore
# Create Windows user in the container
RUN net user /add patrick
# Set it for subsequent commands
USER patrick

WORKDIR

WORKDIR /path/to/workdir

WORKDIR 指令設定 Dockerfile 中其後所有 RUNCMDENTRYPOINTCOPYADD 指令的工作目錄。如果 WORKDIR 不存在,即使在任何後續的 Dockerfile 指令中未使用,它也會被創建。

WORKDIR 指令可以在 Dockerfile 中多次使用。如果提供的是相對路徑,它將相對於前一個 WORKDIR 指令的路徑。例如

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

此 Dockerfile 中最終 pwd 命令的輸出將是 /a/b/c

WORKDIR 指令可以解析先前使用 ENV 設定的環境變數。您只能使用在 Dockerfile 中明確設定的環境變數。例如

ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

此 Dockerfile 中最終 pwd 命令的輸出將是 /path/$DIRNAME

如果未指定,預設工作目錄為 /。實際上,如果您不是從頭開始建置 Dockerfile (FROM scratch),您使用的基礎映像檔很可能會設定 WORKDIR

因此,為了避免在未知目錄中進行意外操作,最佳實踐是明確設定您的 WORKDIR

ARG

ARG <name>[=<default value>] [<name>[=<default value>]...]

ARG 指令定義了一個變數,使用者可以在建置時透過 docker build 命令並使用 --build-arg <varname>=<value> 旗標將其傳遞給建置器。

警告

不建議使用建置參數來傳遞密碼,例如使用者憑證、API 令牌等。建置參數在 docker history 命令和 max 模式來源證明中是可見的,如果您使用 Buildx GitHub Actions 且您的 GitHub 儲存庫是公開的,則這些參數預設會附加到映像檔中。

請參閱 RUN --mount=type=secret 部分,了解在建置映像檔時安全使用密碼的方法。

一個 Dockerfile 可能包含一個或多個 ARG 指令。例如,以下是一個有效的 Dockerfile

FROM busybox
ARG user1
ARG buildno
# ...

預設值

ARG 指令可以選擇性地包含一個預設值

FROM busybox
ARG user1=someuser
ARG buildno=1
# ...

如果 ARG 指令有預設值,並且在建置時沒有傳遞任何值,則建置器會使用預設值。

範圍

ARG 變數從其在 Dockerfile 中宣告的行開始生效。例如,考慮這個 Dockerfile

FROM busybox
USER ${username:-some_user}
ARG username
USER $username
# ...

使用者透過呼叫以下指令來建置此檔案

$ docker build --build-arg username=what_user .
  • 第 2 行的 USER 指令會評估為 some_user 備用值,因為 username 變數尚未宣告。
  • username 變數在第 3 行宣告,從該點開始可在 Dockerfile 指令中引用。
  • 第 4 行的 USER 指令評估為 what_user,因為此時 username 參數的值為 what_user,該值是透過命令列傳遞的。在由 ARG 指令定義之前,任何變數的使用都會產生空字串。

在建置階段內宣告的 ARG 變數會自動被基於該階段的其他階段繼承。不相關的建置階段無法存取該變數。若要在多個不同的階段中使用參數,每個階段都必須包含 ARG 指令,或者它們都必須基於同一個 Dockerfile 中宣告該變數的共享基礎階段。

有關更多資訊,請參閱 變數範圍

使用 ARG 變數

您可以使用 ARGENV 指令來指定可供 RUN 指令使用的變數。使用 ENV 指令定義的環境變數總是會覆寫同名的 ARG 指令。請考慮這個包含 ENVARG 指令的 Dockerfile。

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=v1.0.0
RUN echo $CONT_IMG_VER

然後,假設此映像檔使用此命令建置

$ docker build --build-arg CONT_IMG_VER=v2.0.1 .

在這種情況下,RUN 指令使用的是 v1.0.0 而非使用者傳遞的 ARG 設定:v2.0.1。這種行為類似於 shell 腳本,其中局部範圍的變數會從其定義點開始覆寫作為參數傳遞或從環境繼承的變數。

使用上述範例,但採用不同的 ENV 規格,您可以在 ARGENV 指令之間建立更有用的互動

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER

ARG 指令不同,ENV 值總是會保留在建置的映像檔中。考慮一個不帶 --build-arg 旗標的 Docker 建置

$ docker build .

使用此 Dockerfile 範例,CONT_IMG_VER 仍然保留在映像檔中,但其值將是 v1.0.0,因為它是第 3 行由 ENV 指令設定的預設值。

此範例中的變數擴展技術允許您從命令列傳遞參數,並透過利用 ENV 指令將它們保留在最終映像檔中。變數擴展僅支援 有限的 Dockerfile 指令集。

預定義的 ARG

Docker 擁有一組預定義的 ARG 變數,您可以直接使用,而無需在 Dockerfile 中對應的 ARG 指令。

  • HTTP_PROXY
  • http_proxy
  • HTTPS_PROXY
  • https_proxy
  • FTP_PROXY
  • ftp_proxy
  • NO_PROXY
  • no_proxy
  • ALL_PROXY
  • all_proxy

若要使用這些變數,請在命令列上使用 --build-arg 旗標傳遞它們,例如

$ docker build --build-arg HTTPS_PROXY=https://my-proxy.example.com .

預設情況下,這些預定義變數會從 docker history 的輸出中排除。排除它們可以降低意外洩漏 HTTP_PROXY 變數中敏感認證資訊的風險。

例如,考慮使用 --build-arg HTTP_PROXY=http://user:pass@proxy.lon.example.com 建置以下 Dockerfile

FROM ubuntu
RUN echo "Hello World"

在這種情況下,HTTP_PROXY 變數的值在 docker history 中不可用,並且不會被快取。如果您更改位置,並且您的代理伺服器更改為 http://user:pass@proxy.sfo.example.com,則後續的建置不會導致快取遺失。

如果您需要覆寫此行為,可以透過在 Dockerfile 中新增 ARG 語句來實現,如下所示

FROM ubuntu
ARG HTTP_PROXY
RUN echo "Hello World"

建置此 Dockerfile 時,HTTP_PROXY 會保留在 docker history 中,並且更改其值會使建置快取失效。

全域範圍中的自動平台 ARG

此功能僅在使用 BuildKit 後端時可用。

BuildKit 支援一組預定義的 ARG 變數,其中包含執行建置的節點平台(建置平台)和結果映像檔平台(目標平台)的資訊。目標平台可以使用 docker build 上的 --platform 旗標來指定。

以下 ARG 變數會自動設定

  • TARGETPLATFORM - 建置結果的平台。例如 linux/amd64linux/arm/v7windows/amd64
  • TARGETOS - TARGETPLATFORM 的作業系統組件
  • TARGETARCH - TARGETPLATFORM 的架構組件
  • TARGETVARIANT - TARGETPLATFORM 的變體組件
  • BUILDPLATFORM - 執行建置的節點平台。
  • BUILDOS - BUILDPLATFORM 的作業系統組件
  • BUILDARCH - BUILDPLATFORM 的架構組件
  • BUILDVARIANT - BUILDPLATFORM 的變體組件

這些參數在全域範圍中定義,因此不會自動在建置階段內部或您的 RUN 命令中可用。要在建置階段內部公開其中一個參數,請在不賦值的情況下重新定義它。

例如

FROM alpine
ARG TARGETPLATFORM
RUN echo "I'm building for $TARGETPLATFORM"

BuildKit 內建的建置引數

參數類型描述
BUILDKIT_BUILD_NAMEString覆寫在 buildx history 命令Docker Desktop 建置檢視 中顯示的建置名稱。
BUILDKIT_CACHE_MOUNT_NSString設定可選的快取 ID 命名空間。
BUILDKIT_CONTEXT_KEEP_GIT_DIR布林值觸發 Git 上下文以保留 .git 目錄。
BUILDKIT_HISTORY_PROVENANCE_V1布林值為建置歷史記錄啟用 SLSA Provenance v1
BUILDKIT_INLINE_CACHE2布林值是否將內聯快取元資料寫入映像檔配置。
BUILDKIT_MULTI_PLATFORM布林值選擇確定性輸出,無論是否為多平台輸出。
BUILDKIT_SANDBOX_HOSTNAMEString設定主機名稱(預設為 buildkitsandbox
BUILDKIT_SYNTAXString設定前端映像檔
SOURCE_DATE_EPOCH整數設定所創建映像檔和層的 Unix 時間戳記。更多資訊請參閱 可重現建置。自 Dockerfile 1.5、BuildKit 0.11 起支援

範例:保留 .git 目錄

當使用 Git 上下文時,.git 目錄在檢出時不會被保留。如果您想在建置期間檢索 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/repo.git#main

對建置快取的影響

ARG 變數不會像 ENV 變數那樣保留在建置的映像檔中。然而,ARG 變數確實以類似的方式影響建置快取。如果 Dockerfile 定義了一個 ARG 變數,其值與先前建置不同,那麼在第一次使用它時(而非其定義時)會發生「快取遺失」。特別是,所有跟隨 ARG 指令的 RUN 指令都會隱式使用 ARG 變數(作為環境變數),因此可能導致快取遺失。所有預定義的 ARG 變數都免於快取,除非 Dockerfile 中存在匹配的 ARG 語句。

例如,考慮這兩個 Dockerfile

FROM ubuntu
ARG CONT_IMG_VER
RUN echo $CONT_IMG_VER
FROM ubuntu
ARG CONT_IMG_VER
RUN echo hello

如果您在命令列上指定 --build-arg CONT_IMG_VER=<value>,在兩種情況下,第 2 行的指定不會導致快取遺失;第 3 行會導致快取遺失。ARG CONT_IMG_VER 會導致 RUN 行被識別為與執行 CONT_IMG_VER=<value> echo hello 相同,因此如果 <value> 發生變化,您就會遇到快取遺失。

在相同的命令列下考慮另一個範例

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=$CONT_IMG_VER
RUN echo $CONT_IMG_VER

在此範例中,快取遺失發生在第 3 行。發生遺失的原因是 ENV 中變數的值參考了 ARG 變數,而該變數透過命令列被更改。在此範例中,ENV 命令會導致映像檔包含該值。

如果一個 ENV 指令覆寫了同名的 ARG 指令,就像這個 Dockerfile 一樣

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=hello
RUN echo $CONT_IMG_VER

第 3 行不會導致快取遺失,因為 CONT_IMG_VER 的值是一個常數 (hello)。因此,在 RUN(第 4 行)上使用的環境變數和值在不同建置之間不會改變。

ONBUILD

ONBUILD <INSTRUCTION>

ONBUILD 指令會向映像檔新增一個觸發指令,該指令將在稍後當此映像檔被用作另一個建置的基礎時執行。該觸發器將在下游建置的上下文中執行,如同它被立即插入到下游 Dockerfile 中的 FROM 指令之後一樣。

如果您正在建置一個將用作其他映像檔基礎的映像檔,例如應用程式建置環境或可能透過使用者特定配置進行客製化的守護程式,這會很有用。

例如,如果您的映像檔是一個可重複使用的 Python 應用程式建置器,它將要求應用程式原始碼新增到特定目錄中,並且可能需要在之後呼叫建置腳本。您不能現在就呼叫 ADDRUN,因為您還無法存取應用程式原始碼,而且每個應用程式建置的原始碼都會不同。您可以簡單地向應用程式開發人員提供一個樣板 Dockerfile,讓他們複製貼上到他們的應用程式中,但這樣效率低下、容易出錯且難以更新,因為它與應用程式特定的程式碼混淆。

解決方案是使用 ONBUILD 註冊預先指令,以便在下一個建置階段稍後執行。

其運作方式如下

  1. 當遇到 ONBUILD 指令時,建置器會將一個觸發器新增到正在建置的映像檔的元資料中。該指令本身不會影響目前的建置。
  2. 在建置結束時,所有觸發器的列表會儲存在映像檔清單的 OnBuild 鍵下。可以使用 docker inspect 命令檢查它們。
  3. 之後,該映像檔可能會透過 FROM 指令用作新建置的基礎。作為處理 FROM 指令的一部分,下游建置器會尋找 ONBUILD 觸發器,並以它們註冊的相同順序執行。如果任何觸發器失敗,FROM 指令將被中止,進而導致建置失敗。如果所有觸發器都成功,FROM 指令將完成,建置將照常繼續。
  4. 觸發器在執行後會從最終映像檔中清除。換句話說,它們不會被「孫代」建置繼承。

例如,您可能會新增類似這樣的內容

ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src

從階段、映像檔或上下文複製或掛載

自 Dockerfile 語法 1.11 起,您可以將 ONBUILD 與從其他階段、映像檔或建置上下文複製或掛載檔案的指令一起使用。例如

# syntax=docker/dockerfile:1.11
FROM alpine AS baseimage
ONBUILD COPY --from=build /usr/bin/app /app
ONBUILD RUN --mount=from=config,target=/opt/appconfig ...

如果 from 的來源是一個建置階段,則該階段必須在觸發 ONBUILD 的 Dockerfile 中定義。如果它是一個具名上下文,該上下文必須傳遞給下游建置。

ONBUILD 限制

  • 不允許使用 ONBUILD ONBUILD 連結 ONBUILD 指令。
  • ONBUILD 指令可能不會觸發 FROMMAINTAINER 指令。

STOPSIGNAL

STOPSIGNAL signal

STOPSIGNAL 指令設定將發送給容器以退出的系統呼叫信號。此信號可以是 SIG<NAME> 格式的信號名稱,例如 SIGKILL,或是一個符合核心系統呼叫表位置的無符號數字,例如 9。如果未定義,預設為 SIGTERM

映像檔的預設停止信號可以針對每個容器覆寫,方法是在 docker rundocker create 上使用 --stop-signal 旗標。

HEALTHCHECK

HEALTHCHECK 指令有兩種形式

  • HEALTHCHECK [OPTIONS] CMD command (透過在容器內部執行命令來檢查容器健康狀態)
  • HEALTHCHECK NONE (禁用從基礎映像檔繼承的任何健康檢查)

HEALTHCHECK 指令告訴 Docker 如何測試容器以檢查其是否仍在運作。這可以偵測到諸如網頁伺服器陷入無限循環且無法處理新連線的情況,即使伺服器行程仍在執行。

當容器指定了健康檢查時,除了其正常狀態外,它還具有一個健康狀態。此狀態最初為 starting。每當健康檢查通過時,它就會變為 healthy(無論之前處於何種狀態)。在一定數量的連續失敗後,它會變為 unhealthy

可以在 CMD 之前出現的選項有

  • --interval=DURATION (預設:30s)
  • --timeout=DURATION (預設:30s)
  • --start-period=DURATION (預設:0s)
  • --start-interval=DURATION (預設:5s)
  • --retries=N (預設:3)

健康檢查將在容器啟動後 interval 秒首次執行,然後在每次前一個檢查完成後再 interval 秒執行。

如果單次檢查的執行時間超過 timeout 秒,則該檢查被視為失敗。

健康檢查需要 retries 次連續失敗,容器才會被視為 unhealthy

啟動週期(start period) 為需要時間引導的容器提供初始化時間。在此期間的探測失敗將不計入最大重試次數。然而,如果在啟動週期內健康檢查成功,則容器被視為已啟動,並且所有連續失敗都將計入最大重試次數。

啟動間隔(start interval) 是啟動週期內健康檢查之間的時間。此選項需要 Docker Engine 25.0 或更高版本。

一個 Dockerfile 中只能有一個 HEALTHCHECK 指令。如果您列出多個,則只有最後一個 HEALTHCHECK 會生效。

CMD 關鍵字後的命令可以是 shell 命令(例如 HEALTHCHECK CMD /bin/check-running)或 exec 陣列(如同其他 Dockerfile 命令;詳情請參閱 ENTRYPOINT)。

命令的退出狀態表示容器的健康狀態。可能的值有

  • 0:成功 - 容器健康且可供使用
  • 1:不健康 - 容器運作不正常
  • 2:保留 - 請勿使用此退出碼

例如,大約每五分鐘檢查一次網頁伺服器是否能在三秒內提供網站主頁

HEALTHCHECK --interval=5m --timeout=3s \
  CMD curl -f https:/// || exit 1

為協助偵錯失敗的探測,命令寫入 stdout 或 stderr 的任何輸出文字(UTF-8 編碼)將儲存在健康狀態中,並可透過 docker inspect 查詢。此類輸出應保持簡短(目前僅儲存前 4096 位元組)。

當容器的健康狀態改變時,會產生一個帶有新狀態的 health_status 事件。

SHELL

SHELL ["executable", "parameters"]

SHELL 指令允許覆寫命令的 shell 形式所使用的預設 shell。Linux 上的預設 shell 是 ["/bin/sh", "-c"],Windows 上則是 ["cmd", "/S", "/C"]SHELL 指令必須以 JSON 格式寫在 Dockerfile 中。

SHELL 指令在 Windows 上特別有用,因為 Windows 有兩個常用且截然不同的原生 shell:cmdpowershell,以及包括 sh 在內的其他可用的 shell。

SHELL 指令可以出現多次。每個 SHELL 指令都會覆寫所有先前的 SHELL 指令,並影響所有後續指令。例如

FROM microsoft/windowsservercore

# Executed as cmd /S /C echo default
RUN echo default

# Executed as cmd /S /C powershell -command Write-Host default
RUN powershell -command Write-Host default

# Executed as powershell -command Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello

# Executed as cmd /S /C echo hello
SHELL ["cmd", "/S", "/C"]
RUN echo hello

當 Dockerfile 中使用以下指令的 shell 形式時,它們可能會受到 SHELL 指令的影響:RUNCMDENTRYPOINT

以下範例是 Windows 上常見的一種模式,可以透過使用 SHELL 指令來簡化

RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"

建置器將執行的命令會是

cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"

這有兩個原因導致效率低下。首先,有一個不必要的 cmd.exe 命令處理器(即 shell)被呼叫。其次,shell 形式的每個 RUN 指令都需要在命令前加上額外的 powershell -command

為了提高效率,可以使用兩種機制之一。其一是使用 RUN 命令的 JSON 形式,例如

RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]

雖然 JSON 格式明確且不使用不必要的 cmd.exe,但它確實需要透過雙引號和轉義來增加冗長性。另一種機制是使用 SHELL 指令與 shell 格式,為 Windows 使用者提供更自然的語法,特別是與 escape 解析器指令結合使用時

# escape=`

FROM microsoft/nanoserver
SHELL ["powershell","-command"]
RUN New-Item -ItemType Directory C:\Example
ADD Execute-MyCmdlet.ps1 c:\example\
RUN c:\example\Execute-MyCmdlet -sample 'hello world'

結果為

PS E:\myproject> docker build -t shell .

Sending build context to Docker daemon 4.096 kB
Step 1/5 : FROM microsoft/nanoserver
 ---> 22738ff49c6d
Step 2/5 : SHELL powershell -command
 ---> Running in 6fcdb6855ae2
 ---> 6331462d4300
Removing intermediate container 6fcdb6855ae2
Step 3/5 : RUN New-Item -ItemType Directory C:\Example
 ---> Running in d0eef8386e97


    Directory: C:\


Mode         LastWriteTime              Length Name
----         -------------              ------ ----
d-----       10/28/2016  11:26 AM              Example


 ---> 3f2fbf1395d9
Removing intermediate container d0eef8386e97
Step 4/5 : ADD Execute-MyCmdlet.ps1 c:\example\
 ---> a955b2621c31
Removing intermediate container b825593d39fc
Step 5/5 : RUN c:\example\Execute-MyCmdlet 'hello world'
 ---> Running in be6d8e63fe75
hello world
 ---> 8e559e9bf424
Removing intermediate container be6d8e63fe75
Successfully built 8e559e9bf424
PS E:\myproject>

SHELL 指令也可以用來修改 shell 的操作方式。例如,在 Windows 上使用 SHELL cmd /S /C /V:ON|OFF 可以修改延遲環境變數擴展的語義。

如果需要替代的 shell,例如 zshcshtcsh 等,SHELL 指令也可以在 Linux 上使用。

內聯文件 (Here-Documents)

Here-documents 允許將後續 Dockerfile 行重定向到 RUNCOPY 命令的輸入。如果此類命令包含 here-document,Dockerfile 會將其後的行(直到只包含 here-doc 分隔符的行)視為同一命令的一部分。

範例:執行多行指令碼

# syntax=docker/dockerfile:1
FROM debian
RUN <<EOT bash
  set -ex
  apt-get update
  apt-get install -y vim
EOT

如果命令只包含一個 here-document,其內容將使用預設 shell 進行評估。

# syntax=docker/dockerfile:1
FROM debian
RUN <<EOT
  mkdir -p foo/bar
EOT

或者,可以使用 shebang 標頭來定義解釋器。

# syntax=docker/dockerfile:1
FROM python:3.6
RUN <<EOT
#!/usr/bin/env python
print("hello world")
EOT

更複雜的範例可能會使用多個 here-documents。

# syntax=docker/dockerfile:1
FROM alpine
RUN <<FILE1 cat > file1 && <<FILE2 cat > file2
I am
first
FILE1
I am
second
FILE2

範例:建立內聯檔案

透過 COPY 指令,您可以將來源參數替換為 here-doc 指示符,以將 here-document 的內容直接寫入檔案。以下範例使用 COPY 指令創建一個包含 hello worldgreeting.txt 檔案。

# syntax=docker/dockerfile:1
FROM alpine
COPY <<EOF greeting.txt
hello world
EOF

常規 here-doc 的 變數擴展和 tab 剝離規則 適用。以下範例顯示了一個小型 Dockerfile,它使用帶有 here-document 的 COPY 指令創建一個 hello.sh 腳本檔案。

# syntax=docker/dockerfile:1
FROM alpine
ARG FOO=bar
COPY <<-EOT /script.sh
  echo "hello ${FOO}"
EOT
ENTRYPOINT ash /script.sh

在這個案例中,檔案腳本會印出「hello bar」,因為變數在 COPY 指令執行時被擴展。

$ docker build -t heredoc .
$ docker run heredoc
hello bar

如果您反而將 here-document 字串 EOT 的任何部分引起來,則變數將不會在建置時擴展。

# syntax=docker/dockerfile:1
FROM alpine
ARG FOO=bar
COPY <<-"EOT" /script.sh
  echo "hello ${FOO}"
EOT
ENTRYPOINT ash /script.sh

請注意,這裡的 ARG FOO=bar 是多餘的,可以移除。該變數在運行時,即腳本被調用時進行解釋。

$ docker build -t heredoc .
$ docker run -e FOO=world heredoc
hello world

Dockerfile 範例

有關 Dockerfile 的範例,請參閱


  1. 需要值 ↩︎ ↩︎ ↩︎

  2. 適用於 Docker 整合的 BuildKitdocker buildx build ↩︎

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