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 形式
RUN
、CMD
和 ENTRYPOINT
指令都有兩種可能的格式
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 版本 |
---|---|
--device | 1.14-labs |
--mount | 1.2 |
--network | 1.3 |
--security | 1.1.2-labs |
RUN 指令的快取失效
RUN
指令的快取不會在下次構建期間自動失效。RUN apt-get dist-upgrade -y
等指令的快取將在下次構建期間重複使用。可以使用 --no-cache
標誌使 RUN
指令的快取失效,例如 docker build --no-cache
。
有關更多資訊,請參閱Dockerfile 最佳實踐指南。
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
此掛載型別允許將檔案或目錄繫結到構建容器。繫結掛載預設是隻讀的。
選項 | 描述 |
---|---|
target 、dst 、destination 1 | 掛載路徑。 |
source | from 中的源路徑。預設為 from 的根目錄。 |
來自 | 源的根目錄的構建階段、上下文或映象名稱。預設為構建上下文。 |
rw ,readwrite | 允許在掛載上寫入。寫入的資料將被丟棄。 |
RUN --mount=type=cache
此掛載型別允許構建容器快取編譯器和包管理器的目錄。
選項 | 描述 |
---|---|
id | 用於識別獨立/不同快取的可選 ID。預設為 target 的值。 |
target 、dst 、destination 1 | 掛載路徑。 |
ro ,readonly | 如果設定,則為只讀。 |
共享 | shared 、private 或 locked 之一。預設為 shared 。shared 快取掛載可由多個寫入器併發使用。如果存在多個寫入器,private 將建立一個新的掛載。locked 將暫停第二個寫入器,直到第一個寫入器釋放掛載。 |
來自 | 用作快取掛載基礎的構建階段、上下文或映象名稱。預設為空目錄。 |
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
。
選項 | 描述 |
---|---|
target 、dst 、destination 1 | 掛載路徑。 |
size | 指定檔案系統大小的上限。 |
RUN --mount=type=secret
此掛載型別允許構建容器訪問機密值,例如令牌或私鑰,而無需將其烘焙到映象中。
預設情況下,機密作為檔案掛載。您還可以透過設定 `env` 選項將機密作為環境變數掛載。
選項 | 描述 |
---|---|
id | 機密 ID。預設為目標路徑的基本名稱。 |
target 、dst 、destination | 將機密掛載到指定路徑。如果未設定且 `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 金鑰,並支援密碼短語。
選項 | 描述 |
---|---|
id | SSH 代理套接字或金鑰的 ID。預設為“default”。 |
target 、dst 、destination | SSH 代理套接字路徑。預設為 /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
允許控制命令在哪個網路環境中執行。
支援的網路型別有
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
指令。
如果您希望容器每次都執行相同的可執行檔案,那麼您應該考慮將 ENTRYPOINT
與 CMD
結合使用。請參閱ENTRYPOINT
。如果使用者向 docker run
指定引數,則這些引數將覆蓋 CMD
中指定的預設值,但仍使用預設的 ENTRYPOINT
。
如果 CMD
用於為 ENTRYPOINT
指令提供預設引數,則 CMD
和 ENTRYPOINT
指令都應以exec 形式指定。
注意不要混淆
RUN
和CMD
。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
指令將環境變數
設定為值
。此值將存在於構建階段的所有後續指令的環境中,並且也可以在許多指令中內聯替換。該值將解釋為其他環境變數,因此如果引號字元未轉義,它們將被刪除。與命令列解析一樣,引號和反斜槓可用於在值中包含空格。
示例
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-dir | 1.1 |
--checksum | 1.6 |
--chown | |
--chmod | 1.2 |
--link | 1.4 |
--exclude | 1.7-labs |
ADD
指令將新檔案或目錄從
複製到映象檔案系統中的路徑
。檔案和目錄可以從構建上下文、遠端 URL 或 Git 倉庫複製。
ADD
和 COPY
指令在功能上相似,但目的略有不同。瞭解更多關於ADD
和 COPY
之間的區別。
源
您可以使用 ADD
指定多個原始檔或目錄。最後一個引數必須始終是目標。例如,要將構建上下文中的兩個檔案 file1.txt
和 file2.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 檢測
的型別並相應地處理它。
- 如果
是本地檔案或目錄,則目錄內容將被複制到指定目標。請參閱從構建上下文新增檔案。 - 如果
是本地 tar 歸檔檔案,則它將被解壓縮並提取到指定目標。請參閱新增本地 tar 歸檔檔案。 - 如果
是 URL,則 URL 的內容將被下載並放置在指定目標。請參閱從 URL 新增檔案。 - 如果
是 Git 倉庫,則倉庫將被克隆到指定目標。請參閱從 Git 倉庫新增檔案。
從構建上下文新增檔案
任何不以 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
的源時,並且歸檔檔案採用已識別的壓縮格式(gzip
、bzip2
或 xz
,或未壓縮),歸檔檔案將被解壓縮並提取到指定目標。只有本地 tar 歸檔檔案會被提取。如果 tar 歸檔檔案是遠端 URL,則歸檔檔案不會被提取,而是下載並放置在目標位置。
當目錄被提取時,它具有與 `tar -x` 相同的行為。結果是以下內容的並集
- 目標路徑上已存在的任何內容,以及
- 源樹的內容,衝突將按檔案逐個解決,以新增的內容為準。
注意檔案是否被識別為已識別的壓縮格式僅基於檔案內容,而不是檔名。例如,如果一個空檔案恰好以 `.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
ADD --link
請參閱COPY --link
。
ADD --exclude
請參閱COPY --exclude
。
COPY
COPY 有兩種形式。後者形式適用於包含空白字元的路徑。
COPY [OPTIONS] <src> ... <dest>
COPY [OPTIONS] ["<src>", ... "<dest>"]
可用的 [OPTIONS]
為
COPY
指令將新檔案或目錄從
複製到映象檔案系統中的路徑
。檔案和目錄可以從構建上下文、構建階段、命名上下文或映象複製。
ADD
和 COPY
指令在功能上相似,但目的略有不同。瞭解更多關於ADD
和 COPY
之間的區別。
源
您可以使用 COPY
指定多個原始檔或目錄。最後一個引數必須始終是目標。例如,要將構建上下文中的兩個檔案 file1.txt
和 file2.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
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
的好處
即使上一層已更改,也可以使用 --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
工具的 --parents
或 rsync
的 --relative
標誌。
與 Rsync 一樣,可以透過在源路徑中插入點和斜槓(`./`)來限制保留哪些父目錄。如果存在這樣的點,則只保留其後的父目錄。這對於在階段之間使用 ` --from ` 進行復制特別有用,因為源路徑需要是絕對路徑。
# syntax=docker/dockerfile:1-labs
FROM scratch
COPY --parents ./x/./y/*.txt /parents/
# Build context:
# ./x/y/a.txt
# ./x/y/b.txt
#
# Output:
# /parents/y/a.txt
# /parents/y/b.txt
請注意,在未指定 --parents
標誌的情況下,任何檔名衝突都將使 Linux cp
操作失敗並顯示明確的錯誤訊息(cp: will not overwrite just-created './x/a.txt' with './y/a.txt'
),而 Buildkit 將在目標位置靜默覆蓋目標檔案。
雖然可以為僅包含一個 `src` 條目的 `COPY` 指令保留目錄結構,但通常將結果映象中的層數保持儘可能低更有益。因此,透過 ` --parents ` 標誌,Buildkit 能夠將多個 `COPY` 指令打包在一起,保持目錄結構不變。
COPY --exclude
注意尚未在穩定語法中提供,請使用
docker/dockerfile:1-labs
版本。
COPY [--exclude=<path> ...] <src> ... <dest>
--exclude
標誌允許您指定要排除檔案的路徑表示式。
路徑表示式遵循與
相同的格式,支援萬用字元並使用 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
引數傳遞給入口點。您可以使用 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 環境變數,並將忽略任何 CMD
或 docker 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 如何互動
CMD
和 ENTRYPOINT
指令都定義了執行容器時要執行的命令。有一些規則描述了它們的協作。
Dockerfile 應該至少指定一個
CMD
或ENTRYPOINT
命令。當將容器用作可執行檔案時,應該定義
ENTRYPOINT
。CMD
應該用作定義ENTRYPOINT
命令的預設引數或在容器中執行臨時命令的方式。當使用替代引數執行容器時,
CMD
將被覆蓋。
下表顯示了不同 ENTRYPOINT
/ CMD
組合執行的命令。
無 ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT ["exec_entry", "p1_entry"] | |
---|---|---|---|
無 CMD | 錯誤,不允許 | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
CMD ["exec_cmd", "p1_cmd"] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_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_entry | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |
注意如果
CMD
是從基礎映象定義的,設定ENTRYPOINT
會將CMD
重置為空值。在這種情況下,CMD
必須在當前映象中定義才能有值。
VOLUME
VOLUME ["/data"]
VOLUME
指令建立指定名稱的掛載點,並將其標記為包含來自本機主機或其他容器的外部掛載卷。該值可以是 JSON 陣列,VOLUME ["/var/log/"]
,或者帶有多個引數的純字串,例如 VOLUME /var/log
或 VOLUME /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
指令,並在執行時執行相關的 ENTRYPOINT
和 CMD
命令。
請注意,當為使用者指定組時,使用者將僅擁有指定的組成員資格。任何其他配置的組成員資格都將被忽略。
警告當用戶沒有主組時,映象(或下一個指令)將以
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 中其後所有 RUN
、CMD
、ENTRYPOINT
、COPY
和 ADD
指令的工作目錄。如果 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 變數
您可以使用 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
,因為它是在第 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/amd64
、linux/arm/v7
、windows/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_CACHE 2 | 布林值 | 是否將快取元資料內聯到映象配置中。 |
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=
相同,因此如果
發生變化,您會遇到快取未命中。
在相同的命令列下考慮另一個示例:
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 應用程式構建器,它將需要將應用程式原始碼新增到特定目錄中,並且可能需要在此之後呼叫構建指令碼。您現在不能僅僅呼叫 ADD
和 RUN
,因為您還沒有訪問應用程式原始碼的許可權,並且每個應用程式構建都會有所不同。您可以簡單地嚮應用程式開發人員提供一個樣板 Dockerfile 以複製貼上到其應用程式中,但這效率低下、容易出錯且難以更新,因為它與應用程式特定程式碼混合在一起。
解決方案是使用 ONBUILD
註冊高階指令,以便在後續構建階段執行。
其工作原理如下:
- 當遇到
ONBUILD
指令時,構建器會向正在構建的映象的元資料新增一個觸發器。該指令不會以其他方式影響當前構建。 - 在構建結束時,所有觸發器列表儲存在映象清單中,鍵為
OnBuild
。它們可以使用docker inspect
命令進行檢查。 - 稍後,該映象可以使用
FROM
指令作為新構建的基礎。作為處理FROM
指令的一部分,下游構建器會查詢ONBUILD
觸發器,並按其註冊的相同順序執行它們。如果任何觸發器失敗,FROM
指令將中止,這反過來會導致構建失敗。如果所有觸發器都成功,FROM
指令將完成,並且構建照常繼續。 - 觸發器執行後會從最終映象中清除。換句話說,它們不會被“孫子”構建繼承。
例如,您可以新增如下內容:
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
指令不得觸發FROM
或MAINTAINER
指令。
STOPSIGNAL
STOPSIGNAL signal
STOPSIGNAL
指令設定將傳送到容器以退出的系統呼叫訊號。此訊號可以是 SIG
格式的訊號名稱,例如 SIGKILL
,也可以是與核心系統呼叫表中的位置匹配的無符號數字,例如 9
。如果未定義,預設為 SIGTERM
。
可以使用 docker run
和 docker 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:cmd
和 powershell
,以及包括 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
指令的影響:RUN
、CMD
和 ENTRYPOINT
。
以下示例是在 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,例如 zsh
、csh
、tcsh
等,SHELL
指令也可以在 Linux 上使用。
Here-Documents(此處文件)
Here-documents 允許將後續 Dockerfile 行重定向到 RUN
或 COPY
命令的輸入。如果此類命令包含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 world
的 greeting.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 示例,請參閱: