Gebruik van Docker multi-stage voor het bouwen van Windows-images

Hallo allemaal! Mijn naam is Andre en ik werk als DevOps-engineer bij Exness in het ontwikkelingsteam. Mijn belangrijkste taak is het bouwen, implementeren en onderhouden van applicaties in Docker op het Linux-besturingssysteem (verder — OS). Onlangs kreeg ik de taak om dezelfde activiteiten uit te voeren, maar met Windows Server als de doel-OS van het project en een reeks projecten in C++. Dit was mijn eerste intensieve interactie met Docker-containers onder Windows OS en in het algemeen met applicaties in C++. Hierdoor heb ik interessante ervaringen opgedaan en heb ik meer geleerd over enkele nuances van applicatiecontainerisatie in Windows OS.

Gebruik van Docker multi-stage voor het bouwen van Windows-images

In dit artikel wil ik vertellen met welke moeilijkheden ik te maken kreeg en hoe ik ze heb opgelost. Ik hoop dat dit nuttig zal zijn voor het oplossen van uw huidige en toekomstige taken. Veel leesplezier!

Waarom containers?

Het bedrijf heeft een bestaande infrastructuur voor containerorkestratie met Hashicorp Nomad en de bijbehorende componenten — Consul en Vault. Daarom werd applicatiecontainerisatie gekozen als de gestandaardiseerde methode voor het leveren van een kant-en-klare oplossing. Aangezien er in de projectinfrastructuur Docker-hosts zijn met versies van Windows Server Core 1803 en 1809, is het noodzakelijk om afzonderlijke versies van Docker-images voor 1803 en 1809 te bouwen. Het is belangrijk te onthouden dat bij versie 1803 het revisienummer van de bouw-Docker-host moet overeenkomen met het revisienummer van de basis-Docker-image en de host waar de container uit deze image zal worden uitgevoerd. Versie 1809 heeft dit nadeel niet. Meer informatie hierover kan gelezen worden. hier.

Waarom multi-stage?

De ontwikkelingsingenieurs hebben geen toegang tot de bouw-hosten of deze is sterk beperkt, en er is geen mogelijkheid om snel de set componenten voor het bouwen van de applicatie op deze hosts te beheren, bijvoorbeeld extra toolsets of workloads voor Visual Studio te installeren. Daarom hebben we besloten — alle noodzakelijke componenten voor het bouwen van de applicatie in de bouw-Docker-image te installeren. Indien nodig kan het Dockerfile vrij snel worden aangepast en de pipeline voor het maken van deze image worden gestart.

Van theorie naar praktijk

In een ideale docker multi-stage build van een afbeelding vindt de voorbereiding van de omgeving voor het bouwen van de applicatie plaats in hetzelfde dockerfile-script als de bouw van de applicatie zelf. Maar in ons geval hebben we een tussenstap toegevoegd, namelijk de stap voor het vooraf creƫren van de docker-afbeelding met alles wat nodig is om de applicatie te bouwen. Dit is gedaan omdat we gebruik wilden maken van de mogelijkheden van docker cache om de tijd voor het installeren van alle afhankelijkheden te verkorten.

Laten we de belangrijkste punten van het dockerfile-script voor het opstellen van deze afbeelding doornemen.

Om afbeeldingen van verschillende versies van het besturingssysteem in het dockerfile te maken, kan een argument worden gedefinieerd waarmee het versienummer tijdens de build wordt doorgegeven, en dat is ook de tag van de basisafbeelding.

Een volledige lijst van de tags van Microsoft Windows Server-afbeeldingen is te vinden hier.

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

Standaard worden de commando's in de instructie RUN binnen het dockerfile op Windows uitgevoerd in de cmd.exe-console. Voor het gemak van het schrijven van scripts en het uitbreiden van de functionaliteit van de gebruikte commando's, overschrijven we de uitvoerconsole naar Powershell met de instructie SHELL.

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

De volgende stap is het installeren van de pakketbeheerder chocolatey en de benodigde pakketten:

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'

Om pakketten met chocolatey te installeren, kun je eenvoudig een lijst doorgeven of elk afzonderlijk installeren als je unieke parameters voor elk pakket wilt doorgeven. In onze situatie hebben we een manifestbestand in XML-formaat gebruikt, waarin de lijst van benodigde pakketten en hun parameters is opgegeven. De inhoud ervan ziet er als volgt uit:

<?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>

Verder stellen we de build-omgeving van de applicatie in, namelijk MS Build Tools 2019 — dit is een lichtgewicht versie van Visual Studio 2019, die de minimaal noodzakelijke set componenten bevat voor het compileren van code.
Voor een volledige werking met ons C++-project hebben we aanvullende componenten nodig, namelijk:

  • Werkbelasting C++-tools
  • Toolset v141
  • Windows 10 SDK (10.0.17134.0)

U kunt de uitgebreide toolset automatisch installeren met behulp van een JSON-configuratiebestand. De inhoud van het configuratiebestand:

Een volledige lijst van beschikbare componenten vindt u op de documentatiewebsite 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"
  ]
}

In de dockerfile wordt een installatie-script uitgevoerd, en voor het gemak wordt het pad naar de uitvoerbare bestanden van build tools aan de omgevingsvariabele toegevoegd PAD. Het is ook aan te raden om onnodige bestanden en mappen te verwijderen om de grootte van de afbeelding te verkleinen.

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)

In deze fase is onze afbeelding voor het compileren van de C++-toepassing gereed, en kunnen we beginnen met het daadwerkelijk creƫren van de Docker multi-stage build van de toepassing.

Multi-stage in actie

Als build-image gebruiken we de gemaakte afbeelding met alle tools aan boord. Evenals in het vorige dockerfile-script, voegen we de mogelijkheid toe om dynamisch het versienummer/tag van de afbeelding aan te geven voor het gemak van hergebruik van code. Het is belangrijk om een label toe te voegen als builder aan de build-image in de instructie FROM.

ARG WINDOWS_OS_VERSION=1809
FROM buildtools:$WINDOWS_OS_VERSION as builder

Het is tijd om de toepassing te bouwen. Hier is alles vrij eenvoudig: kopieer de brondcode en alles wat daarmee samenhangt, en start het compilatieproces.

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

De laatste stap van het creƫren van de eindafbeelding is het opgeven van de basisafbeelding van de toepassing, waar alle compilatie-artifacten en configuratiebestanden zich zullen bevinden. Om de gecompileerde bestanden van de tussenliggende build-image te kopiƫren, moet de parameter worden opgegeven --from=builder in de instructie COPY.

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

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

Nu hoeven we alleen nog de vereiste afhankelijkheden voor de werking van onze toepassing toe te voegen en het startcommando via de instructies op te geven ENTRYPOINT of CMD.

Conclusie

In dit artikel heb ik uitgelegd hoe je een volledige compilatie-omgeving voor C++-toepassingen kunt opzetten binnen een container op Windows en hoe je de mogelijkheden van docker multi-stage builds kunt gebruiken voor het creƫren van volledige afbeeldingen van onze toepassing.

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers šŸ”„ Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster