Windows Dockerfile を最適化する

Docker のビルド プロセスと結果の Docker イメージの両方を最適化する方法は多数あります。 この記事では、Docker のビルド プロセスがどのように機能するかと、Windows コンテナーのイメージを最適に作成する方法について説明します。

Docker ビルド内のイメージ レイヤー

Docker ビルドを最適化するには、Docker のビルドがどのように機能するかを理解する必要があります。 Docker ビルド プロセスでは、Dockerfile が使用されて、アクション可能な各命令が 1 つずつ専用の一時的なコンテナーで実行されます。 その結果、アクション可能な各命令に対して新しいイメージ レイヤーが作成されます。

たとえば、次のサンプル Dockerfile では、mcr.microsoft.com/windows/servercore:ltsc2019 ベース OS イメージを使用して、IIS をインストールし、簡単な Web サイトを作成しています。

# 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 では、2 つのレイヤーを持つイメージが生成されると想定されています。1 つはコンテナー OS イメージ用、2 つ目は IIS と Web サイトを含むものです。 ただし、実際のイメージには多くのレイヤーがあって、各レイヤーはその前のレイヤーに依存しています。

これを明確にするために、サンプルの 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

出力には、このイメージには 4 つのレイヤーがあることが示されています。基本レイヤーと、Dockerfile 内の各命令にマップされている 3 つの追加レイヤーです。 最下位のレイヤー (この例では 6801d964fda5) は、ベース OS イメージを表します。 1 つ上のレイヤーは、IIS のインストールです。 次のレイヤーには、新しい Web サイトなどが含まれます。

Dockerfiles は、イメージ レイヤーの最小化、ビルドのパフォーマンスの最適化、読みやすさを通したアクセシビリティの最適化を目的として記述できます。 最終的に、同じイメージ ビルド タスクを実行するにはさまざまな方法があります。 Dockerfile の形式がビルド時間と作成されるイメージにどのように影響するかを理解すれば、オートメーションのエクスペリエンスを向上させることになります。

イメージ サイズを最適化する

対象分野の要件によっては、Docker コンテナー イメージを作成するときにイメージ サイズが重要な要素になることがあります。 コンテナー イメージは、レジストリとホストの間を移動され、エクスポートおよびインポートされて、最終的にはスペースを消費します。 このセクションでは、Windows コンテナーの Docker ビルド プロセス時にイメージ サイズを最小限に抑える方法について説明します。

Dockerfile のベスト プラクティスについては、Docker.com の、Dockerfile を作成するときのベスト プラクティスを参照してください。

それぞれの RUN 命令によってコンテナー イメージ内に 1 つの新しいレイヤーが作成されるため、複数のアクションを 1 つの RUN 命令にグループ化すると、Dockerfile のレイヤーの数を減らすことができます。 レイヤーの数を最小限に抑えてもイメージのサイズに大きな影響はありませんが、後の例で示すように、関連するアクションをグループ化するとイメージのサイズに大きな影響があります。

このセクションでは、同じことを実行する Dockerfile の 2 つの例を比較します。 ただし、1 つの Dockerfile にはアクションごとに 1 つの命令があるのに対して、別の 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 命令ごとに 1 つずつ、3 つの追加レイヤーで構成されます。

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

2 番目の例は、まったく同じ操作を実行する 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 命令に対する 1 つの追加レイヤーだけです。

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 パッケージがダウンロードされて実行された後、削除されます。 このすべてを 1 つの RUN 操作で行うので、作成されるイメージ レイヤーは 1 つです。

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 の両方の再配布パッケージをダウンロードし、インストールした後、不要になったファイルの削除によってクリーンアップを行っています。 これは、1 つの 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

結果のイメージには 2 つのレイヤーがあります。1 つはベース OS イメージ用で、もう 1 つのレイヤーには 1 つの 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

比較して、こちらでは同じ操作が 3 つの 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

結果のイメージは、4 つのレイヤーで構成されます。ベース OS イメージ用のレイヤーが 1 つと、3 つの RUN 命令それぞれに 1 つです。 各 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 には、番号が付いたフォルダーが 4 つあります。

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

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

結果のイメージには 5 つのレイヤーがあります。ベース OS イメージ用に 1 つ、RUN 命令ごとに 1 つずつです。

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 は少し変更されて、3 つ目の RUN 命令が新しいファイルに変更されました。 この Dockerfile に対して Docker ビルドを実行すると、前の例と同じ最初の 3 つの命令は、キャッシュされたイメージ レイヤーを使用します。 ただし、変更された RUN 命令はキャッシュされていないので、変更された命令と、後続のすべての命令に対して新しいレイヤーが作成されます。

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

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

新しいイメージのイメージ ID を、このセクションの最初の例に含まれるものと比較すると、下から上に最初の 3 つのレイヤーは共有されていますが、4 つ目と 5 つ目は一意になっていることがわかります。

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 の命令の大文字と小文字は区別されませんが、規則では大文字を使うようになっています。 これにより、命令の呼び出しと命令の操作が区別されて、読みやすさが向上しています。 以下の 2 つの例では、大文字で記述していない Dockerfile と大文字で記述してある Dockerfile を比較しています。

次に示すのは、大文字で記述していない 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 の再頒布可能パッケージをインストールし、インストーラー ファイルを削除して、構成ファイルを作成します。 これら 3 つの操作すべてが 1 行で指定されています。

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

1 つの 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

Dockerfile 作成のベスト プラクティス (Docker.com)