Оптимизация файлов Dockerfile в Windows

Процесс сборки Docker и получаемые образы Docker можно оптимизировать несколькими способами. В этой статье объясняется, как происходит процесс сборки Docker и как оптимально создавать образы для контейнеров Windows.

Слои образов в сборке Docker

Чтобы оптимизировать сборку Docker, необходимо знать, как она выполняется. В процессе сборки Docker используется файл Dockerfile, а также поочередно выполняются все активные инструкции, каждая в своем собственном временном контейнере. В результате для каждой активной инструкции создается новый слой образа.

Например, в следующем примере Dockerfile использует базовый образ ОС mcr.microsoft.com/windows/servercore:ltsc2019, устанавливает службы 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 создаст образ с двумя слоями: один для образа ОС контейнера, а второй — для служб IIS и веб-сайта. Однако фактический образ состоит из нескольких слоев, и каждый из них зависит от предыдущего.

Чтобы лучше это понять, давайте применим команду docker history к образу, созданному с помощью нашего примера файла Dockerfile.

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) представляет базовый образ ОС. Одним слоем выше находится установка служб IIS. Следующий слой включает новый веб-сайт и т. д.

Файлы Dockerfile можно составлять таким образом, чтобы свести к минимуму число слоев в образе, оптимизировать производительность сборки, а также повысить доступность за счет удобочитаемости. По сути, существует множество способов выполнить одну и ту же задачу сборки образа. Понимание того, как формат файла Dockerfile влияет на время сборки и создаваемый образ, расширяет возможности автоматизации.

Оптимизация размера образа

В зависимости от требований к пространству при создании образов контейнеров Docker важную роль может играть размер образа. Образы контейнеров перемещаются между реестрами и узлом, экспортируются и импортируются и в конечном счете занимают определенное место. В этом разделе вы узнаете, как свести к минимуму размер образа в процессе сборки Docker для контейнеров Windows.

Дополнительные сведения о рекомендациях для файлов Dockerfile см. в советах по составлению файлов Dockerfile на сайте Docker.com.

Поскольку каждая инструкция RUN создает новый слой в образе контейнера, группирование действий в одной инструкции RUN позволяет сократить число слоев в файле Dockerfile. Хотя минимизация числа слоев может не влиять на размер образа, группирование связанных действий влияет на него, что видно в приведенных ниже примерах.

В этом разделе мы будем сравнивать два примера файлов Dockerfile, которые выполняют одни и те же действия. Однако в одном файле Dockerfile используется одна инструкция для каждого действия, а в другом — связанные действия сгруппированы вместе.

В следующем примере несгруппированный файл Dockerfile скачивает Python для Windows, устанавливает его и удаляет скачанный файл установки после завершения установки. В этом файле 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

Итоговый образ имеет два слоя — один для базового образа ОС, а второй для всех операций из одной инструкции 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

Полученный образ имеет четыре слоя — один для базового образа ОС и по одному для каждой из трех инструкций 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

Полученный образ имеет пять слоев — один для базового образа ОС и по одному для каждой инструкции 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 с инструкциями, написанными в верхнем и нижнем регистре.

Ниже приведен файл 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

Дополнительные материалы и справочники

Dockerfile в Windows

Рекомендации по составлению файлов Dockerfile на сайте Docker.com