Utilisation de docker multi-stage pour la construction d'images Windows

Bonjour à tous ! Je m'appelle Andreï et je travaille en tant qu'ingénieur DevOps chez Exness dans l'équipe de développement. Mon activité principale consiste à assembler, déployer et maintenir des applications dans des conteneurs Docker sous le système d'exploitation Linux (ci-après dénommé OS). Récemment, j'ai eu la tâche de réaliser les mêmes activités, mais cette fois le système d'exploitation cible du projet était Windows Server et un ensemble de projets en C++. Cela a marqué ma première interaction intensive avec les conteneurs Docker sous Windows et, en général, avec des applications en C++. Grâce à cela, j'ai acquéri une expérience intéressante et découvert certaines subtilités de la conteneurisation d'applications sous Windows.

Utilisation de docker multi-stage pour la construction d'images Windows

Dans cet article, je souhaite vous parler des difficultés auxquelles j'ai été confronté et comment j'ai réussi à les surmonter. J'espère que cela vous sera utile pour résoudre vos problèmes actuels et futurs. Bonne lecture !

Pourquoi des conteneurs ?

L'entreprise dispose d'une infrastructure existante d'orchestrateur de conteneurs Hashicorp Nomad et de composants associés — Consul et Vault. C'est pourquoi la conteneurisation des applications a été choisie comme méthode unifiée pour livrer une solution prête à l'emploi. Comme il y a des hôtes Docker dans l'infrastructure du projet avec des versions de Windows Server Core 1803 et 1809, il est nécessaire de construire séparément les versions des images Docker pour 1803 et 1809. Pour la version 1803, il est important de garder à l'esprit que le numéro de révision de l'hôte de construction Docker doit correspondre au numéro de révision de l'image Docker de base et à l'hôte sur lequel le conteneur de cette image sera exécuté. La version 1809 ne présente pas ce désavantage. Pour plus de détails, vous pouvez lire ici.

Pourquoi multi-stage ?

Les ingénieurs de l'équipe de développement n'ont pas accès aux hôtes de construction ou celui-ci est fortement restreint, ce qui rend difficile la gestion en temps réel de l'ensemble des composants nécessaires à la construction d'une application sur ces hôtes, par exemple pour installer un toolset ou un workload supplémentaire pour Visual Studio. C'est pourquoi nous avons pris la décision d'installer tous les composants nécessaires à la construction de l'application dans l'image Docker de construction. Si besoin, il est assez rapide de modifier uniquement le Dockerfile et de lancer le pipeline de création de cette image.

De la théorie à la pratique

Dans une construction d'image Docker multi-étapes idéale, la préparation de l'environnement pour la compilation de l'application se fait dans le même script Dockerfile que la compilation elle-même. Cependant, dans notre cas, un lien intermédiaire a été ajouté, à savoir une étape de pré-création de l'image Docker avec tout le nécessaire pour la compilation de l'application. Cela a été fait pour profiter de la mise en cache de Docker, afin de réduire le temps d'installation de toutes les dépendances.

Examinons les points essentiels du script Dockerfile pour créer cette image.

Pour créer des images de différentes versions du système d'exploitation dans le Dockerfile, on peut définir un argument à travers lequel le numéro de version est transmis lors de la construction, qui sera également le tag de l'image de base.

La liste complète des tags d'images Microsoft Windows Server peut être trouvée ici.

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

Par défaut, les commandes dans les instructions RUN dans le Dockerfile sur Windows s'exécutent dans la console cmd.exe. Pour faciliter l'écriture de scripts et étendre les fonctionnalités des commandes utilisées, nous allons redéfinir la console d'exécution des commandes en Powershell via l'instruction SHELL.

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

La prochaine étape consiste à installer le gestionnaire de paquets Chocolatey et les paquets nécessaires :

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'

Pour installer des paquets en utilisant Chocolatey, il suffit de les transmettre sous forme de liste ou de les installer un par un dans le cas où des paramètres uniques sont à transmettre pour chaque paquet. Dans notre situation, nous avons utilisé un fichier manifest au format XML, contenant la liste des paquets nécessaires et leurs paramètres. Son contenu est le suivant :

Ensuite, nous installons l'environnement de compilation de l'application, à savoir MS Build Tools 2019 — une version allégée de Visual Studio 2019 qui inclut un minimum de composants nécessaires pour compiler le code.
Pour travailler pleinement avec notre projet C++, nous aurons besoin de composants supplémentaires, à savoir :

  • Outils de charge de travail C++
  • Ensemble d'outils v141
  • SDK Windows 10 (10.0.17134.0)

Vous pouvez installer un ensemble d'outils avancé en mode automatique à l'aide d'un fichier de configuration au format JSON. Le contenu du fichier de configuration :

Vous pouvez trouver la liste complète des composants disponibles sur le site de documentation 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"
  ]
}

Un script d'installation est exécuté dans le dockerfile, et pour faciliter les choses, le chemin vers les fichiers exécutables des outils de construction est ajouté à la variable d'environnement PATH. Il est également souhaitable de supprimer les fichiers et répertoires inutiles afin de réduire la taille de l'image.

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)

À ce stade, notre image pour compiler l'application C++ est prête, et nous pouvons passer directement à la création d'une construction multi-stage docker de l'application.

Multi-stage en action

Nous allons utiliser l'image créée avec tous les outils embarqués comme image de construction. Comme dans le script dockerfile précédent, nous ajouterons la possibilité de spécifier dynamiquement le numéro de version / tag de l'image pour faciliter la réutilisation du code. Il est important d'ajouter l'étiquette comme constructeur à l'image de construction dans l'instruction DE.

ARG WINDOWS_OS_VERSION=1809
FROM buildtools:$WINDOWS_OS_VERSION as builder

Le moment est venu de construire l'application. Ici, c'est assez simple : copier le code source et tout ce qui y est lié, et lancer le processus de compilation.

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

L'étape finale de la création de l'image finale consiste à spécifier l'image de base de l'application, où tous les artefacts de compilation et les fichiers de configuration seront situés. Pour copier les fichiers compilés de l'image de construction intermédiaire, il faut indiquer le paramètre --from=builder dans le manuel d'utilisation COPY.

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

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

Il ne reste plus qu'à ajouter les dépendances nécessaires pour exécuter notre application et à spécifier la commande de lancement via les instructions ENTRYPOINT ou CMD.

Conclusion

Dans cet article, je vous explique comment créer un environnement complet de compilation d'applications C++ à l'intérieur d'un conteneur sous Windows et comment utiliser les capacités des constructions multi-étapes de Docker pour créer des images complètes de notre application.

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster