tl;dr:可以在同一个 NuGet 缓存上同时构建多个 .NET 应用程序吗?
我有一个 Visual Studio 解决方案,其中包含要在 Docker 中托管的多个库和多个应用程序。我希望我的构建尽可能快,因此我涉及尽可能多的缓存。这是通过两种方式实现的:首先,仅将必要的 .csproj 文件复制到各自的目录中,只要我不编辑任何 .csproj,就可以缓存该层。不能对此副本使用通配符,因为下一步需要 BuildKit。
--mount=type=cache,id=nuget,target=/root/.nuget/packages然后在每条指令之前使用RUN,以便在构建之间在主机上拥有可重用的 NuGet 缓存:
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
WORKDIR /app
# Copy all required (transitive) project references
COPY src/SampleApp.Common/SampleApp.Common.csproj ./SampleApp.Common/SampleApp.Common.csproj
COPY src/SampleApp.Data/SampleApp.Data.csproj ./SampleApp.Data/SampleApp.Data.csproj
# Then copy ourselves
COPY src/SampleApp.Api/SampleApp.Api.csproj ./SampleApp.Api/SampleApp.Api.csproj
# Restore as distinct layers
# Mount NuGet cache dir as cache in Docker for faster subsequent builds.
RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
dotnet restore SampleApp.Api --runtime linux-x64
# Copy all required (transitive) project sources
COPY src/SampleApp.Common ./SampleApp.Common
COPY src/SampleApp.Data ./SampleApp.Data
# Then copy ourselves
COPY src/SampleApp.Api SampleApp.Api
# Build and publish the release (needs cached packages to copy on build)
RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
dotnet publish SampleApp.Api \
--no-restore \
--runtime linux-x64 \
--self-contained false \
--configuration Release \
--output ./Publish/SampleApp.Api/
FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
COPY --from=build-env /app/Publish/SampleApp.Api .
ENTRYPOINT ["dotnet", "SampleApp.Api.dll"]
Run Code Online (Sandbox Code Playgroud)
现在这可行了,尽管维护较大项目的 Dockerfile 变得很麻烦(添加新的库项目需要编辑多个 Dockerfile 中的多行),但是当需要同时构建多个项目时就会出现问题。
命令:
docker compose up -d --build
Run Code Online (Sandbox Code Playgroud)
拥有多个像这样的 Dockerfile 项目,一段时间后会显示:
=> 错误 [sampleapp_api build-env 16/30] 运行 --mount=type=cache,id=nuget,target=/root/.nuget/packages dotnet Restore SampleApp.Api --runtime linux-x64
#12 34.56 /usr/share/dotnet/sdk/6.0.302/NuGet.targets(130,5):错误:找不到文件'/root/.nuget/packages/microsoft.aspnetcore.app.runtime.linux- x64/6.0.7/ fgp1y1vi.2tu '. [/app/SampleApp.Api/SampleApp.Api.csproj]
在第二次运行时,两个项目同时失败:
/usr/share/dotnet/sdk/6.0.302/NuGet.targets(130,5):错误:找不到文件'/root/.nuget/packages/runtime.any.system.runtime.interopservices/4.1.0 /q0z4zwua.tf3 /usr/share/dotnet/sdk/6.0.302/NuGet.targets(130,5):错误:找不到文件“/root/.nuget/packages/runtime.any.system.runtime.interopservices ” /4.1.0/ xpvko1tv.2o3
可能是因为第三个项目正在那个时间恢复该库。这是因为多个构建使用相同的 NuGet 缓存:
--mount=type=cache,id=nuget,target=/root/.nuget/packages
由于 NuGet 在每次构建时都会使用随机文件名提取包的内容,从而清除其中已有的内容,因此在并行构建中,一个构建会清除另一个构建的 NuGet 缓存,从而导致上述构建(恢复)错误。
现在有几种解决方法,但我都不喜欢:
--mount=type=cache,id=nuget-sampleapp-api、、...,id=nuget-sampleapp-web...)。这将炸毁磁盘上的缓存,并且一些 Docker 构建将很高兴地消耗掉几 GB 的空间。NVMe 空间很昂贵。docker compose build sampleapp_api、... sampleapp_web、 ...)。每次更新共享项目时,我都必须对每个应用程序映像执行此操作。或者编写脚本。更多的脚本,更多的维护,更多的非标准构建步骤,不喜欢它。它超越了并行构建的目的。还有其他建议吗?
在与这个确切的问题斗争了很长时间之后,我终于弄清楚为什么这不起作用以及如何解决它。
此评论引导我找到解决方案:https://github.com/NuGet/Home/issues/7060#issuecomment-732065148
因此,如果正在运行并发恢复操作,这些操作使用不同的临时路径,但共享相同的全局包缓存,则缓存根本不受锁定机制的真正保护
NuGet 使用临时文件夹 ( /tmp/NuGetScratch),您可以在此处阅读:https://learn.microsoft.com/en-us/nuget/consume-packages/managing-the-global-packages-and-cache-folders安装此临时文件夹目录和全局包缓存修复了这个问题。
确切的问题似乎是 NuGet 使用临时目录作为恢复进程之间的锁定机制,安装临时目录允许其在从 Docker 并行恢复时发挥作用。
此时,我在执行 NuGet 恢复时只需安装所有 NuGet 缓存文件夹,如下所示:
RUN \
--mount=type=cache,target=/root/.nuget/packages \
--mount=type=cache,target=/root/.local/share/NuGet/v3-cache \
--mount=type=cache,target=/root/.local/share/NuGet/plugins-cache \
--mount=type=cache,target=/tmp/NuGetScratch \
dotnet restore
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1384 次 |
| 最近记录: |