Ottimizzare i Dockerfile di Windows

Esistono molti modi per ottimizzare sia il processo di compilazione Docker che le immagini Docker risultanti. Questo articolo illustra come funziona il processo di compilazione Docker e come creare immagini in modo ottimale per i contenitori Di Windows.

Livelli di immagine nella compilazione Docker

Prima di poter ottimizzare la compilazione Docker, è necessario conoscere il funzionamento della compilazione Docker. Durante il processo di compilazione Docker viene usato un Dockerfile e le istruzioni eseguibili vengono effettivamente eseguite, una alla volta, nel relativo contenitore temporaneo. Il risultato è un nuovo livello di immagine per ogni istruzione eseguibile.

Ad esempio, il Dockerfile di esempio seguente usa l'immagine del mcr.microsoft.com/windows/servercore:ltsc2019 sistema operativo di base, installa IIS e quindi crea un sito Web semplice.

# 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" ]

Si potrebbe prevedere che questo Dockerfile produrrà un'immagine con due livelli, uno per l'immagine del sistema operativo contenitore e un secondo che include IIS e il sito Web. Tuttavia, l'immagine effettiva ha molti livelli e ogni livello dipende da quello precedente.

Per renderlo più chiaro, eseguire il docker history comando sull'immagine creata dal Dockerfile di esempio.

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

L'output mostra che questa immagine ha quattro livelli: il livello di base e tre livelli aggiuntivi mappati a ogni istruzione nel Dockerfile. Il livello inferiore (6801d964fda5 in questo esempio) rappresenta l'immagine del sistema operativo di base. Un livello su è l'installazione di IIS. Il livello ancora successivo include il nuovo sito Web e così via.

I Dockerfile possono essere scritti per ridurre al minimo i livelli di immagine, ottimizzare le prestazioni di compilazione e ottimizzare l'accessibilità tramite leggibilità. In definitiva esistono molti modi per completare l'attività di compilazione della stessa immagine. Comprendere in che modo il formato del Dockerfile influisce sul tempo di compilazione e l'immagine creata migliora l'esperienza di automazione.

Ottimizzare le dimensioni dell'immagine

A seconda dei requisiti di spazio, le dimensioni dell'immagine possono essere un fattore importante durante la compilazione di immagini contenitore Docker. Le immagini contenitore vengono spostate tra registri e host, esportate e importate, perciò consumano spazio. Questa sezione descrive come ridurre al minimo le dimensioni dell'immagine durante il processo di compilazione Docker per i contenitori Windows.

Per altre informazioni sulle procedure consigliate per Dockerfile, vedere Procedure consigliate per la scrittura di Dockerfile in Docker.com.

Poiché ogni RUN istruzione crea un nuovo livello nell'immagine contenitore, il raggruppamento delle azioni in un'unica RUN istruzione può ridurre il numero di livelli in un Dockerfile. Mentre la riduzione dei livelli al minimo potrebbe non influire molto sulla dimensione dell'immagine, ciò che potrebbe influire è invece il raggruppamento delle azioni correlate, trattato negli esempi successivi.

In questa sezione verranno confrontati due Dockerfile di esempio che eseguono le stesse operazioni. Tuttavia, un Dockerfile ha un'istruzione per azione, mentre l'altra ha raggruppato le azioni correlate.

L'esempio dockerfile di esempio non raggruppato seguente scarica Python per Windows, lo installa e rimuove il file di installazione scaricato al termine dell'installazione. In questo Dockerfile a ogni azione viene assegnata una propria RUN istruzione.

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

L'immagine risultante è costituita da tre livelli aggiuntivi, uno per ogni istruzione 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

Il secondo esempio è un Dockerfile che esegue esattamente la stessa operazione. Tuttavia, tutte le azioni correlate sono state raggruppate in una singola RUN istruzione. Ogni passaggio dell'istruzione RUN si trova in una nuova riga del Dockerfile, mentre il carattere '\' viene usato per eseguire il wrapping della riga.

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

L'immagine risultante ha un solo livello aggiuntivo per l'istruzione 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

Rimuovere i file in eccesso

Se è presente un file nel Dockerfile, ad esempio un programma di installazione, che non è necessario dopo che è stato usato, è possibile rimuoverlo per ridurre le dimensioni dell'immagine. È necessario farlo nello stesso passaggio in cui il file viene copiato nel livello di immagine. In questo modo, il file non viene mantenuto in un livello di immagine di livello inferiore.

Nell'esempio seguente Dockerfile il pacchetto Python viene scaricato, eseguito e quindi rimosso. Tutto questo viene completato in una sola operazione RUN e genera un singolo livello di immagine.

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

Ottimizzare la velocità di compilazione

Più righe

