Dockerfile 參考

目錄

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

概觀

Dockerfile 支援以下指令

指令說明
ADD(新增檔案)新增本地或遠端檔案和目錄。
ARG(建置參數)使用建構時期變數。
CMD(預設指令)指定預設指令。
COPY(複製檔案)複製檔案和目錄。
ENTRYPOINT(入口點)指定預設執行檔。
ENV(環境變數)設定環境變數。
EXPOSE(暴露埠口)描述您的應用程式正在監聽的連接埠。
FROM(來源映像檔)從基礎映像建立新的建構階段。
HEALTHCHECK(健康檢查)在啟動時檢查容器的健康狀態。
LABEL(標籤)將中繼資料新增至映像。
維護者指定映像的作者。
ONBUILD(建置觸發器)指定在建構中使用映像時的指示。
RUN(執行指令)執行建構指令。
SHELL(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 指令之前,會先移除註解行。在執行 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=skip=JSONArgsRecommended,StageNameCasing

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

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

# check=error=true

注意

當使用帶有 error=true 選項的 check 指令時,建議將 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}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(與上述其中一個支援的指令組合使用時)

您也可以在 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 指令都有兩種可能的格式:

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

您也可以在 shell 格式中使用 heredoc 來拆分支援的命令。

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

有關 heredoc 的更多資訊,請參閱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 名稱 來為新的建構階段命名。該名稱可以在後續的 FROM <名稱>COPY --from=<名稱>RUN --mount=type=bind,from=<名稱> 指令中使用,以引用在此階段建構的映像。
  • 標籤摘要 值是可選的。如果您省略其中任何一個,建構器預設會採用 latest 標籤。如果建構器找不到 標籤 值,則會返回錯誤。

FROM 引用多平台映像時,可以使用可選的 --platform 標誌來指定映像的平台。例如,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 格式最常用,它允許您將較長的指令拆分為多行,可以使用換行跳脫字元heredoc

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

RUN 指令可用的 [OPTIONS] 如下

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

RUN 指令的快取失效

RUN 指令的快取在下次建構期間不會自動失效。像 RUN apt-get dist-upgrade -y 這樣的指令的快取將在下一次建構期間重複使用。可以使用 --no-cache 標誌使 RUN 指令的快取失效,例如 docker build --no-cache

請參閱Dockerfile 最佳實務指南ADDCOPY 指令失效。

RUN --mount(掛載)

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

RUN --mount 允許您創建建構可以訪問的檔案系統掛載。這可以用於

  • 創建到主機檔案系統或其他建構階段的綁定掛載
  • 訪問建構機密或 ssh-agent 通訊端
  • 使用持久性套件管理快取來加速您的建構

支援的掛載類型如下

類型說明
bind(預設)綁定掛載上下文目錄(唯讀)。
cache掛載臨時目錄以快取編譯器和套件管理器的目錄。
tmpfs在建構容器中掛載 tmpfs
secret允許構建容器訪問安全檔案,例如私鑰,而無需將它們烘焙到映像或構建快取中。
ssh允許構建容器透過 SSH 代理訪問 SSH 金鑰,並支援密碼。

RUN --mount=type=bind(繫結掛載)

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

選項說明
targetdstdestination1掛載路徑。
sourcefrom 中的來源路徑。預設為 from 的根目錄。
from構建階段、上下文或映像名稱,作為來源的根目錄。預設為構建上下文。
rwreadwrite允許在掛載上進行寫入。寫入的數據將被丟棄。

RUN --mount=type=cache(快取掛載)

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

選項說明
id用於識別單獨/不同快取的選用 ID。預設為 target 的值。
targetdstdestination1掛載路徑。
roreadonly如果設定,則為唯讀。
sharingsharedprivatelocked 之一。預設為 sharedshared 快取掛載可以由多個寫入器同時使用。如果有多個寫入器,private 會建立新的掛載。locked 會暫停第二個寫入器,直到第一個寫入器釋放掛載。
from用作快取掛載基礎的構建階段、上下文或映像名稱。預設為空目錄。
source要掛載的 from 中的子路徑。預設為 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 起)
required如果設定為 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 代理訪問 SSH 金鑰,並支援密碼。

選項說明
idSSH 代理通訊端或金鑰的 ID。預設為「default」。
targetdstdestinationSSH 代理通訊端路徑。預設為 /run/buildkit/ssh_agent.${N}
required如果設定為 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 授權,這需要在使用 --allow-insecure-entitlement network.host 旗標啟動 buildkitd 守護程式時啟用,或者在 buildkitd 設定檔--allow network.host 旗標

RUN --security(安全性)

注意

穩定語法尚不支援,請使用 docker/dockerfile:1-labs 版本。

RUN --security=<sandbox|insecure>

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

警告

要使用此功能,需要在使用 --allow-insecure-entitlement security.insecure 旗標啟動 buildkitd 守護程式時,或在 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 指令設定從映像檔執行容器時要執行的命令。

您可以使用 殼層或執行格式 指定 CMD 指令

  • CMD ["executable","param1","param2"] (執行格式)
  • CMD ["param1","param2"] (執行格式,作為 ENTRYPOINT 的預設參數)
  • CMD command param1 param2 (殼層格式)

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

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

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

如果使用 CMDENTRYPOINT 指令提供預設參數,則 CMDENTRYPOINT 指令都應以 執行格式 指定。

注意

不要將 RUNCMD 混淆。RUN 實際執行命令並提交結果;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

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

如果來源是一個檔案,則檔案及其元數據會被複製到目標位置。檔案權限會被保留。如果來源是一個檔案,且目標位置存在一個同名目錄,則會引發錯誤。

如果您透過 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 片段

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

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

目的地

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

