Verwenden von Docker Multi-Stage zum Erstellen von Windows-Images

Hallo zusammen! Mein Name ist Andrey und ich arbeite als DevOps-Ingenieur bei Exness im Entwicklungsteam. Meine Haupttätigkeit bezieht sich auf das Erstellen, Bereitstellen und Unterstützen von Anwendungen in Docker unter dem Linux-Betriebssystem (im Folgenden als Betriebssystem bezeichnet). Vor nicht allzu langer Zeit hatte ich eine Aufgabe mit denselben Aktivitäten, aber das Zielbetriebssystem des Projekts war Windows Server und eine Reihe von C++-Projekten. Für mich war dies die erste enge Interaktion mit Docker-Containern unter Windows und allgemein mit C++-Anwendungen. Dadurch hatte ich eine interessante Erfahrung und lernte einige Feinheiten der Containerisierung von Anwendungen in Windows kennen.

Verwenden von Docker Multi-Stage zum Erstellen von Windows-Images

In diesem Artikel möchte ich Ihnen erzählen, mit welchen Schwierigkeiten ich konfrontiert war und wie ich es geschafft habe, sie zu lösen. Ich hoffe, dass dies hilfreich für Ihre aktuellen und zukünftigen Herausforderungen ist. Viel Spaß beim Lesen!

Warum Container?

Das Unternehmen verfügt über eine bestehende Infrastruktur für den Hashicorp Nomad Container Orchestrator und die zugehörigen Komponenten – Consul und Vault. Daher wurde die Anwendungscontainerisierung als einheitliche Methode zur Bereitstellung einer Komplettlösung gewählt. Da die Projektinfrastruktur Docker-Hosts mit den Windows Server Core-Betriebssystemversionen 1803 und 1809 enthält, ist es notwendig, separate Versionen von Docker-Images für 1803 und 1809 zu erstellen. In Version 1803 ist es wichtig, sich die Revisionsnummer des Build-Docker-Hosts zu merken muss mit der Revisionsnummer des Basis-Docker-Images und des Hosts übereinstimmen, auf dem der Container aus diesem Image gestartet wird. Version 1809 hat keinen solchen Nachteil. Sie können mehr lesen hier.

Warum mehrstufig?

Ingenieure des Entwicklungsteams haben keinen oder nur sehr begrenzten Zugriff auf Build-Hosts. Es gibt keine Möglichkeit, den Komponentensatz zum Erstellen einer Anwendung auf diesen Hosts schnell zu verwalten, beispielsweise ein zusätzliches Toolset oder eine Arbeitslast für Visual Studio zu installieren. Aus diesem Grund haben wir beschlossen, alle zum Erstellen der Anwendung erforderlichen Komponenten im Build-Docker-Image zu installieren. Bei Bedarf können Sie schnell nur die Docker-Datei ändern und die Pipeline zum Erstellen dieses Images starten.

Von der Theorie zur Tat

Bei einem idealen mehrstufigen Docker-Image-Build wird die Umgebung zum Erstellen der Anwendung im selben Dockerfile-Skript vorbereitet, in dem auch die Anwendung selbst erstellt wird. In unserem Fall wurde jedoch ein Zwischenlink hinzugefügt, nämlich der Schritt der vorläufigen Erstellung eines Docker-Images mit allem, was zum Erstellen der Anwendung erforderlich ist. Dies geschah, weil ich die Docker-Cache-Funktion nutzen wollte, um die Installationszeit aller Abhängigkeiten zu verkürzen.

Schauen wir uns die Hauptpunkte des Dockerfile-Skripts zum Erstellen dieses Images an.

Um Images verschiedener Betriebssystemversionen zu erstellen, können Sie in der Docker-Datei ein Argument definieren, über das die Versionsnummer während des Builds übergeben wird und das auch das Tag des Basisimages ist.

Eine vollständige Liste der Microsoft Windows Server-Image-Tags finden Sie hier hier.

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

