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 的頂部。

解析器指令的鍵,例如 `syntax` 或 `check`,不區分大小寫,但按約定使用小寫。指令的值區分大小寫,並且必須以適合該指令的大小寫形式編寫。例如,`#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=skip=JSONArgsRecommended,StageNameCasing

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

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

# check=error=true
注意

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

要組合 `skip` 和 `error` 選項,請使用分號分隔它們

# 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}variable 中第一次出現的 pattern 替換為 replacement

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

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

在所有情況下,`word` 可以是任何字串,包括其他環境變數。

`pattern` 是一個 glob 模式,其中 `?` 匹配任何單個字元,`*` 匹配任意數量的字元(包括零個)。要匹配字面值 `?` 和 `*`,請使用反斜槓轉義:`\?` 和 `\*`。

您可以透過在變數前新增 `\` 來轉義整個變數名稱:例如,`\$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(與上述支援的指令之一結合使用時)

您還可以將環境變數與 `RUN`、`CMD` 和 `ENTRYPOINT` 指令一起使用,但在這些情況下,變數替換由命令 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 ["executable","param1","param2"] (exec 形式)
  • INSTRUCTION command param1 param2 (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 的更多資訊,請參閱此處文件

使用不同的 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` 指令之前提交輸出的最後一個映象 ID。每個 `FROM` 指令都會清除先前指令建立的任何狀態。
  • 可以選擇透過在 `FROM` 指令中新增 `AS name` 來為新的構建階段命名。該名稱可在後續的 `FROM `、`COPY --from=``RUN --mount=type=bind,from=` 指令中引用在此階段構建的映象。
  • `tag` 或 `digest` 值是可選的。如果省略其中任何一個,構建器預設假定 `latest` 標籤。如果構建器找不到 `tag` 值,則會返回錯誤。

可選的 `--platform` 標誌可用於指定映象的平臺,以防 `FROM` 引用多平臺映象。例如,`linux/amd64`、`linux/arm64` 或 `windows/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 等指令的快取將在下次構建期間重複使用。可以使用 --no-cache 標誌使 RUN 指令的快取失效,例如 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 註釋請求裝置
注意

CDI 規範自 0.6.0 起支援註釋。

注意

要自動允許 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.co/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-agent 套接字
  • 使用永續性包管理快取來加速構建

支援的掛載型別有

型別描述
bind(預設)繫結掛載上下文目錄(只讀)。
快取掛載臨時目錄以快取編譯器和包管理器的目錄。
tmpfs在構建容器中掛載一個 tmpfs
secret允許構建容器訪問安全檔案(如私鑰),而無需將其烘焙到映象或構建快取中。
ssh允許構建容器透過 SSH 代理訪問 SSH 金鑰,並支援密碼短語。

RUN --mount=type=bind

此掛載型別允許將檔案或目錄繫結到構建容器。繫結掛載預設是隻讀的。

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

RUN --mount=type=cache

此掛載型別允許構建容器快取編譯器和包管理器的目錄。

選項描述
id用於識別獨立/不同快取的可選 ID。預設為 target 的值。
targetdstdestination1掛載路徑。
ro,readonly如果設定,則為只讀。
共享sharedprivatelocked 之一。預設為 sharedshared 快取掛載可由多個寫入器併發使用。如果存在多個寫入器,private 將建立一個新的掛載。locked 將暫停第二個寫入器,直到第一個寫入器釋放掛載。
來自用作快取掛載基礎的構建階段、上下文或映象名稱。預設為空目錄。
sourcefrom 中要掛載的子路徑。預設為 from 的根目錄。
mode新快取目錄的檔案模式(八進位制)。預設 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掛載路徑。
size指定檔案系統大小的上限。

RUN --mount=type=secret

此掛載型別允許構建容器訪問機密值,例如令牌或私鑰,而無需將其烘焙到映象中。

預設情況下,機密作為檔案掛載。您還可以透過設定 `env` 選項將機密作為環境變數掛載。

選項描述
id機密 ID。預設為目標路徑的基本名稱。
targetdstdestination將機密掛載到指定路徑。如果未設定且 `env` 也未設定,則預設為 /run/secrets/ + id
env將機密掛載為環境變數而不是檔案,或兩者兼而有之。(自 Dockerfile v1.10.0 起)
必需如果設定為 true,則當機密不可用時,指令會出錯。預設為 false
mode機密檔案的檔案模式(八進位制)。預設 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}
必需如果設定為 true,則當金鑰不可用時,指令會出錯。預設為 false
mode套接字的檔案模式(八進位制)。預設 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(預設)在預設網路中執行。
none在沒有網路訪問許可權的情況下執行。
host在主機的網路環境中執行。

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 守護程式時應啟用 `security.insecure` 許可權,使用 ` --allow-insecure-entitlement security.insecure ` 標誌或在 buildkitd 配置 中,以及對於使用 ` --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 ["executable","param1","param2"] (exec 形式)
  • CMD ["param1","param2"] (exec 形式,作為 ENTRYPOINT 的預設引數)
  • CMD command param1 param2 (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 指令將環境變數 設定為值 。此值將存在於構建階段的所有後續指令的環境中,並且也可以在許多指令中內聯替換。該值將解釋為其他環境變數,因此如果引號字元未轉義,它們將被刪除。與命令列解析一樣,引號和反斜槓可用於在值中包含空格。

示例

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

ENV 指令允許一次設定多個 = ... 變數,下面的示例將在最終映象中產生相同的淨結果

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

使用 ENV 設定的環境變數在從生成的映象執行容器時將持久存在。您可以使用 docker inspect 檢視這些值,並使用 docker run --env = 更改它們。

一個階段會繼承其父階段或任何祖先階段使用 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 ,省略 =。例如

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 指令將新檔案或目錄從 複製到映象檔案系統中的路徑 。檔案和目錄可以從構建上下文、遠端 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 檢測 的型別並相應地處理它。

從構建上下文新增檔案

任何不以 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 和歸檔檔案的其餘部分將用作構建上下文。

模式匹配

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

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

ADD *.png /dest/

在以下示例中,`?` 是一個單字元萬用字元,例如匹配 `index.js` 和 `index.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 wget`、`RUN 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>

