Uso de Docker de varias etapas para crear imágenes de Windows

¡Hola a todos! Mi nombre es Andrey y trabajo como ingeniero de DevOps en Exness en el equipo de desarrollo. Mi actividad principal está relacionada con la creación, implementación y soporte de aplicaciones en Docker bajo el sistema operativo Linux (en adelante, el SO). No hace mucho tuve una tarea con las mismas actividades, pero el sistema operativo de destino del proyecto era Windows Server y un conjunto de proyectos de C++. Para mí, esta fue la primera interacción cercana con los contenedores Docker en el sistema operativo Windows y, en general, con las aplicaciones C++. Gracias a esto, tuve una experiencia interesante y aprendí algunas de las complejidades de la creación de contenedores en Windows.

Uso de Docker de varias etapas para crear imágenes de Windows

En este artículo quiero contarte qué dificultades tuve que afrontar y cómo logré solucionarlas. Espero que esto sea útil para sus desafíos actuales y futuros. ¡Disfruta leyendo!

¿Por qué contenedores?

La empresa cuenta con una infraestructura existente para el orquestador de contenedores Hashicorp Nomad y componentes relacionados: Consul y Vault. Por lo tanto, se eligió la contenedorización de aplicaciones como método unificado para ofrecer una solución completa. Dado que la infraestructura del proyecto contiene hosts de Docker con las versiones 1803 y 1809 del sistema operativo Windows Server Core, es necesario crear versiones separadas de las imágenes de Docker para 1803 y 1809. En la versión 1803, es importante recordar que el número de revisión del host de Docker compilado debe coincidir con el número de revisión de la imagen base de la ventana acoplable y el host donde se iniciará el contenedor de esta imagen. La versión 1809 no tiene tal inconveniente. Puedes leer más aquí.

¿Por qué multietapa?

Los ingenieros del equipo de desarrollo tienen acceso nulo o muy limitado para crear hosts; no hay forma de administrar rápidamente el conjunto de componentes para crear una aplicación en estos hosts, por ejemplo, instalar un conjunto de herramientas o una carga de trabajo adicional para Visual Studio. Por lo tanto, tomamos la decisión de instalar todos los componentes necesarios para crear la aplicación en la imagen de Docker de compilación. Si es necesario, puede cambiar rápidamente solo el archivo docker e iniciar la canalización para crear esta imagen.

De la teoría a la acción

En una creación de imágenes de varias etapas de Docker ideal, el entorno para crear la aplicación se prepara en el mismo script Dockerfile en el que se crea la aplicación. Pero en nuestro caso, se agregó un enlace intermedio, es decir, el paso de crear previamente una imagen de Docker con todo lo necesario para construir la aplicación. Esto se hizo porque quería utilizar la función de caché de Docker para reducir el tiempo de instalación de todas las dependencias.

Veamos los puntos principales del script dockerfile para crear esta imagen.

Para crear imágenes de diferentes versiones del sistema operativo, puede definir un argumento en el archivo acoplable a través del cual se pasa el número de versión durante la compilación, y también es la etiqueta de la imagen base.

Puede encontrar una lista completa de etiquetas de imágenes de Microsoft Windows Server aquí.

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

Por defecto los comandos en las instrucciones. RUN dentro del dockerfile en el sistema operativo Windows, se ejecutan en la consola cmd.exe. Para facilitar la escritura de scripts y ampliar la funcionalidad de los comandos utilizados, redefiniremos la consola de ejecución de comandos en Powershell mediante la instrucción SHELL.

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

El siguiente paso es instalar el administrador de paquetes chocolatey y los paquetes necesarios:

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'

Para instalar paquetes usando chocolatey, simplemente puede pasarlos como una lista o instalarlos uno a la vez si necesita pasar parámetros únicos para cada paquete. En nuestra situación, utilizamos un archivo de manifiesto en formato XML, que contiene una lista de los paquetes necesarios y sus parámetros. Su contenido se ve así:

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

A continuación, instalamos el entorno de compilación de la aplicación, es decir, MS Build Tools 2019; esta es una versión liviana de Visual Studio 2019, que contiene el conjunto mínimo requerido de componentes para compilar código.
Para trabajar completamente con nuestro proyecto C++, necesitaremos componentes adicionales, a saber:

  • Herramientas de carga de trabajo C++
  • Conjunto de herramientas v141
  • SDK de Windows 10 (10.0.17134.0)

Puede instalar un conjunto ampliado de herramientas automáticamente utilizando un archivo de configuración en formato JSON. Contenido del archivo de configuración:

Puede encontrar una lista completa de los componentes disponibles en el sitio de documentación. 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"
  ]
}

El dockerfile ejecuta el script de instalación y, para mayor comodidad, agrega la ruta a los archivos ejecutables de las herramientas de compilación a la variable de entorno. PATH. También es recomendable eliminar archivos y directorios innecesarios para reducir el tamaño de la imagen.

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)

En esta etapa, nuestra imagen para compilar la aplicación C++ está lista y podemos proceder directamente a crear una compilación acoplable de varias etapas de la aplicación.

Multietapa en acción

Usaremos la imagen creada con todas las herramientas a bordo como imagen de construcción. Como en el script de dockerfile anterior, agregaremos la capacidad de especificar dinámicamente el número de versión/etiqueta de imagen para facilitar la reutilización del código. Es importante agregar una etiqueta. as builder a la imagen de montaje en las instrucciones FROM.

ARG WINDOWS_OS_VERSION=1809
FROM buildtools:$WINDOWS_OS_VERSION as builder

Ahora es el momento de crear la aplicación. Aquí todo es bastante sencillo: copia el código fuente y todo lo asociado a él, y comienza el proceso de compilación.

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

La etapa final de la creación de la imagen final es especificar la imagen base de la aplicación, donde se ubicarán todos los artefactos de compilación y archivos de configuración. Para copiar archivos compilados de la imagen del ensamblaje intermedio, debe especificar el parámetro --from=builder en las instrucciones COPY.

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

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

Ahora solo queda agregar las dependencias necesarias para que nuestra aplicación funcione y especificar el comando de inicio a través de las instrucciones. ENTRYPOINT o CMD.

Conclusión

En este artículo, hablé sobre cómo crear un entorno de compilación completo para aplicaciones C++ dentro de un contenedor en Windows y cómo usar las capacidades de las compilaciones de múltiples etapas de Docker para crear imágenes completas de nuestra aplicación.

Fuente: habr.com

Añadir un comentario