L'utilizzo di multi-stage Docker per la creazione di immagini Windows

Ciao a tutti! Mi chiamo Andrey e lavoro come ingegnere DevOps presso Exness nel team di sviluppo. La mia principale attività riguarda la costruzione, il deployment e la manutenzione di applicazioni in Docker sotto il sistema operativo Linux (di seguito - OS). Recentemente ho avuto l'incarico di svolgere le stesse attività, ma con Windows Server come sistema operativo target del progetto e un insieme di progetti in C++. È stata la mia prima esperienza concreta con i contenitori Docker sotto Windows e, in generale, con applicazioni in C++. Grazie a questo, ho acquisito un'esperienza interessante e scoperto alcune specificità della containerizzazione delle applicazioni in Windows.

L'utilizzo di multi-stage Docker per la creazione di immagini Windows

In questo articolo voglio raccontare quali difficoltà ho incontrato e come sono riuscito a risolverle. Spero che possa essere utile per affrontare le vostre attuali e future sfide. Buona lettura!

Perché i contenitori?

L'azienda dispone di un'infrastruttura esistente per l'orchestrazione dei container basata su Hashicorp Nomad e componenti correlati — Consul e Vault. Perché la containerizzazione delle applicazioni è stata scelta come metodo unificato per la consegna di soluzioni complete. Poiché l'infrastruttura del progetto include host Docker con versioni di Windows Server Core 1803 e 1809, è necessario costruire separatamente versioni delle immagini Docker per 1803 e 1809. Nella versione 1803, è importante ricordare che il numero di revisione dell'host Docker deve corrispondere al numero di revisione dell'immagine Docker di base e all'host in cui il container di quest'immagine sarà eseguito. La versione 1809 non presenta questo inconveniente. Maggiori dettagli possono essere letti qui.

Perché multi-stage?

Gli ingegneri delle squadre di sviluppo hanno accesso limitato o assente agli host di build, non possono gestire rapidamente il set di componenti per la creazione dell'app su questi host, ad esempio, installare un set di strumenti aggiuntivo o un workload per Visual Studio. Pertanto, abbiamo deciso di installare tutti i componenti necessari per la creazione dell'app in un'immagine Docker per il build. Se necessario, è possibile modificare rapidamente solo il dockerfile e avviare il pipeline di creazione di quell'immagine.

Dalla teoria alla pratica

In un'ideale multi-stage Docker build, la preparazione dell'ambiente per compilare l'app avviene nello stesso script del dockerfile dove si crea l'app stessa. Tuttavia, nel nostro caso è stato aggiunto un passaggio intermedio, ovvero la fase di pre-creazione dell'immagine Docker con tutto il necessario per la compilazione dell'app. Questo è stato fatto per sfruttare la funzionalità di cache di Docker, riducendo il tempo di installazione di tutte le dipendenze.

Esaminiamo i punti principali dello script del dockerfile per la creazione di questa immagine.

Per creare immagini di diverse versioni di OS in dockerfile, è possibile definire un argomento attraverso il quale si passa il numero di versione durante la costruzione, che sarà anche il tag dell'immagine base.

L'elenco completo dei tag delle immagini Microsoft Windows Server può essere trovato qui.

ARG WINDOWS_OS_VERSION=1809
FROM mcr.microsoft.com/windows/servercore:$WINDOWS_OS_VERSION

Per impostazione predefinita, i comandi nell'istruzione RUN all'interno del dockerfile su Windows vengono eseguiti nella console cmd.exe. Per facilitare la scrittura di script e ampliare le funzionalità dei comandi utilizzati, sovrascriveremo la console di esecuzione dei comandi con Powershell attraverso l'istruzione SHELL.

SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop';"]

Il passo successivo è installare il gestore pacchetti chocolatey e i pacchetti necessari:

