構建最佳實踐

使用多階段構建

多階段構建可以透過在映象構建和最終輸出之間建立更清晰的分離來減小最終映象的大小。將 Dockerfile 指令拆分為不同的階段,以確保最終輸出只包含執行應用程式所需的檔案。

使用多階段還可以透過並行執行構建步驟來提高構建效率。

有關更多資訊,請參閱多階段構建

建立可重用階段

如果您有多個具有很多共同點的映象,請考慮建立一個包含共享元件的可重用階段,並將您的獨特階段基於該階段。Docker 只需構建一次公共階段。這意味著您的派生映象可以更有效地利用 Docker 主機上的記憶體,並更快地載入。

維護一個公共基礎階段(“不要重複自己”)也比擁有多個執行類似事情的不同階段更容易。

選擇正確的基礎映象

實現安全映象的第一步是選擇正確的基礎映象。選擇映象時,請確保它來自受信任的來源並保持其小巧。

  • Docker 官方映象是經過精選的集合,它們具有清晰的文件,遵循最佳實踐,並定期更新。它們為許多應用程式提供了可靠的起點。

  • 經過驗證的釋出者映象是與 Docker 合作的組織釋出和維護的高質量映象,Docker 會驗證其倉庫中內容的真實性。

  • Docker 贊助的開源映象是透過開源計劃由 Docker 贊助的開源專案釋出和維護的。

選擇基礎映象時,請注意指示該映象屬於這些程式的徽章。

Docker Hub Official and Verified Publisher images

從 Dockerfile 構建自己的映象時,請確保選擇符合您要求的最小基礎映象。較小的基礎映象不僅提供了可移植性和快速下載,而且還縮小了映象的大小,並最大程度地減少了透過依賴項引入的漏洞數量。

您還應該考慮使用兩種型別的基礎映象:一種用於構建和單元測試,另一種(通常更精簡)用於生產。在開發的後期階段,您的映象可能不需要構建工具,例如編譯器、構建系統和除錯工具。一個具有最小依賴項的小映象可以顯著降低攻擊面。

經常重建映象

Docker 映象是不可變的。構建映象是在那一刻對該映象進行快照。這包括您在構建中使用的任何基礎映象、庫或其他軟體。為了使您的映象保持最新和安全,請確保經常使用更新的依賴項重建您的映象。

為了確保您在構建中獲得最新版本的依賴項,可以使用 --no-cache 選項來避免快取命中。

$ docker build --no-cache -t my-image:my-tag .

以下 Dockerfile 使用 ubuntu 映象的 24.04 標籤。隨著時間的推移,該標籤可能會解析為不同底層版本的 ubuntu 映象,因為釋出者會使用新的安全補丁和更新的庫重建映象。使用 --no-cache,您可以避免快取命中並確保重新下載基礎映象和依賴項。

# syntax=docker/dockerfile:1
FROM ubuntu:24.04
RUN apt-get -y update && apt-get install -y --no-install-recommends python3

還可以考慮鎖定基礎映象版本

使用 .dockerignore 排除

要排除與構建無關的檔案,而無需重組您的源倉庫,請使用 .dockerignore 檔案。此檔案支援類似於 .gitignore 檔案的排除模式。

例如,要排除所有副檔名為 .md 的檔案

*.md

有關如何建立的資訊,請參閱Dockerignore 檔案

建立臨時容器

您的 Dockerfile 定義的映象應該生成儘可能短暫的容器。短暫意味著容器可以停止和銷燬,然後以最少的設定和配置重建和替換。

請參閱《十二因素應用》方法論中的“程序”,以瞭解以這種無狀態方式執行容器的動機。

不要安裝不必要的軟體包

避免安裝額外的或不必要的軟體包,僅僅因為它們可能很好用。例如,您不需要在資料庫映象中包含文字編輯器。

當您避免安裝額外或不必要的軟體包時,您的映象會降低複雜性、減少依賴項、減小檔案大小並縮短構建時間。

解耦應用程式

每個容器都應該只有一個關注點。將應用程式解耦到多個容器中,可以更輕鬆地進行橫向擴充套件和容器重用。例如,一個 Web 應用程式堆疊可能由三個獨立的容器組成,每個容器都有自己獨特的映象,以解耦的方式管理 Web 應用程式、資料庫和記憶體快取。