Standardmäßig die Befehle in der Anleitung RUN Innerhalb der Docker-Datei unter Windows werden sie in der cmd.exe-Konsole ausgeführt. Um das Schreiben von Skripten zu vereinfachen und die Funktionalität der verwendeten Befehle zu erweitern, werden wir die Befehlsausführungskonsole in Powershell mithilfe der Anweisung neu definieren SHELL.

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

Der nächste Schritt besteht darin, den Chocolatey-Paketmanager und die erforderlichen Pakete zu installieren:

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'

Um Pakete mit Chocolatey zu installieren, können Sie sie einfach als Liste übergeben oder sie einzeln installieren, wenn Sie für jedes Paket eindeutige Parameter übergeben müssen. In unserer Situation haben wir eine Manifestdatei im XML-Format verwendet, die eine Liste der erforderlichen Pakete und ihrer Parameter enthält. Sein Inhalt sieht so aus:

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

Als nächstes installieren wir die Anwendungserstellungsumgebung, nämlich MS Build Tools 2019 – dies ist eine schlanke Version von Visual Studio 2019, die den minimal erforderlichen Satz an Komponenten zum Kompilieren von Code enthält.
Um vollständig mit unserem C++-Projekt arbeiten zu können, benötigen wir zusätzliche Komponenten, nämlich:

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

Mithilfe einer Konfigurationsdatei im JSON-Format können Sie einen erweiterten Satz an Tools automatisch installieren. Inhalt der Konfigurationsdatei:

Eine vollständige Liste der verfügbaren Komponenten finden Sie auf der Dokumentationsseite 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"
  ]
}

Die Docker-Datei führt das Installationsskript aus und fügt der Umgebungsvariablen der Einfachheit halber den Pfad zu den ausführbaren Dateien der Build-Tools hinzu PATH. Es empfiehlt sich außerdem, unnötige Dateien und Verzeichnisse zu entfernen, um die Größe des Bildes zu reduzieren.

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)

Zu diesem Zeitpunkt ist unser Image zum Kompilieren der C++-Anwendung fertig und wir können direkt mit der Erstellung eines mehrstufigen Docker-Builds der Anwendung fortfahren.

Mehrstufig im Einsatz

Wir werden das erstellte Image mit allen integrierten Tools als Build-Image verwenden. Wie im vorherigen Dockerfile-Skript werden wir die Möglichkeit hinzufügen, die Versionsnummer/das Image-Tag dynamisch anzugeben, um die Wiederverwendung des Codes zu vereinfachen. Es ist wichtig, eine Beschriftung hinzuzufügen as builder Siehe Montagebild in der Anleitung FROM.

ARG WINDOWS_OS_VERSION=1809
FROM buildtools:$WINDOWS_OS_VERSION as builder

Jetzt ist es an der Zeit, die Anwendung zu erstellen. Hier ist alles ganz einfach: Kopieren Sie den Quellcode und alles, was dazu gehört, und starten Sie den Kompilierungsvorgang.

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

Der letzte Schritt beim Erstellen des endgültigen Images besteht darin, das Basisimage der Anwendung anzugeben, in dem sich alle Kompilierungsartefakte und Konfigurationsdateien befinden. Um kompilierte Dateien aus dem Zwischen-Assembly-Image zu kopieren, müssen Sie den Parameter angeben --from=builder in der Anleitung COPY.

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

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

Jetzt müssen wir nur noch die notwendigen Abhängigkeiten hinzufügen, damit unsere Anwendung funktioniert, und den Startbefehl über die Anweisungen angeben ENTRYPOINT oder CMD.

Abschluss

In diesem Artikel habe ich darüber gesprochen, wie man eine vollwertige Kompilierungsumgebung für C++-Anwendungen in einem Container unter Windows erstellt und wie man die Fähigkeiten von mehrstufigen Docker-Builds nutzt, um vollwertige Images unserer Anwendung zu erstellen.

Source: habr.com

Kommentar hinzufügen