Utilisation de Docker en plusieurs étapes pour créer des images Windows

Salut tout le monde! Je m'appelle Andrey et je travaille en tant qu'ingénieur DevOps chez Exness au sein de l'équipe de développement. Mon activité principale est liée à la construction, au déploiement et au support d'applications dans docker sous le système d'exploitation Linux (ci-après dénommé l'OS). Il n'y a pas si longtemps, j'avais une tâche avec les mêmes activités, mais le système d'exploitation cible du projet était Windows Server et un ensemble de projets C++. Pour moi, il s'agissait de la première interaction étroite avec les conteneurs Docker sous Windows et, en général, avec les applications C++. Grâce à cela, j'ai vécu une expérience intéressante et découvert certaines des subtilités de la conteneurisation d'applications sous Windows.

Utilisation de Docker en plusieurs étapes pour créer des images Windows

Dans cet article, je veux vous raconter les difficultés auxquelles j'ai dû faire face et comment j'ai réussi à les résoudre. J'espère que cela vous sera utile pour vos défis actuels et futurs. Bonne lecture!

Pourquoi des conteneurs ?

La société dispose d'une infrastructure existante pour l'orchestrateur de conteneurs Hashicorp Nomad et les composants associés - Consul et Vault. Par conséquent, la conteneurisation des applications a été choisie comme méthode unifiée pour fournir une solution complète. Étant donné que l'infrastructure du projet contient des hôtes Docker avec les versions 1803 et 1809 du système d'exploitation Windows Server Core, il est nécessaire de créer des versions distinctes des images Docker pour 1803 et 1809. Dans la version 1803, il est important de se rappeler que le numéro de révision de l'hôte Docker de construction 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 lancé. La version 1809 ne présente pas un tel inconvénient. Vous pouvez en lire davantage ici.

Pourquoi en plusieurs étapes ?

Les ingénieurs de l'équipe de développement n'ont pas d'accès ou un accès très limité aux hôtes de génération ; il n'existe aucun moyen de gérer rapidement l'ensemble des composants permettant de créer une application sur ces hôtes, par exemple, installer un ensemble d'outils ou une charge de travail supplémentaire pour Visual Studio. Par conséquent, nous avons pris la décision d'installer tous les composants nécessaires pour créer l'application dans l'image Docker de construction. Si nécessaire, vous pouvez modifier rapidement uniquement le fichier docker et lancer le pipeline pour créer cette image.

De la théorie à l'action

Dans une création d'image Docker en plusieurs étapes idéale, l'environnement de création de l'application est préparé dans le même script Dockerfile que celui de la création de l'application elle-même. Mais dans notre cas, un lien intermédiaire a été ajouté, à savoir l'étape de création préalable d'une image docker avec tout le nécessaire pour construire l'application. Cela a été fait parce que je souhaitais utiliser la fonctionnalité de cache Docker pour réduire le temps d'installation de toutes les dépendances.

Regardons les principaux points du script dockerfile pour créer cette image.

Pour créer des images de différentes versions du système d'exploitation, vous pouvez définir un argument dans le fichier docker par lequel le numéro de version est transmis lors de la construction, et c'est également la balise de l'image de base.

Une liste complète des balises d'image 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 fichier docker sous le système d'exploitation Windows, ils sont exécutés dans la console cmd.exe. Pour faciliter l'écriture de scripts et étendre les fonctionnalités des commandes utilisées, nous redéfinirons la console d'exécution des commandes dans Powershell via l'instruction SHELL.

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

L'étape suivante consiste à installer le gestionnaire de packages chocolatey et les packages 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 packages à l'aide de chocolatey, vous pouvez simplement les transmettre sous forme de liste ou les installer un par un si vous devez transmettre des paramètres uniques pour chaque package. Dans notre situation, nous avons utilisé un fichier manifeste au format XML, qui contient une liste des packages requis et leurs paramètres. Son contenu ressemble à ceci :

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

Ensuite, nous installons l'environnement de construction d'applications, à savoir MS Build Tools 2019 - il s'agit d'une version allégée de Visual Studio 2019, qui contient l'ensemble minimum de composants requis pour compiler le code.
Pour travailler pleinement avec notre projet C++, nous aurons besoin de composants supplémentaires, à savoir :

  • Outils C++ pour les charges de travail
  • Ensemble d'outils v141
  • Kit de développement logiciel Windows 10 (10.0.17134.0)

Vous pouvez installer automatiquement un ensemble étendu d'outils à l'aide d'un fichier de configuration au format JSON. Contenu du fichier de configuration :

Une liste complète des composants disponibles est disponible 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"
  ]
}

Le dockerfile exécute le script d'installation et, pour plus de commodité, ajoute le chemin des fichiers exécutables des outils de construction à la variable d'environnement. PATH. Il est également conseillé de supprimer les fichiers et répertoires inutiles pour 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 procéder directement à la création d'une version Docker en plusieurs étapes de l'application.

Plusieurs étapes en action

Nous utiliserons l’image créée avec tous les outils intégré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/la balise d'image pour faciliter la réutilisation du code. Il est important d'ajouter une étiquette as builder à l'image d'assemblage dans les instructions FROM.

ARG WINDOWS_OS_VERSION=1809
FROM buildtools:$WINDOWS_OS_VERSION as builder

Il est maintenant temps de créer l'application. Tout ici est assez simple : copiez le code source et tout ce qui lui est associé, et lancez le processus de compilation.

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

La dernière étape de la création de l'image finale consiste à spécifier l'image de base de l'application, où se trouveront tous les artefacts de compilation et fichiers de configuration. Pour copier des fichiers compilés à partir de l'image d'assemblage intermédiaire, vous devez spécifier le paramètre --from=builder dans les instructions 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 au fonctionnement de notre application et à spécifier la commande de lancement via les instructions ENTRYPOINT ou CMD.

Conclusion

Dans cet article, j'ai expliqué comment créer un environnement de compilation à part entière pour les applications C++ dans un conteneur sous Windows et comment utiliser les capacités des builds en plusieurs étapes de Docker pour créer des images à part entière de notre application.

Source: habr.com

Ajouter un commentaire