# 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(保留 Git 目錄)

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 旗標可以驗證遠端資源的 checksum。checksum 的格式為 <演算法>:<雜湊值>。支援的演算法有 sha256sha384sha512

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

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

如果來源是一個檔案,則檔案及其元數據會被複製到目標位置。檔案權限會被保留。如果來源是一個檔案,且目標位置存在一個同名目錄,則會引發錯誤。

如果您透過 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>

要在 多階段建置

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

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

COPY --chown --chmod(變更擁有者和權限)

注意

目前僅支援八進位表示法。非八進位的支援記錄在 moby/buildkit#1951

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

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

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

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

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,先前建置生成的層將被重複使用並合併到新層之上。這也意味著當基礎映像收到更新時,您可以輕鬆地重新基底化您的映像,而無需再次執行整個建置。在支援它的後端中,BuildKit 可以執行此重新基底化操作,而無需在客戶端和 registry 之間推送或拉取任何層。BuildKit 將會偵測這種情況,並且只建立包含新層和舊層以正確順序排列的新映像清單。

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

--link=false 的不相容性

使用 --link 時,不允許 COPY/ADD 指令從先前狀態讀取任何檔案。這表示如果在先前狀態中,目標目錄是包含符號連結的路徑,則 COPY/ADD 無法追蹤它。在最終映像中,使用 --link 建立的目標路徑將始終是僅包含目錄的路徑。

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

COPY --parents(保留父目錄結構)

注意

穩定語法中尚不支援,請使用 docker/dockerfile:1.7-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.7-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 有兩種可能的格式

有關不同格式的更多資訊,請參閱 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 指令。

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

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

Exec 格式 ENTRYPOINT 範例

您可以使用 exec 格式的 ENTRYPOINT 來設定相當穩定的預設指令和參數,然後使用 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 中磁碟區的事項。

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 指令定義了一個變數,使用者可以在建置時使用 --build-arg <變數名稱>=<值> 旗標,透過 docker build 命令將其傳遞給建置器。

警告

不建議使用建置參數來傳遞機密資訊,例如使用者憑證、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 .

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

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

使用 ARG 變數

您可以使用 `ARG` 或 `ENV` 指令來指定 `RUN` 指令可用的變數。使用 `ENV` 指令定義的環境變數將始終覆蓋同名的 `ARG` 指令。請考慮這個包含 `ENV` 和 `ARG` 指令的 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` 規範,您可以建立 `ARG` 和 `ENV` 指令之間更有用的互動

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`,因為它是 `ENV` 指令在第 3 行設定的預設值。

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

預定義 ARG

Docker 有一組預定義的 `ARG` 變數,您可以在 Dockerfile 中不使用對應的 `ARG` 指令即可使用它們。

要使用這些變數,請使用 `--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 內建建置參數

參數類型說明
BUILDKIT_CACHE_MOUNT_NS字串設定選用的快取 ID 命名空間。
BUILDKIT_CONTEXT_KEEP_GIT_DIR布林值觸發 Git context 以保留 `.git` 目錄。
`BUILDKIT_INLINE_CACHE`2布林值是否將快取中繼資料內嵌到映像檔設定中。
BUILDKIT_MULTI_PLATFORM布林值選擇是否使用確定性輸出,不論多平台輸出為何。
BUILDKIT_SANDBOX_HOSTNAME字串設定主機名稱(預設為 `buildkitsandbox`)
BUILDKIT_SYNTAX字串設定前端映像檔
SOURCE_DATE_EPOCH整數設定建立的映像檔和圖層的 Unix 時間戳記。更多資訊請參考 可重現建置。Dockerfile 1.5 和 BuildKit 0.11 以後版本皆支援。

範例:保留 .git 目錄

使用 Git context 時,.git 目錄不會保留在 checkout 中。如果您希望在建置過程中擷取 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 限制

STOPSIGNAL(停止訊號)

STOPSIGNAL signal

STOPSIGNAL 指令設定將發送到容器以使其退出的系統呼叫訊號。此訊號可以是 SIG<NAME> 格式的訊號名稱,例如 SIGKILL,或者可以是與核心系統呼叫表格中位置匹配的無符號數字,例如 9。如果未定義,則預設值為 SIGTERM

可以使用 docker rundocker create 上的 --stop-signal 旗標,針對每個容器覆蓋映像檔的預設停止訊號。

HEALTHCHECK(健康檢查)

HEALTHCHECK 指令有兩種形式

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

當容器指定了健康檢查時,除了其正常狀態之外,它還具有健康狀態。此狀態最初為「啟動中」。每當健康檢查通過時,它就會變為「健康」(無論之前的狀態為何)。在一定數量的連續失敗之後,它就會變為「不健康」。

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

健康檢查將在容器啟動後 **interval** 秒鐘第一次運行,然後在每次先前檢查完成後 **interval** 秒鐘再次運行。

如果單次檢查執行時間超過 **timeout** 秒,則認為檢查失敗。

健康檢查需要連續 **retries** 次失敗,容器才會被視為「不健康」。

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

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

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

CMD 關鍵字後面的命令可以是 shell 命令(例如 HEALTHCHECK CMD /bin/check-running)或 exec 陣列(與其他 Dockerfile 命令一樣;有關詳細資訊,請參閱例如 ENTRYPOINT)。

命令的退出狀態碼指示容器的健康狀態。可能的值為:

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

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

為了幫助調試失敗的探測,命令在 stdout 或 stderr 上寫入的任何輸出文本(UTF-8 編碼)都將存儲在健康狀態中,並且可以使用 docker inspect 進行查詢。此類輸出應保持簡短(目前僅存儲前 4096 個位元組)。

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

SHELL(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,以及其他可用的 shell,包括 sh

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 等,則也可以在 Linux 上使用 SHELL 指令。

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 ↩︎