是遠端 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:。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 指令將新檔案或目錄從 複製到映象檔案系統中的路徑 。檔案和目錄可以從構建上下文、構建階段、命名上下文或映象複製。

ADDCOPY 指令在功能上相似,但目的略有不同。瞭解更多關於ADDCOPY 之間的區別

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

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

如果您指定多個原始檔,無論是直接指定還是使用萬用字元,則目標必須是一個目錄(必須以斜槓 / 結尾)。

COPY 接受一個標誌 --from=,它允許您指定源位置是一個構建階段、上下文或映象。以下示例從名為 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。

模式匹配

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

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

COPY *.png /dest/

在以下示例中,`?` 是一個單字元萬用字元,例如匹配 `index.js` 和 `index.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 = 指定)或映象複製檔案。以下示例從官方 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 作業系統的容器。

除非可選的 --chown 標誌指定給定的使用者名稱、組名或 UID/GID 組合以請求複製內容的特定所有權,否則從構建上下文複製的所有檔案和目錄都將以 UID 和 GID 為 0 建立。--chown 標誌的格式允許使用使用者名稱和組名字串或直接的整數 UID 和 GID 的任意組合。提供沒有組名的使用者名稱或沒有 GID 的 UID 將使用相同的數字 UID 作為 GID。如果提供了使用者名稱或組名,則容器的根檔案系統 /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>

在 `COPY` 或 `ADD` 命令中啟用此標誌,可讓您以增強的語義複製檔案,您的檔案在自己的層中保持獨立,並且當更改上一層上的命令時不會失效。

當使用 --link 時,您的原始檔將複製到一個空的目標目錄中。該目錄將轉換為一個層,該層連結在您之前的狀態之上。

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

等同於進行兩次構建

FROM alpine

FROM scratch
COPY /foo /bar

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

即使上一層已更改,也可以使用 --link 在後續構建中透過 --cache-from 重用已構建的層。這對於多階段構建尤為重要,因為如果同一階段中的任何先前命令更改,則 COPY --from 語句以前會失效,導致需要再次重建中間階段。使用 --link,以前構建生成的層將被重用併合併到新層之上。這也意味著當基礎映象收到更新時,您可以輕鬆地重新定位您的映象,而無需再次執行整個構建。在支援它的後端中,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 工具的 --parentsrsync--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 標誌允許您指定要排除檔案的路徑表示式。

路徑表示式遵循與 相同的格式,支援萬用字元並使用 Go 的 filepath.Match 規則進行匹配。例如,要新增所有以“hom”開頭的檔案,但排除 .txt 副檔名的檔案

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

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

您可以為 COPY 指令多次指定 --exclude 選項。多個 --exclude 表示與其模式匹配的檔案不會被複制,即使檔案路徑與 中指定的模式匹配。要新增所有以“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 的命令列引數將附加到 exec 形式 ENTRYPOINT 中的所有元素之後,並將覆蓋使用 CMD 指定的所有元素。

這允許將引數傳遞給入口點,即 docker run -d 將把 -d 引數傳遞給入口點。您可以使用 docker run --entrypoint 標誌覆蓋 ENTRYPOINT 指令。

ENTRYPOINT 的 shell 形式阻止使用任何 CMD 命令列引數。它還會將您的 ENTRYPOINT 作為 /bin/sh -c 的子命令啟動,該子命令不傳遞訊號。這意味著可執行檔案將不會是容器的 PID 1,並且不會接收 Unix 訊號。在這種情況下,您的可執行檔案不會從 docker stop 接收到 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"]

如果您需要為單個可執行檔案編寫啟動指令碼,可以透過使用 `exec` 和 `gosu` 命令來確保最終可執行檔案接收到 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 exec` 或 `docker 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 時 cleanly 退出。

$ /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,容器將無法 cleanly 退出 - 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 = 標誌將其傳遞給構建器。

警告

不建議使用構建引數來傳遞諸如使用者憑據、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 build:

$ 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_NAME字串覆蓋 buildx history 命令Docker Desktop Builds 檢視中顯示的構建名稱。
BUILDKIT_CACHE_MOUNT_NS字串設定可選的快取 ID 名稱空間。
BUILDKIT_CONTEXT_KEEP_GIT_DIR布林值觸發 Git 上下文保留 .git 目錄。
BUILDKIT_HISTORY_PROVENANCE_V1布林值為構建歷史記錄啟用 SLSA Provenance v1
BUILDKIT_INLINE_CACHE2布林值是否將快取元資料內聯到映象配置中。
BUILDKIT_MULTI_PLATFORM布林值選擇確定性輸出,無論是否多平臺輸出。
BUILDKIT_SANDBOX_HOSTNAME字串設定主機名(預設為 buildkitsandbox
BUILDKIT_SYNTAX字串設定前端映象
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=,在這兩種情況下,第 2 行的規範不會導致快取未命中;第 3 行會導致快取未命中。ARG CONT_IMG_VER 會導致 RUN 行被識別為與執行 CONT_IMG_VER= echo hello 相同,因此如果 發生變化,您會遇到快取未命中。

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

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 格式的訊號名稱,例如 SIGKILL,也可以是與核心系統呼叫表中的位置匹配的無符號數字,例如 9。如果未定義,預設為 SIGTERM

可以使用 docker rundocker create 上的 --stop-signal 標誌,根據每個容器覆蓋映象的預設停止訊號。

HEALTHCHECK

HEALTHCHECK 指令有兩種形式:

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

HEALTHCHECK 指令告訴 Docker 如何測試容器以檢查其是否仍在工作。這可以檢測例如 Web 伺服器陷入無限迴圈且無法處理新連線的情況,即使伺服器程序仍在執行。

當容器指定了健康檢查時,除了其正常狀態外,它還具有健康狀態。此狀態最初為 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: 保留 - 不要使用此退出程式碼

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

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

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

當容器的健康狀態發生變化時,將生成一個包含新狀態的 health_status 事件。

SHELL

SHELL ["executable", "parameters"]

SHELL 指令允許覆蓋用於 shell 形式命令的預設 shell。Linux 上的預設 shell 是 ["/bin/sh", "-c"],Windows 上的預設 shell 是 ["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 變數擴充套件和製表符剝離規則適用。以下示例展示了一個小的 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 ↩︎