使用docker多阶段构建windows镜像

大家好! 我叫 Andrey,是 Exness 开发团队的 DevOps 工程师。 我的主要工作是在 Linux 操作系统(以下简称 OS)下在 docker 中构建、部署和支持应用程序。 不久前,我有一个具有相同活动的任务,但该项目的目标操作系统是 Windows Server 和一组 C++ 项目。 对我来说,这是与 Windows 操作系统下的 docker 容器以及一般而言与 C++ 应用程序的第一次密切交互。 正因为如此,我获得了一次有趣的经历,并了解了 Windows 中容器化应用程序的一些复杂性。

使用docker多阶段构建windows镜像

在这篇文章中,我想告诉你我遇到过哪些困难以及我是如何解决这些困难的。 我希望这对您当前和未来的挑战有所帮助。 享受阅读!

为什么是容器?

该公司拥有 Hashicorp Nomad 容器编排器和相关组件(Consul 和 Vault)的现有基础设施。 因此,选择应用程序容器化作为交付完整解决方案的统一方法。 由于项目基础架构包含 Windows Server Core OS 版本 1803 和 1809 的 docker 主机,因此有必要为 1803 和 1809 构建单独版本的 docker 映像。在版本 1803 中,重要的是要记住构建 docker 主机的修订号必须与基础 docker 映像的修订号以及将启动该映像中的容器的主机相匹配。 1809版本没有这样的缺点。 您可以阅读更多内容 这里.

为什么要多阶段?

开发团队工程师没有或非常有限地访问构建主机;无法快速管理用于在这些主机上构建应用程序的组件集,例如,为 Visual Studio 安装附加工具集或工作负载。 因此,我们决定将构建应用程序所需的所有组件安装到构建 Docker 映像中。 如有必要,您可以快速仅更改 dockerfile 并启动用于创建此映像的管道。

从理论到行动

在理想的 Docker 多阶段映像构建中,构建应用程序的环境是在构建应用程序本身的同一个 Dockerfile 脚本中准备的。 但在我们的例子中,添加了一个中间环节,即初步创建 docker 镜像以及构建应用程序所需的一切的步骤。 这样做是因为我想使用 docker 缓存功能来减少所有依赖项的安装时间。

我们来看看创建这个镜像的 dockerfile 脚本的要点。

要创建不同操作系统版本的镜像,可以在dockerfile中定义一个参数,在构建过程中通过该参数传递版本号,同时它也是基础镜像的标签。

可以找到 Microsoft Windows Server 映像标签的完整列表 这里.

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

默认情况下使用说明中的命令 RUN 在 Windows 操作系统上的 dockerfile 内,它们在 cmd.exe 控制台中执行。 为了方便编写脚本和扩展所使用命令的功能,我们将通过指令重新定义Powershell中的命令执行控制台 SHELL.

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

下一步是安装 Chocolatey 包管理器和必要的包:

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'

要使用 Chocolatey 安装软件包,您只需将它们作为列表传递,或者如果您需要为每个软件包传递唯一的参数,则一次安装一个。 在我们的例子中,我们使用了 XML 格式的清单文件,其中包含所需包及其参数的列表。 其内容如下所示:

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

接下来,我们安装应用程序构建环境,即 MS Build Tools 2019 - 这是 Visual Studio 2019 的轻量级版本,其中包含编译代码所需的最少组件集。
为了充分使用我们的 C++ 项目,我们需要额外的组件,即:

  • 工作负载 C++ 工具
  • 工具集 v141
  • Windows 10 SDK (10.0.17134.0)

您可以使用 JSON 格式的配置文件自动安装一组扩展工具。 配置文件内容:

可用组件的完整列表可以在文档站点上找到 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"
  ]
}

dockerfile运行安装脚本,为了方便,将构建工具可执行文件的路径添加到环境变量中 PATH。 还建议删除不必要的文件和目录以减小图像的大小。

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)

在此阶段,用于编译 C++ 应用程序的映像已准备就绪,我们可以直接继续创建应用程序的 docker 多阶段构建。

多阶段行动

我们将使用创建的映像以及板上的所有工具作为构建映像。 与之前的 dockerfile 脚本一样,我们将添加动态指定版本号/图像标签的功能,以便于代码重用。 添加标签很重要 as builder 到说明中的装配图像 FROM.

ARG WINDOWS_OS_VERSION=1809
FROM buildtools:$WINDOWS_OS_VERSION as builder

现在是时候构建应用程序了。 这里的一切都非常简单:复制源代码和与之相关的所有内容,然后开始编译过程。

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

创建最终映像的最后阶段是指定应用程序的基础映像,所有编译工件和配置文件都将位于其中。 要从中间程序集映像复制已编译的文件,必须指定参数 --from=builder 在说明中 COPY.

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

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

现在剩下的就是添加应用程序运行所需的依赖项,并通过说明指定启动命令 ENTRYPOINT или CMD.

结论

在本文中,我讨论了如何在 Windows 下的容器内为 C++ 应用程序创建成熟的编译环境,以及如何使用 docker 多阶段构建的功能来创建应用程序的成熟镜像。

来源: habr.com

添加评论