È possibile suddividere le operazioni in più istruzioni singole per ottimizzare la velocità di compilazione di Docker. Più RUN operazioni aumentano l'efficacia della memorizzazione nella cache perché vengono creati singoli livelli per ogni RUN istruzione. Se un'istruzione identica è già stata eseguita in un'operazione di compilazione Docker diversa, questa operazione memorizzata nella cache (livello immagine) viene riutilizzata, con conseguente riduzione del runtime di compilazione Docker.

Nell'esempio seguente i pacchetti Apache e Visual Studio Redistribute vengono scaricati, installati e quindi puliti rimuovendo i file che non sono più necessari. Questa operazione viene eseguita con un'unica RUN istruzione. Se una di queste azioni viene aggiornata, tutte le azioni verranno eseguite di nuovo.

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

L'immagine risultante ha due livelli, uno per l'immagine del sistema operativo di base e uno che contiene tutte le operazioni della singola RUN istruzione.

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

Di seguito sono riportate le stesse azioni suddivise in tre RUN istruzioni. In questo caso, ogni RUN istruzione viene memorizzata nella cache in un livello immagine contenitore e solo quelle modificate devono essere rieseguite nelle compilazioni dockerfile successive.

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

L'immagine risultante è costituita da quattro livelli; un livello per l'immagine del sistema operativo di base e ognuna delle tre RUN istruzioni. Poiché ogni RUN istruzione è stata eseguita nel proprio livello, tutte le esecuzioni successive di questo Dockerfile o un set identico di istruzioni in un Dockerfile diverso useranno i livelli immagine memorizzati nella cache, riducendo il tempo di compilazione.

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

L'ordine delle istruzioni è importante quando si utilizzano le cache delle immagini, come si vedrà nella sezione successiva.

Istruzioni per l'ordinamento

Un Dockerfile viene elaborato dall'alto verso il basso e ogni istruzione viene confrontata rispetto ai livelli memorizzati nella cache. Quando viene rilevata un'istruzione senza un livello memorizzato nella cache, l'istruzione e tutte le istruzioni successive vengono elaborate in nuovi livelli dell'immagine contenitore. Per questo motivo è importante l'ordine in cui vengono posizionate le istruzioni. Inserire le istruzioni che rimangono costanti nella parte superiore del Dockerfile. Inserire le istruzioni che possono cambiare nella parte inferiore del Dockerfile. In questo modo si riduce la probabilità di un mancato riconoscimento di copie effettivamente esistenti nella cache.

Gli esempi seguenti illustrano come l'ordinamento delle istruzioni Dockerfile può influire sull'efficacia della memorizzazione nella cache. In questo semplice esempio Dockerfile sono presenti quattro cartelle numerate.

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

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

L'immagine risultante ha cinque livelli, uno per l'immagine del sistema operativo di base e ognuna delle RUN istruzioni.

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

Questo dockerfile successivo è stato leggermente modificato, con la terza RUN istruzione modificata in un nuovo file. Quando la compilazione Docker viene eseguita su questo Dockerfile, le prime tre istruzioni, che sono identiche a quelle nell'ultimo esempio, usano i livelli di immagine memorizzati nella cache. Tuttavia, poiché l'istruzione modificata RUN non viene memorizzata nella cache, viene creato un nuovo livello per l'istruzione modificata e per tutte le istruzioni successive.

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

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

Quando si confrontano gli ID immagine della nuova immagine con quello nel primo esempio di questa sezione, si noterà che i primi tre livelli dal basso verso l'alto sono condivisi, ma il quarto e il quinto sono univoci.

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

Ottimizzazione cosmetica

Caso di istruzione

Le istruzioni di Dockerfile non fanno distinzione tra maiuscole e minuscole, ma la convenzione prevede l'uso di maiuscole e minuscole. Ciò migliora la leggibilità differenziando la chiamata all'istruzione e l'operazione di istruzione. I due esempi seguenti confrontano un Dockerfile senza maiuscole e senza maiuscole.

Di seguito è riportato un Dockerfile non capitalizzato:

# 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" ]

Di seguito è riportato lo stesso Dockerfile usando il maiuscolo:

# 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" ]

Ritorno a capo linea

Le operazioni lunghe e complesse possono essere separate su più righe in base al carattere barra rovesciata \ . Il Dockerfile seguente installa il pacchetto ridistribuibile di Visual Studio, rimuove i file del programma di installazione e poi crea un file di configurazione. Queste tre operazioni sono tutte specificate su una sola riga.

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

Il comando può essere suddiviso con barre rovesciate in modo che ogni operazione di un'istruzione RUN venga specificata sulla propria riga.

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

Altre letture e riferimenti

Dockerfile in Windows

Procedure consigliate per la scrittura di Dockerfile in Docker.com