我有一个.NET Core解决方案,其中包含6个可运行的应用程序(API(和多个网络标准项目。在 Azure DevOps 上的生成管道中,我需要创建 6 个 Docker 映像并将它们推送到 Azure 注册表。
现在我所做的是逐个映像构建,这 6 个 Dockerfile 中的每一个都从头开始构建解决方案(还原、构建、发布(。这需要几分钟,整个管道几乎需要 30 分钟。
我的目标是优化构建时间。我想到了两种可能的并行方法:
- 删除还原和生成,仅运行发布(因为它还原引用并与生成执行相同的操作(
- 发布一次代码(适用于所有可运行的应用程序(,在 Dockerfile 中只需复制二进制文件,无需再次构建
这两种方法都可行吗?我不知道如何使第二个工作 - 我应该为每个可运行的应用程序运行dotnet publish
,然后使用二进制文件收集文件夹中的所有 Dockerfile 并运行docker build
?我担心的是 - 我需要将所需的.dll文件复制到映像中,但我如何在没有明确指定的情况下选择哪些文件?
编辑:
我使用的是 Linux 容器。我不编写我的Dockerfile - 它们是由Visual Studio自动生成的。我给你看一个例子:
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS build
WORKDIR /src
COPY ["Application.WebAPI/Application.WebAPI.csproj", "Application.WebAPI/"]
COPY ["Processing.Dependency/Processing.Dependency.csproj", "Processing.Dependency/"]
COPY ["Processing.QueryHandling/Processing.QueryHandling.csproj", "Processing.QueryHandling/"]
COPY ["Model.ViewModels/Model.ViewModels.csproj", "Model.ViewModels/"]
COPY ["Core.Infrastructure/Core.Infrastructure.csproj", "Core.Infrastructure/"]
COPY ["Model.Values/Model.Values.csproj", "Model.Values/"]
COPY ["Sql.Business/Sql.Business.csproj", "Sql.Business/"]
COPY ["Model.Events/Model.Events.csproj", "Model.Events/"]
COPY ["Model.Messages/Model.Messages.csproj", "Model.Messages/"]
COPY ["Model.Commands/Model.Commands.csproj", "Model.Commands/"]
COPY ["Sql.Common/Sql.Common.csproj", "Sql.Common/"]
COPY ["Model.Business/Model.Business.csproj", "Model.Business/"]
COPY ["Processing.MessageBus/Processing.MessageBus.csproj", "Processing.MessageBus/"]
COPY ["Processing.CommandHandling/Processing.CommandHandling.csproj", "Processing.CommandHandling/"]
COPY ["Processing.EventHandling/Processing.EventHandling.csproj", "Processing.EventHandling/"]
COPY ["Sql.System/Sql.System.csproj", "Sql.System/"]
COPY ["Application.Common/Application.Common.csproj", "Application.Common/"]
RUN dotnet restore "Application.WebAPI/Application.WebAPI.csproj"
COPY . .
WORKDIR "/src/Application.WebAPI"
RUN dotnet build "Application.WebAPI.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "Application.WebAPI.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Application.WebApi.dll"]
还有一件事 - 问题是 azure devops 有这个工作来构建一个映像,我只是复制了这个作业 6 次,每个副本都指向其他 Dockerfile。这就是为什么他们不重用代码 - 我很想改变它,以便它们基于相同的二进制文件。下面是 Azure DevOps 中的步骤:
- 获取源
- 构建和推送 1 号映像
- 构建和推送映像 2
- 构建和推送图像 3
- 构建和推送图像 4
- 构建和推送第 5 号图像
- 构建并推送第 6 号映像
每个"构建和推送映像"都会执行以下操作:
- 点网还原
- 点网构建
- 点网发布
我想摆脱这种开销 - 可能吗?
很难说没有看到你的Dockerfiles,但你可能犯了一些错误,这些错误增加了映像构建的时间。例如,Dockerfile 中的每个命令都会生成一个图层。Docker 缓存这些层,并且仅在层或以前的层发生更改时才重建该层。
人们常犯的一个错误是首先复制包含所有文件的整个项目,然后运行dotnet restore
。执行此操作时,对任何文件的任何更改都会使该复制层失效,从而使dotnet restore
层失效,这意味着您必须在每次构建时还原包。dotnet restore
唯一需要的是项目文件,因此,如果只复制这些文件,运行dotnet restore
,然后复制所有文件,否则将缓存这些图层,除非项目文件本身发生更改。由于这通常只发生在您更改包(添加、更新、删除等(时,大多数时候,您不必重复还原步骤,并且构建速度会快得多。
当您在 Windows 上使用 npm 和 Linux 映像时,可能会出现另一个问题。这个咬了我个人。为了支持 Linux 映像,Docker 使用 Linux VM(MobyLinux(。在构建开始时,Docker 首先将整个文件系统上下文(即运行docker
命令的位置(提升到 MobyLinux VM 中,因为所有 Dockerfile 命令都将在 VM 中实际运行,因此文件需要驻留在那里。如果您有node_modules
目录,则可能需要花费大量时间才能将所有目录全部移出。您可以通过向.dockerignore
文件添加node_modules
来解决此问题。
您可能还会犯其他类似类型的错误。我们真的需要查看您的 Dockerfile 以进一步帮助您。无论如何,您都不应该使用您提出的任何一种方法。仅运行publish
会遇到上述相同的问题,并且此时您无法解决问题。在映像外部发布可能会导致平台不一致和其他问题,除非您非常小心。它还在映像构建过程中添加了一系列手动步骤,这破坏了Docker提供的很多好处。您的图像也会更大,除非您碰巧在与图像将使用的完全相同的体系结构上发布。例如,如果您在Windows上进行开发,但使用Linux映像,则必须包含完整的 ASP.NET Core运行时。如果在映像中生成和发布,则只能将 SDK 包含在要生成和发布的阶段中,然后使用特定于体系结构的独立发布来定位 alpine Linux 之类的内容。