COPY chocolatey.pkg.config .
RUN Set-ExecutionPolicy Bypass -Scope Process -Force ;
    [System.Net.ServicePointManager]::SecurityProtocol = 
    [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 ;
    $env:chocolateyUseWindowsCompression = 'true' ;
    iex ((New-Object System.Net.WebClient).DownloadString( 
      'https://chocolatey.org/install.ps1')) ;
    choco install chocolatey.pkg.config -y --ignore-detected-reboot ;
    if ( @(0, 1605, 1614, 1641, 3010) -contains $LASTEXITCODE ) { 
      refreshenv; } else { exit $LASTEXITCODE; } ;
    Remove-Item 'chocolatey.pkg.config'

Per installare pacchetti utilizzando chocolatey, è possibile passarli in elenco o installarli uno alla volta nel caso sia necessario fornire parametri unici per ciascun pacchetto. Nella nostra situazione abbiamo utilizzato un file manifesto in formato XML, che contiene l'elenco dei pacchetti necessari e i loro parametri. Il suo contenuto è il seguente:

Successivamente, installiamo l'ambiente di build dell'applicazione, ovvero MS Build Tools 2019 — una versione leggera di Visual Studio 2019, che contiene il set minimo di componenti necessari per compilarne il codice.
Per lavorare correttamente con il nostro progetto C++, ci serviranno componenti aggiuntivi, ovvero:

  • Strumenti Workload C++
  • Toolset v141
  • Windows 10 SDK (10.0.17134.0)

È possibile installare un set di strumenti avanzato in modo automatico tramite un file di configurazione in formato JSON. Il contenuto del file di configurazione:

Un elenco completo dei componenti disponibili può essere trovato sul sito della documentazione Microsoft Visual Studio.

{
  "version": "1.0",
  "components": [
    "Microsoft.Component.MSBuild",
    "Microsoft.VisualStudio.Workload.VCTools;includeRecommended",
    "Microsoft.VisualStudio.Component.VC.v141.x86.x64",
    "Microsoft.VisualStudio.Component.Windows10SDK.17134"
  ]
}

Nel dockerfile viene eseguito uno script di installazione e, per comodità, viene aggiunto il percorso agli eseguibili degli strumenti di build alla variabile d'ambiente. PATHÈ inoltre consigliabile rimuovere file e directory non necessari per ridurre le dimensioni dell'immagine.

COPY buildtools.config.json .
RUN Invoke-WebRequest 'https://aka.ms/vs/16/release/vs_BuildTools.exe' 
      -OutFile '.vs_buildtools.exe' -UseBasicParsing ;
    Start-Process -FilePath '.vs_buildtools.exe' -Wait -ArgumentList 
      '--quiet --norestart --nocache --config C:buildtools.config.json' ;
    Remove-Item '.vs_buildtools.exe' ;
    Remove-Item '.buildtools.config.json' ;
    Remove-Item -Force -Recurse 
      'C:Program Files (x86)Microsoft Visual StudioInstaller' ;
    $env:PATH = 'C:Program Files (x86)Microsoft Visual Studio2019BuildToolsMSBuildCurrentBin;' + $env:PATH; 
    [Environment]::SetEnvironmentVariable('PATH', $env:PATH, 
      [EnvironmentVariableTarget]::Machine)

A questo punto, la nostra immagine per la compilazione dell'applicazione C++ è pronta e si può procedere direttamente alla creazione della build multi-stage dell'applicazione.

Multi-stage in azione

Utilizzeremo l'immagine creata con tutti gli strumenti a bordo come immagine di build. Come nel precedente script dockerfile, aggiungeremo la possibilità di specificare dinamicamente il numero della versione/tag dell'immagine per facilitare il riutilizzo del codice. È importante aggiungere un'etichetta come builder all'immagine di build nelle istruzioni DA.

ARG WINDOWS_OS_VERSION=1809
FROM buildtools:$WINDOWS_OS_VERSION come builder

È giunto il momento di costruire l'applicazione. Qui tutto è abbastanza semplice: copiare il codice sorgente e tutto ciò che lo riguarda, e avviare il processo di compilazione.

COPY myapp .
RUN nuget restore myapp.sln ;
    msbuild myapp.sln /t:myapp /p:Configuration=Release

L'ultima fase della creazione dell'immagine finale prevede l'indicazione dell'immagine di base dell'applicazione, dove saranno collocati tutti gli artefatti di compilazione e i file di configurazione. Per copiare i file compilati dall'immagine intermedia di build, è necessario specificare il parametro --from=builder nelle istruzioni COPY.

FROM mcr.microsoft.com/windows/servercore:$WINDOWS_OS_VERSION

COPY --from=builder C:/x64/Release/myapp/ ./
COPY ./configs ./

Ora rimane solo aggiungere le dipendenze necessarie per il funzionamento della nostra applicazione e specificare il comando di avvio tramite le istruzioni ENTRYPOINT o CMD.

Conclusione

In questo articolo ho spiegato come creare un ambiente completo per la compilazione di applicazioni C++ all'interno di un container su Windows e come utilizzare le funzionalità delle build multi-stage di Docker per creare immagini complete della nostra applicazione.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster