建置映像檔時,Docker 會逐步執行 Dockerfile 中的指示,並按指定的順序執行每個指示。對於每個指示,Docker 會檢查是否可以從建置快取中重複使用該指示。
了解建置快取的工作原理以及快取失效發生的方式,對於確保更快的建置至關重要。有關 Docker 建置快取以及如何最佳化建置的詳細資訊,請參閱Docker 建置快取 。
映像檔標籤是可變的,表示發布者可以更新標籤以指向新的映像檔。這很有用,因為它允許發布者更新標籤以指向映像檔的較新版本。作為映像檔使用者,這表示您在重建映像檔時會自動取得新版本。
例如,如果您在 Dockerfile 中指定 FROM alpine:3.19
,則 3.19
會解析為 3.19
的最新修補程式版本。
在某個時間點,3.19
標籤可能指向映像檔的 3.19.1 版本。如果您在 3 個月後重建映像檔,則相同的標籤可能指向不同的版本,例如 3.19.4。此發布工作流程是最佳實務,大多數發布者都使用此標記策略,但它並非強制性的。
這樣做的缺點是您無法保證每次建置都得到相同的結果。這可能會導致重大變更,也表示您沒有正在使用的確切映像檔版本的審計追蹤。
為了完全確保您的供應鏈完整性,您可以將映像檔版本鎖定到特定的摘要。透過將映像檔鎖定到摘要,即使發布者用新映像檔替換標籤,您也可以保證始終使用相同的映像檔版本。例如,以下 Dockerfile 將 Alpine 映像檔鎖定到與先前相同的標籤 3.19
,但這次也使用摘要參考。
使用此 Dockerfile,即使發布者更新 3.19
標籤,您的建置仍將使用鎖定的映像檔版本:13b7e62e8df80264dbb747995705a986aa530415763a6c58f84a3ca8af9a5bcd
。
雖然這有助於您避免意外變更,但每次您想要更新它時,都必須手動查閱並包含基礎映像檔版本的映像檔摘要,這也很麻煩。而且您選擇退出自動安全修復,這可能是您想要獲得的。
Docker Scout 預設的最新基礎映像檔 策略 會檢查您正在使用的基礎映像檔版本是否確實是最新版本。此策略還會檢查 Dockerfile 中鎖定的摘要是否對應到正確的版本。如果您鎖定的映像檔已被發布者更新,則策略評估會傳回不符合規範的狀態,表示您應該更新映像檔。
Docker Scout 也支援自動修復工作流程,以保持您的基礎映像檔為最新狀態。當新的映像檔摘要可用時,Docker Scout 可以自動在您的儲存庫上提出提取請求,以更新您的 Dockerfile 以使用最新版本。這比使用自動變更版本的標籤更好,因為您可以掌控一切,並且可以追蹤變更發生的時間和方式。
有關使用 Docker Scout 自動更新基礎映像檔的詳細資訊,請參閱修復 。
當您將變更簽入程式碼控制或建立提取請求時,請使用GitHub Actions 或其他 CI/CD 管線來自動建置和標記 Docker 映像檔並進行測試。
請遵循這些關於如何正確使用Dockerfile 指示 來建立有效且可維護的 Dockerfile 的建議。
盡可能使用目前的官方映像檔作為映像檔的基礎。Docker 建議使用Alpine 映像檔 ,因為它受到嚴格控制且體積小(目前小於 6 MB),同時仍然是完整的 Linux 發行版。
有關 FROM
指示的詳細資訊,請參閱FROM 指示的 Dockerfile 參考 。
您可以將標籤新增到映像檔中,以協助按專案組織映像檔、記錄授權資訊、協助自動化或其他原因。對於每個標籤,請新增以 LABEL
開頭的行,其中包含一或多個鍵值對。以下範例顯示不同的可接受格式。說明性註解包含在內聯中。
包含空格的字串必須用引號括起來,或必須逸出空格。內部引號字元 ("
) 也必須逸出。例如
一個映像檔可以有多個標籤。在 Docker 1.10 之前,建議將所有標籤合併到單個 LABEL
指令中,以防止建立額外的層。現在不再需要這樣做了,但仍然支援合併標籤。例如:
以上範例也可以寫成:
有關可接受的標籤鍵和值的準則,請參閱了解物件標籤 。有關查詢標籤的資訊,請參考管理物件上的標籤 中與篩選相關的項目。另請參閱 Dockerfile 參考中的LABEL 。
將長或複雜的 RUN
陳述式用反斜線分隔成多行,以提高 Dockerfile 的可讀性、可理解性和可維護性。
例如,您可以使用 &&
運算子串連指令,並使用跳脫字元將長指令斷成多行。
預設情況下,反斜線會跳脫換行字元,但您可以使用escape
指令 來更改它。
您也可以使用 here 文件來執行多個指令,而無需使用管道運算子將它們串連起來
有關 RUN
的更多資訊,請參閱RUN 指令的 Dockerfile 參考 。
在基於 Debian 的映像檔中,RUN
指令的一個常見用例是使用 apt-get
安裝軟體。由於 apt-get
會安裝套件,因此 RUN apt-get
指令有一些違反直覺的行為需要注意。
始終在同一個 RUN
陳述式中將 RUN apt-get update
與 apt-get install
結合使用。例如:
在 RUN
陳述式中單獨使用 apt-get update
會導致快取問題,並導致後續的 apt-get install
指令失敗。例如,這個問題會在以下 Dockerfile 中發生
建置映像檔後,所有層都會位於 Docker 快取中。假設您稍後修改 apt-get install
,方法是新增一個額外的套件,如下列 Dockerfile 所示
Docker 將初始指令和修改後的指令視為相同,並重複使用先前步驟中的快取。因此,不會執行 apt-get update
,因為建置使用的是快取版本。由於未執行 apt-get update
,因此您的建置可能會取得過時的 curl
和 nginx
套件版本。
使用 RUN apt-get update && apt-get install -y --no-install-recommends
可確保您的 Dockerfile 安裝最新的套件版本,而無需進一步編碼或手動干預。這種技術稱為快取清除。您也可以透過指定套件版本來實現快取清除。這稱為版本鎖定。例如:
版本鎖定會強制建置擷取特定版本,而不考慮快取中的內容。這種技術還可以減少因所需套件的意外變更而導致的失敗。
以下是一個格式良好的 RUN
指令,示範了所有 apt-get
建議。
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
指令依賴於使用管道字元 (|
) 將一個指令的輸出管道傳輸到另一個指令的能力,如下例所示
Docker 使用 /bin/sh -c
直譯器執行這些指令,該直譯器僅評估管道中最後一個操作的結束代碼來判斷是否成功。在上面的範例中,只要 wc -l
指令成功,即使 wget
指令失敗,此建置步驟也會成功並產生新的映像檔。
如果您希望指令因管道中任何階段的錯誤而失敗,請在前面加上 set -o pipefail &&
,以確保意外錯誤防止建置意外成功。例如:
**注意**
並非所有 shell 都支援 -o pipefail
選項。
在基於 Debian 的映像檔上的 dash
shell 等情況下,請考慮使用 RUN
的 _exec_ 形式來明確選擇支援 pipefail
選項的 shell。例如:
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,隨時可以使用。除非您和您的預期使用者已經非常熟悉 ENTRYPOINT
的工作原理,否則很少會以 CMD ["param", "param"]
的方式與ENTRYPOINT
結合使用 CMD
。
有關 CMD
的更多資訊,請參閱CMD 指令的 Dockerfile 參考 。
EXPOSE
指令指示容器在其上偵聽連線的埠。因此,您應該為您的應用程式使用常見的傳統埠。例如,包含 Apache Web 伺服器的映像檔將使用 EXPOSE 80
,而包含 MongoDB 的映像檔將使用 EXPOSE 27017
,依此類推。
對於外部存取,您的使用者可以執行帶有旗標的 docker run
,指示如何將指定的埠映射到他們選擇的埠。對於容器連結,Docker 提供了從接收容器返回到來源的路徑的環境變數(例如,MYSQL_PORT_3306_TCP
)。
有關 EXPOSE
的更多資訊,請參閱EXPOSE 指令的 Dockerfile 參考 。
為了更容易執行新的軟體,您可以使用 ENV
更新容器安裝的軟體的 PATH
環境變數。例如,ENV PATH=/usr/local/nginx/bin:$PATH
可確保 CMD ["nginx"]
正常工作。
ENV
指令還可用於提供您要容器化的服務所需的特定環境變數,例如 Postgres 的 PGDATA
。
最後,ENV
還可以用於設定常用的版本號,以便更容易維護版本更新,如下例所示
類似於在程式中使用常數變數,而不是硬編碼值,這種方法允許您更改單個 ENV
指令來自動更新容器中軟體的版本。
每個 ENV
行都會建立一個新的中間層,就像 RUN
指令一樣。這意味著即使您在以後的層中取消設定環境變數,它仍然會保留在此層中,並且可以轉儲其值。您可以透過建立如下所示的 Dockerfile,然後建置它來測試這一點。
為了防止這種情況,並真正取消設定環境變數,請使用帶有 shell 指令的 RUN
指令,在單層中設定、使用和取消設定變數。您可以使用 ;
或 &&
分隔您的指令。如果您使用第二種方法,並且其中一個指令失敗,則 docker build
也會失敗。這通常是一個好主意。將 \
用作 Linux Dockerfile 的行繼續字元可以提高可讀性。您也可以將所有指令放入 shell 腳本中,並讓 RUN
指令只運行該 shell 腳本。
有關 ENV
的更多資訊,請參閱ENV 指令的 Dockerfile 參考 。
ADD
和 COPY
在功能上相似。COPY
支援將檔案從建置上下文 或多階段建置 中的階段基本複製到容器中。ADD
支援從遠端 HTTPS 和 Git URL 擷取檔案的功能,並在從建置上下文新增檔案時自動解壓縮 tar 檔案。
在大多數情況下,您會想要使用 COPY
將檔案從一個階段複製到多階段建置中的另一個階段。如果您需要暫時將建置上下文中的檔案新增到容器中以執行 RUN
指令,您通常可以用繫結掛載代替 COPY
指令。例如,要暫時新增一個 requirements.txt
檔案以用於 RUN pip install
指令
與 COPY
相比,繫結掛載在將建置上下文中的檔案包含在容器中時效率更高。請注意,繫結掛載的檔案僅針對單個 RUN
指令暫時新增,並且不會保留在最終映像檔中。如果您需要在最終映像檔中包含建置上下文中的檔案,請使用 COPY
。
當您需要下載遠端構件作為建置的一部分時,ADD
指令最適合。ADD
比使用 wget
和 tar
之類的工具手動新增檔案更好,因為它確保了更精確的建置快取。ADD
還內建支援遠端資源的校驗和驗證,以及一種用於從Git URL 解析分支、標籤和子目錄的協定。
以下範例使用 ADD
指令下載 .NET 安裝程式。結合多階段建置,最終階段只會保留 .NET 執行環境,沒有中間檔案。
關於 ADD
或 COPY
的更多資訊,請參閱以下內容
ENTRYPOINT
的最佳用途是設定映像檔的主要指令,讓映像檔可以像執行該指令一樣運行,然後使用 CMD
作為預設旗標。
以下是命令列工具 s3cmd
的映像檔範例
您可以使用以下指令來執行映像檔並顯示指令的說明
或者,您可以使用正確的參數來執行指令,如下例所示
這很有用,因為映像檔名稱可以兼作二進制檔的參考,如上述指令所示。
ENTRYPOINT
指令也可以與輔助腳本結合使用,使其功能與上述指令類似,即使啟動工具可能需要多個步驟。
例如,Postgres 官方映像檔 使用以下腳本作為其 ENTRYPOINT
此腳本使用 exec
Bash 指令 ,讓最終運行的應用程式成為容器的 PID 1。這允許應用程式接收任何發送到容器的 Unix 訊號。如需更多資訊,請參閱 ENTRYPOINT
參考文件 。
在以下範例中,一個輔助腳本被複製到容器中,並在容器啟動時透過 ENTRYPOINT
運行
此腳本允許您以多種方式與 Postgres 互動。
它可以簡單地啟動 Postgres
或者,您可以使用它來運行 Postgres 並將參數傳遞給伺服器
最後,您可以使用它來啟動完全不同的工具,例如 Bash
關於 ENTRYPOINT
的更多資訊,請參閱 ENTRYPOINT 指令的 Dockerfile 參考文件 。
您應該使用 VOLUME
指令來公開任何資料庫儲存區域、配置儲存空間,或 Docker 容器建立的檔案和資料夾。強烈建議您對映像檔中任何可變或使用者可維護的部分使用 VOLUME
。
關於 VOLUME
的更多資訊,請參閱 VOLUME 指令的 Dockerfile 參考文件 。
如果服務可以在沒有權限的情況下運行,請使用 USER
切換到非 root 使用者。首先在 Dockerfile 中使用類似以下範例的方式建立使用者和群組
**注意**
考慮明確的 UID/GID。
映像檔中的使用者和群組會被分配一個非確定性的 UID/GID,也就是說,無論映像檔如何重建,都會分配「下一個」UID/GID。因此,如果這很重要,您應該分配一個明確的 UID/GID。
**注意**
由於 Go archive/tar 套件在處理稀疏檔案時存在一個 未解決的錯誤“gosu” 。
最後,為了減少層數和複雜性,避免頻繁地來回切換 USER
。
關於 USER
的更多資訊,請參閱 USER 指令的 Dockerfile 參考文件 。
為了清晰和可靠性,您應該始終為 WORKDIR
使用絕對路徑。此外,您應該使用 WORKDIR
,而不是使用像 RUN cd … && do-something
這樣難以閱讀、排除故障和維護的指令。
關於 WORKDIR
的更多資訊,請參閱 WORKDIR 指令的 Dockerfile 參考文件 。
ONBUILD
指令在當前 Dockerfile 建置完成後執行。ONBUILD
會在任何從當前映像檔衍生的子映像檔中執行。將 ONBUILD
指令視為父 Dockerfile 給子 Dockerfile 的指令。
Docker 建置會在子 Dockerfile 中的任何指令之前執行 ONBUILD
指令。
ONBUILD
對於將從指定映像檔建置的映像檔很有用。例如,您會將 ONBUILD
用於在 Dockerfile 中建置以該語言編寫的任意使用者軟體的語言堆疊映像檔,如您在 Ruby 的 ONBUILD
變體ONBUILD 指令的 Dockerfile 參考文件 。