將每個容器限制為一個程序是一個很好的經驗法則,但它並不是一個硬性規定。例如,容器不僅可以與 init 程序一起生成,某些程式也可能會自行生成額外的程序。例如,Celery 可以生成多個工作程序,而 Apache 可以為每個請求建立一個程序。

請根據您的最佳判斷,使容器儘可能乾淨和模組化。如果容器之間相互依賴,您可以使用Docker 容器網路來確保這些容器能夠通訊。

對多行引數進行排序

在可能的情況下,按字母數字順序對多行引數進行排序,以使維護更容易。這有助於避免包的重複,並使列表更容易更新。這還使得 PR 更易於閱讀和審查。在反斜槓 (\) 前新增一個空格也很有幫助。

這是一個來自 buildpack-deps 映象的示例

RUN apt-get update && apt-get install -y --no-install-recommends \
  bzr \
  cvs \
  git \
  mercurial \
  subversion \
  && rm -rf /var/lib/apt/lists/*

利用構建快取

構建映象時,Docker 會逐步執行 Dockerfile 中的指令,並按指定順序執行每個指令。對於每個指令,Docker 會檢查它是否可以重用構建快取中的指令。

瞭解構建快取的工作原理以及快取失效的發生方式對於確保更快的構建至關重要。有關 Docker 構建快取以及如何最佳化構建的更多資訊,請參閱Docker 構建快取

鎖定基礎映象版本

映象標籤是可變的,這意味著釋出者可以更新標籤以指向新映象。這很有用,因為它允許釋出者更新標籤以指向新版本的映象。作為映象消費者,這意味著您在重新構建映象時會自動獲得新版本。

例如,如果您在 Dockerfile 中指定 FROM alpine:3.21,則 3.21 將解析為 3.21 的最新補丁版本。

# syntax=docker/dockerfile:1
FROM alpine:3.21

在某個時間點,3.21 標籤可能指向映象的 3.21.1 版本。如果您在 3 個月後重建映象,相同的標籤可能指向不同的版本,例如 3.21.4。這種釋出工作流是最佳實踐,大多數釋出者都使用這種標籤策略,但它並未強制執行。

這帶來的缺點是,您無法保證每次構建都獲得相同的結果。這可能導致破壞性更改,並且意味著您也沒有確切使用過的映象版本的審計追蹤。

為了完全確保您的供應鏈完整性,您可以將映象版本鎖定到特定的摘要。透過將映象鎖定到摘要,您保證始終使用相同的映象版本,即使釋出者用新映象替換了標籤。例如,以下 Dockerfile 將 Alpine 映象鎖定到與之前相同的標籤 3.21,但這次也包含摘要引用。

# syntax=docker/dockerfile:1
FROM alpine:3.21@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c

使用此 Dockerfile,即使釋出者更新了 3.21 標籤,您的構建仍將使用鎖定的映象版本:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c

雖然這有助於您避免意外更改,但每次您想要更新基礎映象版本時都必須手動查詢和包含映象摘要,這更繁瑣。而且您選擇放棄自動化安全修復,這很可能是您想要獲得的。

Docker Scout 預設的最新基礎映象策略會檢查您正在使用的基礎映象版本是否確實是最新版本。此策略還會檢查 Dockerfile 中鎖定的摘要是否與正確版本對應。如果釋出者更新了您已鎖定的映象,策略評估將返回不合規狀態,表明您應該更新您的映象。

Docker Scout 還支援自動化修復工作流,以保持您的基礎映象最新。當有新的映象摘要可用時,Docker Scout 可以自動在您的倉庫中發起拉取請求,以更新您的 Dockerfile 以使用最新版本。這比使用自動更改版本的標籤更好,因為您可以控制,並且您可以擁有更改發生的時間和方式的審計追蹤。

有關使用 Docker Scout 自動更新基礎映象的更多資訊,請參閱修復

在 CI 中構建和測試映象

當您將更改提交到原始碼管理或建立拉取請求時,使用GitHub Actions或其他 CI/CD 管道來自動構建和標記 Docker 映象並對其進行測試。

Dockerfile 指令

請遵循以下關於如何正確使用 Dockerfile 指令的建議,以建立高效且可維護的 Dockerfile。

提示

為了改進 Visual Studio Code 中 Dockerfile 的 linting、程式碼導航和漏洞掃描,請參閱 Docker VS Code 擴充套件

FROM

在可能的情況下,使用當前的官方映象作為您映象的基礎。Docker 推薦使用 Alpine 映象,因為它受到嚴格控制且體積小(目前小於 6 MB),同時仍然是一個完整的 Linux 發行版。

有關 FROM 指令的更多資訊,請參閱 Dockerfile 參考中的 FROM 指令

LABEL

您可以為映象新增標籤,以幫助按專案組織映象、記錄許可資訊、輔助自動化或出於其他原因。對於每個標籤,新增以 LABEL 開頭的一行,包含一個或多個鍵值對。以下示例顯示了不同的可接受格式。解釋性註釋已內聯。

帶有空格的字串必須用引號引起來,或者空格必須轉義。內部引號字元(")也必須轉義。例如

# Set one or more individual labels
LABEL com.example.version="0.0.1-beta"
LABEL vendor1="ACME Incorporated"
LABEL vendor2=ZENITH\ Incorporated
LABEL com.example.release-date="2015-02-12"
LABEL com.example.version.is-production=""

一個映象可以有多個標籤。在 Docker 1.10 之前,建議將所有標籤組合到單個 LABEL 指令中,以防止建立額外的層。這不再是必需的,但仍然支援組合標籤。例如

# Set multiple labels on one line
LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12"

上面的示例也可以寫成

# Set multiple labels at once, using line-continuation characters to break long lines
LABEL vendor=ACME\ Incorporated \
      com.example.is-beta= \
      com.example.is-production="" \
      com.example.version="0.0.1-beta" \
      com.example.release-date="2015-02-12"

請參閱理解物件標籤,瞭解可接受的標籤鍵和值指南。有關查詢標籤的資訊,請參閱管理物件上的標籤中與過濾相關的專案。另請參閱 Dockerfile 參考中的LABEL

RUN

將長或複雜的 RUN 語句拆分成多行,並用反斜槓分隔,以使 Dockerfile 更具可讀性、可理解性和可維護性。

例如,您可以使用 && 運算子連結命令,並使用跳脫字元將長命令分解為多行。

RUN apt-get update && apt-get install -y --no-install-recommends \
    package-bar \
    package-baz \
    package-foo

預設情況下,反斜槓會轉義換行符,但您可以使用escape 指令進行更改。

您還可以使用 here-document 來執行多個命令,而無需使用管道運算子將它們連結起來

RUN <<EOF
apt-get update
apt-get install -y --no-install-recommends \
    package-bar \
    package-baz \
    package-foo
EOF

有關 RUN 的更多資訊,請參閱 Dockerfile 參考中的 RUN 指令

apt-get

在基於 Debian 的映象中,RUN 指令的一個常見用例是使用 apt-get 安裝軟體。由於 apt-get 安裝軟體包,RUN apt-get 命令有幾個反直覺的行為需要注意。

始終在同一個 RUN 語句中結合 RUN apt-get updateapt-get install。例如

RUN apt-get update && apt-get install -y --no-install-recommends \
    package-bar \
    package-baz \
    package-foo

RUN 語句中單獨使用 apt-get update 會導致快取問題,並使後續的 apt-get install 指令失敗。例如,在以下 Dockerfile 中將發生此問題

# syntax=docker/dockerfile:1

FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y --no-install-recommends curl

構建映象後,所有層都儲存在 Docker 快取中。假設您稍後透過新增額外的包來修改 apt-get install,如以下 Dockerfile 所示

# syntax=docker/dockerfile:1

FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y --no-install-recommends curl nginx

Docker 將初始指令和修改後的指令視為相同,並重用先前步驟中的快取。因此,apt-get update 不會被執行,因為構建使用了快取版本。由於 apt-get update 未執行,您的構建可能會獲取過時的 curlnginx 包版本。

使用 RUN apt-get update && apt-get install -y --no-install-recommends 可確保您的 Dockerfile 安裝最新軟體包版本,無需進一步編碼或手動干預。這種技術稱為快取失效。您還可以透過指定軟體包版本來實現快取失效。這稱為版本固定。例如

RUN apt-get update && apt-get install -y --no-install-recommends \
    package-bar \
    package-baz \
    package-foo=1.3.*

版本鎖定強制構建檢索特定版本,而不管快取中有什麼。此技術還可以減少由於所需軟體包中意外更改而導致的失敗。

以下是一個格式良好的 RUN 指令,它演示了所有 apt-get 建議。

RUN apt-get update && apt-get install -y --no-install-recommends \
    aufs-tools \
    automake \
    build-essential \
    curl \
    dpkg-sig \
    libcap-dev \
    libsqlite3-dev \
    mercurial \
    reprepro \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.* \
    && rm -rf /var/lib/apt/lists/*

s3cmd 引數指定版本 1.1.*。如果映象之前使用了舊版本,指定新版本會導致 apt-get update 的快取失效,並確保安裝新版本。每行列出軟體包還可以防止軟體包重複的錯誤。

此外,透過刪除 /var/lib/apt/lists 清理 apt 快取,可以減小映象大小,因為 apt 快取不會儲存在層中。由於 RUN 語句以 apt-get update 開頭,因此軟體包快取始終在 apt-get install 之前重新整理。

官方 Debian 和 Ubuntu 映象會自動執行 apt-get clean,因此無需顯式呼叫。

使用管道

某些 RUN 命令依賴於將一個命令的輸出透過管道傳輸到另一個命令的能力,使用管道字元 (|),如以下示例所示

RUN wget -O - https://some.site | wc -l > /number

Docker 使用 /bin/sh -c 直譯器執行這些命令,該直譯器僅評估管道中最後一個操作的退出程式碼來確定成功。在上面的示例中,只要 wc -l 命令成功,即使 wget 命令失敗,此構建步驟也會成功並生成一個新映象。

如果希望命令因管道中任何階段的錯誤而失敗,請在命令前加上 set -o pipefail &&,以確保意外錯誤可以阻止構建意外成功。例如

RUN set -o pipefail && wget -O - https://some.site | wc -l > /number
注意

並非所有 shell 都支援 -o pipefail 選項。

在基於 Debian 的映象(如 dash shell)的情況下,請考慮使用 RUNexec形式來明確選擇支援 pipefail 選項的 shell。例如

RUN ["/bin/bash", "-c", "set -o pipefail && wget -O - https://some.site | wc -l > /number"]

CMD

CMD 指令應用於執行映象中包含的軟體及其任何引數。CMD 幾乎總是以 CMD ["executable", "param1", "param2"] 的形式使用。因此,如果映象是用於 Apache 和 Rails 等服務,您將執行類似 CMD ["apache2","-DFOREGROUND"] 的命令。實際上,對於任何基於服務的映象,都建議使用這種形式的指令。

在大多數其他情況下,CMD 應該給定一個互動式 shell,例如 bash、Python 和 perl。例如,CMD ["perl", "-de0"]CMD ["python"]CMD ["php", "-a"]。使用這種形式意味著當您執行諸如 docker run -it python 之類的命令時,您將進入一個可用的 shell,隨時可以使用。CMD 很少與 ENTRYPOINT 結合使用 CMD ["param", "param"] 的方式,除非您和您的預期使用者已經非常熟悉 ENTRYPOINT 的工作原理。

有關 CMD 的更多資訊,請參閱 Dockerfile 參考中的 CMD 指令

EXPOSE

EXPOSE 指令指示容器偵聽連線的埠。因此,您應該為您的應用程式使用常用、傳統的埠。例如,包含 Apache Web 伺服器的映象將使用 EXPOSE 80,而包含 MongoDB 的映象將使用 EXPOSE 27017,依此類推。

對於外部訪問,您的使用者可以執行 docker run,並帶有一個標誌,指示如何將指定埠對映到他們選擇的埠。對於容器連結,Docker 提供環境變數,用於從接收方容器返回到源容器的路徑(例如,MYSQL_PORT_3306_TCP)。

有關 EXPOSE 的更多資訊,請參閱 Dockerfile 參考中的 EXPOSE 指令

ENV

為了讓新軟體更容易執行,您可以使用 ENV 更新容器安裝軟體的 PATH 環境變數。例如,ENV PATH=/usr/local/nginx/bin:$PATH 可確保 CMD ["nginx"] 正常工作。

ENV 指令對於提供您想要容器化的服務所需的特定環境變數也很有用,例如 Postgres 的 PGDATA

最後,ENV 還可以用於設定常用的版本號,以便更易於維護版本升級,如以下示例所示

ENV PG_MAJOR=9.3
ENV PG_VERSION=9.3.4
RUN curl -SL https://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgres &&
ENV PATH=/usr/local/postgres-$PG_MAJOR/bin:$PATH

與在程式中擁有常量變數而非硬編碼值類似,這種方法允許您透過更改單個 ENV 指令來自動提升容器中軟體的版本。

每行 ENV 都會建立一個新的中間層,就像 RUN 命令一樣。這意味著即使您在未來的層中取消設定環境變數,它仍然會保留在該層中,並且其值可以被匯出。您可以透過建立類似如下的 Dockerfile,然後構建它來測試這一點。

# syntax=docker/dockerfile:1
FROM alpine
ENV ADMIN_USER="mark"
RUN echo $ADMIN_USER > ./mark
RUN unset ADMIN_USER
$ docker run --rm test sh -c 'echo $ADMIN_USER'

mark

為了防止這種情況並取消設定環境變數,請使用帶有 shell 命令的 RUN 命令,以在一個層中設定、使用和取消設定變數。您可以使用 ;&& 分隔命令。如果使用第二種方法,並且其中一個命令失敗,則 docker build 也會失敗。這通常是一個好主意。使用 \ 作為 Linux Dockerfile 的行繼續字元可以提高可讀性。您還可以將所有命令放入一個 shell 指令碼中,並讓 RUN 命令只執行該 shell 指令碼。

# syntax=docker/dockerfile:1
FROM alpine
RUN export ADMIN_USER="mark" \
    && echo $ADMIN_USER > ./mark \
    && unset ADMIN_USER
CMD sh
$ docker run --rm test sh -c 'echo $ADMIN_USER'

有關 ENV 的更多資訊,請參閱 Dockerfile 參考中的 ENV 指令

ADD 或 COPY

ADDCOPY 功能相似。COPY 支援從構建上下文多階段構建中的某個階段將檔案基本複製到容器中。ADD 支援從遠端 HTTPS 和 Git URL 獲取檔案,並在從構建上下文新增檔案時自動提取 tar 檔案。

在多階段構建中,您通常希望使用 COPY 將檔案從一個階段複製到另一個階段。如果您需要將構建上下文中的檔案臨時新增到容器中以執行 RUN 指令,您通常可以用繫結掛載代替 COPY 指令。例如,要臨時新增 requirements.txt 檔案用於 RUN pip install 指令

RUN --mount=type=bind,source=requirements.txt,target=/tmp/requirements.txt \
    pip install --requirement /tmp/requirements.txt

對於將構建上下文中的檔案包含到容器中,繫結掛載比 COPY 更高效。請注意,繫結掛載的檔案只為單個 RUN 指令臨時新增,並且不會保留在最終映象中。如果需要將構建上下文中的檔案包含在最終映象中,請使用 COPY

當您需要在構建過程中下載遠端工件時,ADD 指令是最佳選擇。ADD 比手動使用 wgettar 等工具新增檔案更好,因為它能確保更精確的構建快取。ADD 還內建支援遠端資源的校驗和驗證,以及用於從 Git URL 解析分支、標籤和子目錄的協議。

以下示例使用 ADD 下載 .NET 安裝程式。與多階段構建結合使用,最終階段只剩下 .NET 執行時,沒有中間檔案。

# syntax=docker/dockerfile:1

FROM scratch AS src
ARG DOTNET_VERSION=8.0.0-preview.6.23329.7
ADD --checksum=sha256:270d731bd08040c6a3228115de1f74b91cf441c584139ff8f8f6503447cebdbb \
    https://dotnetcli.azureedge.net/dotnet/Runtime/$DOTNET_VERSION/dotnet-runtime-$DOTNET_VERSION-linux-arm64.tar.gz /dotnet.tar.gz

FROM mcr.microsoft.com/dotnet/runtime-deps:8.0.0-preview.6-bookworm-slim-arm64v8 AS installer

# Retrieve .NET Runtime
RUN --mount=from=src,target=/src <<EOF
mkdir -p /dotnet
tar -oxzf /src/dotnet.tar.gz -C /dotnet
EOF

FROM mcr.microsoft.com/dotnet/runtime-deps:8.0.0-preview.6-bookworm-slim-arm64v8

COPY --from=installer /dotnet /usr/share/dotnet
RUN ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet

有關 ADDCOPY 的更多資訊,請參閱以下內容

ENTRYPOINT

ENTRYPOINT 的最佳用途是設定映象的主命令,允許該映象像該命令一樣執行,然後使用 CMD 作為預設標誌。

以下是命令列工具 s3cmd 映象的示例

ENTRYPOINT ["s3cmd"]
CMD ["--help"]

您可以使用以下命令執行映象並顯示命令的幫助資訊

$ docker run s3cmd

或者,您可以使用正確的引數執行命令,如以下示例所示

$ docker run s3cmd ls s3://mybucket

這很有用,因為映象名稱可以兼作二進位制檔案的引用,如上面的命令所示。

ENTRYPOINT 指令也可以與輔助指令碼結合使用,即使啟動工具可能需要多個步驟,它也能以類似於上述命令的方式執行。

例如,Postgres 官方映象使用以下指令碼作為其 ENTRYPOINT

#!/bin/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 "$@"

此指令碼使用 exec Bash 命令,以便最終執行的應用程式成為容器的 PID 1。這允許應用程式接收發送到容器的任何 Unix 訊號。有關更多資訊,請參閱 ENTRYPOINT 參考

在以下示例中,一個輔助指令碼被複制到容器中,並在容器啟動時透過 ENTRYPOINT 執行

COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["postgres"]

此指令碼允許您以多種方式與 Postgres 互動。

它可以簡單地啟動 Postgres

$ docker run postgres

或者,您可以用來執行 Postgres 並向伺服器傳遞引數

$ docker run postgres postgres --help

最後,您可以使用它啟動一個完全不同的工具,例如 Bash

$ docker run --rm -it postgres bash

有關 ENTRYPOINT 的更多資訊,請參閱 Dockerfile 參考中的 ENTRYPOINT 指令

VOLUME

您應該使用 VOLUME 指令來公開任何資料庫儲存區域、配置儲存或由 Docker 容器建立的檔案和資料夾。強烈建議您對映象中任何可變或使用者可維護的部分組合使用 VOLUME

有關 VOLUME 的更多資訊,請參閱 Dockerfile 參考中的 VOLUME 指令

USER

如果服務可以在沒有特權的情況下執行,請使用 USER 切換到非 root 使用者。首先在 Dockerfile 中建立使用者和組,例如以下示例

RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres
注意

考慮一個顯式的 UID/GID。

映象中的使用者和組被分配一個非確定性的 UID/GID,這意味著無論映象重建如何,都會分配“下一個”UID/GID。因此,如果至關重要,您應該分配一個顯式的 UID/GID。

注意

由於 Go archive/tar 包在處理稀疏檔案時存在一個未解決的 bug,在 Docker 容器內嘗試建立具有非常大 UID 的使用者可能會導致磁碟耗盡,因為容器層中的 /var/log/faillog 會被 NULL (\0) 字元填滿。一個解決方法是向 useradd 傳遞 --no-log-init 標誌。Debian/Ubuntu 的 adduser 包裝器不支援此標誌。

避免安裝或使用 sudo,因為它具有不可預測的 TTY 和訊號轉發行為,可能導致問題。如果您絕對需要類似於 sudo 的功能,例如以 root 身份初始化守護程序但以非 root 身份執行,請考慮使用 “gosu”

最後,為了減少層和複雜性,避免頻繁切換 USER

有關 USER 的更多資訊,請參閱 Dockerfile 參考中的 USER 指令

WORKDIR

為了清晰和可靠,您應該始終為您的 WORKDIR 使用絕對路徑。此外,您應該使用 WORKDIR,而不是大量使用諸如 RUN cd … && do-something 這樣的指令,這些指令難以閱讀、排除故障和維護。

有關 WORKDIR 的更多資訊,請參閱 Dockerfile 參考中的 WORKDIR 指令

ONBUILD

ONBUILD 命令在當前 Dockerfile 構建完成後執行。ONBUILD 在從當前映象派生的任何子映象中執行。可以將 ONBUILD 命令視為父 Dockerfile 給子 Dockerfile 的指令。

Docker 構建在任何子 Dockerfile 中的任何命令之前執行 ONBUILD 命令。

ONBUILD 對於那些將從給定映象構建的映象非常有用。例如,您將為語言堆疊映象使用 ONBUILD,該映象在 Dockerfile 中構建用該語言編寫的任意使用者軟體,如您在Ruby 的 ONBUILD 變體中看到的那樣。

使用 ONBUILD 構建的映象應該獲得一個單獨的標籤。例如,ruby:1.9-onbuildruby:2.0-onbuild

ADDCOPY 放入 ONBUILD 時要小心。如果新構建的上下文缺少要新增的資源,`onbuild 映象會災難性地失敗。如上所述新增單獨的標籤有助於緩解此問題,允許 Dockerfile 作者做出選擇。

有關 ONBUILD 的更多資訊,請參閱 Dockerfile 參考中的 ONBUILD 指令