Utilizzo della finestra mobile multistadio per creare immagini Windows

Ciao a tutti! Mi chiamo Andrey e lavoro come ingegnere DevOps presso Exness nel team di sviluppo. La mia attività principale è legata alla creazione, distribuzione e supporto di applicazioni in docker sotto il sistema operativo Linux (di seguito denominato sistema operativo). Non molto tempo fa ho avuto un compito con le stesse attività, ma il sistema operativo di destinazione del progetto era Windows Server e una serie di progetti C++. Per me, questa è stata la prima interazione ravvicinata con i contenitori docker nel sistema operativo Windows e, in generale, con le applicazioni C++. Grazie a questo, ho avuto un'esperienza interessante e ho imparato alcune complessità della containerizzazione delle applicazioni in Windows.

Utilizzo della finestra mobile multistadio per creare immagini Windows

In questo articolo voglio raccontarti quali difficoltà ho dovuto affrontare e come sono riuscita a risolverle. Spero che questo sia utile per le tue sfide attuali e future. Buona lettura!

Perché i contenitori?

L'azienda dispone di un'infrastruttura esistente per l'orchestratore di container Hashicorp Nomad e i relativi componenti: Consul e Vault. Pertanto, la containerizzazione delle applicazioni è stata scelta come metodo unificato per fornire una soluzione completa. Poiché l'infrastruttura del progetto contiene host docker con versioni del sistema operativo Windows Server Core 1803 e 1809, è necessario creare versioni separate delle immagini docker per 1803 e 1809. Nella versione 1803, è importante ricordare che il numero di revisione dell'host docker di build deve corrispondere al numero di revisione dell'immagine docker di base e all'host in cui verrà avviato il contenitore di questa immagine. La versione 1809 non presenta questo inconveniente. Puoi leggere di più qui.

Perché multistadio?

Gli ingegneri del team di sviluppo non hanno accesso o hanno un accesso molto limitato alla compilazione degli host; non esiste alcun modo per gestire rapidamente il set di componenti per la creazione di un'applicazione su questi host, ad esempio installando un set di strumenti o un carico di lavoro aggiuntivo per Visual Studio. Pertanto, abbiamo deciso di installare tutti i componenti necessari per creare l'applicazione nell'immagine Docker build. Se necessario, puoi modificare rapidamente solo il dockerfile e avviare la pipeline per creare questa immagine.

Dalla teoria all'azione

In una creazione ideale di un'immagine Docker in più fasi, l'ambiente per la creazione dell'applicazione viene preparato nello stesso script Dockerfile con cui viene creata l'applicazione stessa. Ma nel nostro caso è stato aggiunto un collegamento intermedio, ovvero la fase di creazione preliminare di un'immagine docker con tutto il necessario per costruire l'applicazione. Ciò è stato fatto perché volevo utilizzare la funzionalità docker cache per ridurre il tempo di installazione di tutte le dipendenze.

Diamo un'occhiata ai punti principali dello script dockerfile per creare questa immagine.

Per creare immagini di diverse versioni del sistema operativo, è possibile definire un argomento nel dockerfile attraverso il quale viene passato il numero di versione durante la compilazione ed è anche il tag dell'immagine di base.

È possibile trovare un elenco completo dei tag immagine di Microsoft Windows Server qui.

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

Per impostazione predefinita i comandi nelle istruzioni RUN all'interno del dockerfile sul sistema operativo Windows vengono eseguiti nella console cmd.exe. Per comodità di scrivere script ed espandere le funzionalità dei comandi utilizzati, ridefiniremo la console di esecuzione dei comandi in Powershell attraverso l'istruzione SHELL.

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

Il passo successivo è installare il gestore di 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 i pacchetti utilizzando Chocolatey, puoi semplicemente passarli come elenco o installarli uno alla volta se devi passare parametri univoci per ciascun pacchetto. Nella nostra situazione, abbiamo utilizzato un file manifest in formato XML, che contiene un elenco dei pacchetti richiesti e dei relativi parametri. Il suo contenuto è simile al seguente:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="python" version="3.8.2"/>
  <package id="nuget.commandline" version="5.5.1"/>
  <package id="git" version="2.26.2"/>
</packages>

Successivamente, installiamo l'ambiente di compilazione dell'applicazione, ovvero MS Build Tools 2019: questa è una versione leggera di Visual Studio 2019, che contiene il set minimo richiesto di componenti per la compilazione del codice.
Per funzionare completamente con il nostro progetto C++, avremo bisogno di componenti aggiuntivi, vale a dire:

  • Strumenti C++ del carico di lavoro
  • Set di strumenti v141
  • SDK di Windows 10 (10.0.17134.0)

È possibile installare automaticamente un set esteso di strumenti utilizzando un file di configurazione in formato JSON. Contenuto del file di configurazione:

Un elenco completo dei componenti disponibili è disponibile 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"
  ]
}

Il dockerfile esegue lo script di installazione e, per comodità, aggiunge il percorso dei file eseguibili degli strumenti di compilazione alla variabile di ambiente PATH. Si consiglia inoltre di 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 compilare l'applicazione C++ è pronta e possiamo procedere direttamente alla creazione di una build docker in più fasi dell'applicazione.

Multistadio in azione

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

ARG WINDOWS_OS_VERSION=1809
FROM buildtools:$WINDOWS_OS_VERSION as builder

Ora è il momento di creare l'applicazione. Qui tutto è abbastanza semplice: copia il codice sorgente e tutto ciò che è associato ad esso e avvia il processo di compilazione.

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

La fase finale della creazione dell'immagine finale consiste nel specificare l'immagine di base dell'applicazione, in cui verranno posizionati tutti gli artefatti di compilazione e i file di configurazione. Per copiare file compilati dall'immagine dell'assembly intermedio, è 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 non resta che aggiungere le dipendenze necessarie affinché la nostra applicazione funzioni e specificare il comando di avvio tramite le istruzioni ENTRYPOINT o CMD.

conclusione

In questo articolo ho parlato di come creare un ambiente di compilazione completo per applicazioni C++ all'interno di un contenitore in Windows e di come utilizzare le funzionalità delle build multistadio docker per creare immagini complete della nostra applicazione.

Fonte: habr.com

Aggiungi un commento