將 Windows Dockerfiles 最佳化

有許多方式可將 Docker 建置程式與產生的 Docker 映射優化。 本文說明 Docker 建置程式的運作方式,以及如何以最佳方式建立 Windows 容器的映射。

Docker 組建中的映射層

您必須先知道 Docker 組建的運作方式,才能將 Docker 組建優化。 Docker 建置流程期間使用了 Dockerfile,且每個可採取動作的指令都依序在其自身的暫存容器中執行。 結果就為每個可採取動作的指令產生了新的映像層。

例如,下列範例 Dockerfile 會使用 mcr.microsoft.com/windows/servercore:ltsc2019 基底 OS 映射、安裝 IIS,然後建立簡單的網站。

# Sample Dockerfile

FROM mcr.microsoft.com/windows/servercore:ltsc2019
RUN dism /online /enable-feature /all /featurename:iis-webserver /NoRestart
RUN echo "Hello World - Dockerfile" > c:\inetpub\wwwroot\index.html
CMD [ "cmd" ]

您可能會預期此 Dockerfile 會產生具有兩層的映射、一個用於容器 OS 映射,另一個包含 IIS 和網站。 不過,實際映射有許多層,而且每個圖層都相依于其之前。

為了更清楚,讓我們對範例 Dockerfile 所建立的映射執行 docker history 命令。

docker history iis

IMAGE               CREATED              CREATED BY                                      SIZE                COMMENT
f4caf476e909        16 seconds ago       cmd /S /C REM (nop) CMD ["cmd"]                 41.84 kB
f0e017e5b088        21 seconds ago       cmd /S /C echo "Hello World - Dockerfile" > c   6.816 MB
88438e174b7c        About a minute ago   cmd /S /C dism /online /enable-feature /all /   162.7 MB
6801d964fda5        4 months ago                                                         0 B

輸出顯示此映射有四個層:基底層和三個額外的層,這些層會對應至 Dockerfile 中的每個指令。 底層 (此範例中為 6801d964fda5) 代表基本 OS 映像。 一層是 IIS 安裝。 再上一層則包含新的網站,以此類推。

Dockerfiles 可以寫入以將映射層最小化、優化建置效能,以及透過可讀性將協助工具優化。 最後,有許多方式可以完成相同的映像建置工作。 瞭解 Dockerfile 的格式如何影響建置時間,以及其建立的映射可改善自動化體驗。

優化影像大小

根據您的空間需求,在建置 Docker 容器映射時,映射大小可能是一個重要因素。 容器映像在登錄和主機之間移動、進行匯出和匯入,而且最終會耗用空間。 本節將告訴您如何在 Windows 容器的 Docker 建置程式期間將映射大小降到最低。

如需 Dockerfile 最佳做法的其他資訊,請參閱 在 Docker.com 上撰寫 Dockerfiles 的最佳做法

由於每個 RUN 指令都會在容器映射中建立新的層,因此將動作分組成一個 RUN 指令可以減少 Dockerfile 中的層數。 雖然將層最小化可能不會對映像大小造成太大的影響,但將相關聯的動作分組卻會有明顯影響,您會在後續的範例看到相關示範。

在本節中,我們將比較兩個執行相同動作的範例 Dockerfile。 不過,一個 Dockerfile 每個動作都有一個指令,而另一個則將其相關的動作分組在一起。

下列未分組的 Dockerfile 會下載適用于 Windows 的 Python、安裝它,並在安裝完成後移除下載的安裝程式檔案。 在此 Dockerfile 中,每個動作都會獲得自己的 RUN 指令。

FROM mcr.microsoft.com/windows/servercore:ltsc2019

RUN powershell.exe -Command Invoke-WebRequest "https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe" -OutFile c:\python-3.5.1.exe
RUN powershell.exe -Command Start-Process c:\python-3.5.1.exe -ArgumentList '/quiet InstallAllUsers=1 PrependPath=1' -Wait
RUN powershell.exe -Command Remove-Item c:\python-3.5.1.exe -Force

產生出的映像包含三個額外的層,每個 RUN 指令皆有一個層。

docker history doc-example-1

IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
a395ca26777f        15 seconds ago      cmd /S /C powershell.exe -Command Remove-Item   24.56 MB
6c137f466d28        28 seconds ago      cmd /S /C powershell.exe -Command Start-Proce   178.6 MB
957147160e8d        3 minutes ago       cmd /S /C powershell.exe -Command Invoke-WebR   125.7 MB

第二個範例是執行完全相同作業的 Dockerfile。 不過,所有相關動作都已分組在單 RUN 一指令之下。 指令中的每個 RUN 步驟都是在 Dockerfile 的新行上,而 '\' 字元則用來換行。

FROM mcr.microsoft.com/windows/servercore:ltsc2019

RUN powershell.exe -Command \
  $ErrorActionPreference = 'Stop'; \
  Invoke-WebRequest https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe -OutFile c:\python-3.5.1.exe ; \
  Start-Process c:\python-3.5.1.exe -ArgumentList '/quiet InstallAllUsers=1 PrependPath=1' -Wait ; \
  Remove-Item c:\python-3.5.1.exe -Force

產生的影像對於指令只有一個額外的圖層 RUN

docker history doc-example-2

IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
69e44f37c748        54 seconds ago      cmd /S /C powershell.exe -Command   $ErrorAct   216.3 MB

移除多餘的檔案

如果您的 Dockerfile 中有檔案,例如安裝程式,則不需要在使用它之後,您可以將其移除以減少映射大小。 這需與將檔案複製到映像層的步驟同時進行。 這樣做可防止檔案保存在較低層級的映射層中。

在下列範例 Dockerfile 中,會下載、執行、移除 Python 套件。 這全在一項 RUN 作業期間完成,並產生出單一映像層。

FROM mcr.microsoft.com/windows/servercore:ltsc2019

RUN powershell.exe -Command \
  $ErrorActionPreference = 'Stop'; \
  Invoke-WebRequest https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe -OutFile c:\python-3.5.1.exe ; \
  Start-Process c:\python-3.5.1.exe -ArgumentList '/quiet InstallAllUsers=1 PrependPath=1' -Wait ; \
  Remove-Item c:\python-3.5.1.exe -Force

優化建置速度

多行

您可以將作業分割成多個個別指示,以優化 Docker 建置速度。 多個 RUN 作業會提高快取效率,因為會為每個 RUN 指令建立個別層。 如果已在不同的 Docker 建置作業中執行相同的指令,則會重複使用此快取作業 (映射層) ,因而導致 Docker 組建執行時間降低。

在下列範例中,會下載、安裝 Apache 和 Visual Studio 轉散發套件,然後藉由移除不再需要的檔案來清除套件。 這全都是使用單 RUN 一指令來完成。 如果更新其中任何一個動作,所有動作都會重新執行。

FROM mcr.microsoft.com/windows/servercore:ltsc2019

RUN powershell -Command \

  # Download software ; \

  wget https://www.apachelounge.com/download/VC11/binaries/httpd-2.4.18-win32-VC11.zip -OutFile c:\apache.zip ; \
  wget "https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x86.exe" -OutFile c:\vcredist.exe ; \
  wget -Uri http://windows.php.net/downloads/releases/php-5.5.33-Win32-VC11-x86.zip -OutFile c:\php.zip ; \

  # Install Software ; \

  Expand-Archive -Path c:\php.zip -DestinationPath c:\php ; \
  Expand-Archive -Path c:\apache.zip -DestinationPath c:\ ; \
  Start-Process c:\vcredist.exe -ArgumentList '/quiet' -Wait ; \

  # Remove unneeded files ; \

  Remove-Item c:\apache.zip -Force; \
  Remove-Item c:\vcredist.exe -Force; \
  Remove-Item c:\php.zip

產生的映射有兩層,一個用於基底 OS 映射,另一個包含來自單 RUN 一指令的所有作業。

docker history doc-sample-1

IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
9bdf3a21fd41        8 minutes ago       cmd /S /C powershell -Command     Invoke-WebR   205.8 MB
6801d964fda5        5 months ago                                                        0 B

相較之下,以下是分成三 RUN 個指令的相同動作。 在此情況下,每個 RUN 指令都會快取在容器映射層中,而且只有已變更的指令必須在後續的 Dockerfile 組建上重新執行。

FROM mcr.microsoft.com/windows/servercore:ltsc2019

RUN powershell -Command \
    $ErrorActionPreference = 'Stop'; \
    wget https://www.apachelounge.com/download/VC11/binaries/httpd-2.4.18-win32-VC11.zip -OutFile c:\apache.zip ; \
    Expand-Archive -Path c:\apache.zip -DestinationPath c:\ ; \
    Remove-Item c:\apache.zip -Force

RUN powershell -Command \
    $ErrorActionPreference = 'Stop'; \
    wget "https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x86.exe" -OutFile c:\vcredist.exe ; \
    Start-Process c:\vcredist.exe -ArgumentList '/quiet' -Wait ; \
    Remove-Item c:\vcredist.exe -Force

RUN powershell -Command \
    $ErrorActionPreference = 'Stop'; \
    wget http://windows.php.net/downloads/releases/php-5.5.33-Win32-VC11-x86.zip -OutFile c:\php.zip ; \
    Expand-Archive -Path c:\php.zip -DestinationPath c:\php ; \
    Remove-Item c:\php.zip -Force

產生的影像是由四個圖層所組成;基底 OS 映射的一層,以及這三 RUN 個指示中的每一層。 因為每個 RUN 指令都是在自己的層中執行,所以此 Dockerfile 的任何後續執行或不同 Dockerfile 中的相同指令集都會使用快取的映射層,以減少建置時間。

docker history doc-sample-2

IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
ddf43b1f3751        6 days ago          cmd /S /C powershell -Command  Sleep 2 ;  Inv   127.2 MB
d43abb81204a        7 days ago          cmd /S /C powershell -Command  Sleep 2 ;  Inv   66.46 MB
7a21073861a1        7 days ago          cmd /S /C powershell -Command  Sleep 2 ;  Inv   115.8 MB
6801d964fda5        5 months ago

使用影像快取時,如何排序指示很重要,如下一節所示。

訂購指示

Dockerfile 是從上到下進行處理,每個指令會和快取層進行比較。 當指令沒有快取層時,此指令和所有後續的指令會在新容器映像層中進行處理。 有鑑於此,指令的放置順序非常重要。 在 Dockerfile 上層放置會保持固定的指令。 在 Dockerfile 下層放置可能會有所改變的指令。 這樣做可以降低取消現有快取的可能性。

下列範例示範 Dockerfile 指令順序如何影響快取效率。 這個簡單的範例 Dockerfile 有四個編號的資料夾。

FROM mcr.microsoft.com/windows/servercore:ltsc2019

RUN mkdir test-1
RUN mkdir test-2
RUN mkdir test-3
RUN mkdir test-4

產生的映射有五層,一個用於基底 OS 映射和每個 RUN 指示。

docker history doc-sample-1

IMAGE               CREATED              CREATED BY               SIZE                COMMENT
afba1a3def0a        38 seconds ago       cmd /S /C mkdir test-4   42.46 MB
86f1fe772d5c        49 seconds ago       cmd /S /C mkdir test-3   42.35 MB
68fda53ce682        About a minute ago   cmd /S /C mkdir test-2   6.745 MB
5e5aa8ba1bc2        About a minute ago   cmd /S /C mkdir test-1   7.12 MB
6801d964fda5        5 months ago                                  0 B

這個下一個 Dockerfile 現在已稍微修改,第三 RUN 個指令已變更為新的檔案。 當 Docker 建置針對此 Dockerfile 執行時,前三項指令 (和上個範例中的指令完全相同) 會使用快取映像層。 不過,因為不會快取變更的指令,所以會針對已變更 RUN 的指令和所有後續指示建立新的圖層。

FROM mcr.microsoft.com/windows/servercore:ltsc2019

RUN mkdir test-1
RUN mkdir test-2
RUN mkdir test-5
RUN mkdir test-4

當您比較新影像的影像識別碼與本節第一個範例中的影像識別碼時,您會發現前三層從下到上都是共用的,但第四層和第五層是唯一的。

docker history doc-sample-2

IMAGE               CREATED             CREATED BY               SIZE                COMMENT
c92cc95632fb        28 seconds ago      cmd /S /C mkdir test-4   5.644 MB
2f05e6f5c523        37 seconds ago      cmd /S /C mkdir test-5   5.01 MB
68fda53ce682        3 minutes ago       cmd /S /C mkdir test-2   6.745 MB
5e5aa8ba1bc2        4 minutes ago       cmd /S /C mkdir test-1   7.12 MB
6801d964fda5        5 months ago                                 0 B

外觀優化

指令案例

Dockerfile 指令不區分大小寫,但慣例是使用大寫。 這可藉由區分指令呼叫和指令作業來改善可讀性。 下列兩個範例會比較未資本化和大寫的 Dockerfile。

以下是未使用Capitalized Dockerfile:

# Sample Dockerfile

from mcr.microsoft.com/windows/servercore:ltsc2019
run dism /online /enable-feature /all /featurename:iis-webserver /NoRestart
run echo "Hello World - Dockerfile" > c:\inetpub\wwwroot\index.html
cmd [ "cmd" ]

以下是使用大寫的相同 Dockerfile:

# Sample Dockerfile

FROM mcr.microsoft.com/windows/servercore:ltsc2019
RUN dism /online /enable-feature /all /featurename:iis-webserver /NoRestart
RUN echo "Hello World - Dockerfile" > c:\inetpub\wwwroot\index.html
CMD [ "cmd" ]

換行

長和複雜的作業可以透過反斜線 \ 字元分隔成多行。 下列 Dockerfile 會安裝 Visual Studio 可轉散發套件、移除安裝程式檔案,然後建立設定檔。 這三項作業都是在單行中指定。

FROM mcr.microsoft.com/windows/servercore:ltsc2019

RUN powershell -Command c:\vcredist_x86.exe /quiet ; Remove-Item c:\vcredist_x86.exe -Force ; New-Item c:\config.ini

命令可以使用反斜線來細分,以便從一個 RUN 指令的每個作業都在其自己的行上指定。

FROM mcr.microsoft.com/windows/servercore:ltsc2019

RUN powershell -Command \
    $ErrorActionPreference = 'Stop'; \
    Start-Process c:\vcredist_x86.exe -ArgumentList '/quiet' -Wait ; \
    Remove-Item c:\vcredist_x86.exe -Force ; \
    New-Item c:\config.ini

進一步閱讀和參考

Windows 上的 Dockerfile

Docker.com 上寫入 Dockerfiles 的最佳做法