.NET Core Docker 再也不用逐个 COPY csproj 文件啦!

2020-05-07 23:41:37 浏览数 (1)

导语

ASP.NET Core 项目可以很容易的通过 Visual Studio 一键添加 Docker 支持。VS会帮你自动生成绝对能跑的 Dockerfile。然而随着项目的增大,这个 Dockerfile 会有对应的维护工作,我们来看看如何一劳永逸的简化它!

默认 Dockerfile 的问题

对于一个典型的 ASP.NET Core 项目(例如我的博客系统)来说,VS 默认添加的 Dockerfile 通常如下:

代码语言:shell复制
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
RUN apt-get update && apt-get install -y libgdiplus
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["Moonglade.Web/Moonglade.Web.csproj", "Moonglade.Web/"]
COPY ["Moonglade.Web.Authentication/Moonglade.Web.Authentication.csproj", "Moonglade.Web.Authentication/"]
COPY ["Moonglade.Web.SiteIconGenerator/Moonglade.Web.SiteIconGenerator.csproj", "Moonglade.Web.SiteIconGenerator/"]
COPY ["Moonglade.Web.Middleware/Moonglade.Web.Middleware.csproj", "Moonglade.Web.Middleware/"]
# Common
COPY ["Moonglade.DateTimeOps/Moonglade.DateTimeOps.csproj", "Moonglade.DateTimeOps/"]
# 篇幅关系省略更多的 COPY
RUN dotnet restore "Moonglade.Web/Moonglade.Web.csproj"
COPY . .
WORKDIR "/src/Moonglade.Web"
RUN dotnet build "Moonglade.Web.csproj" -p:Version=10.3.0-docker -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "Moonglade.Web.csproj" -p:Version=10.3.0-docker -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Moonglade.Web.dll"]

其中包含 COPY 每个目录下 csproj 工程文件的步骤。而随着项目的扩充,工程文件会不断的增加、修改或删除。那么程序员就必须去 Dockerfile 里做对应的修改。在 996 的时候很容易忘改,导致编译爆炸,开猿节流。

自动操作,一劳永逸

作为一名有头发的程序员,当一件事情需要经常重复操作的时候,就应该想个办法自动化解决问题。我们希望Dockerfile 能自动去查找和COPY工程目录下所有的csproj文件,不想每次项目文件有更改还得手工维护 Dockerfile。

在花了 996 分钟研究,并两次放弃治疗之后,我终于站在国外资料的肩膀上自主研发成功了!

最终操作是使用下面两条指令,替换所有的 COPY 指令。

代码语言:shell复制
COPY ./**/*.csproj ./
RUN for file in $(ls *.csproj); do mkdir -p ./${file%.*}/ && mv $file ./${file%.*}/; done

方法参考:https://andrewlock.net/optimising-asp-net-core-apps-in-docker-avoiding-manually-copying-csproj-files-part-2/

根据实际项目差异,我对 Andrew Lock 的代码做了少许修改,但思路是一致的。

首先,第一行的 COPY ./**/*.csproj ./ 会把所有子目录下的 csproj 工程文件复制到 ./

然而这里面有个问题在于它不会保持原有的目录结构,所有被复制出来的 csproj 文件都会平行存储于 ./ 里。原作者的博客里也指出了这个问题:

因此需要通过第二条命令重建目录结构。按照正常 by convention 的 .NET 项目命名方法,文件夹的名称就是 csproj 的名称,因此创建文件夹时直接用 ${file%.*} 拿到csproj 的名称即可。最后再把 csproj 文件移动到各自的文件夹里,效果和 VS 自动生成的 COPY 指令一模一样。

最终版 Dockerfile

代码语言:shell复制
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
RUN apt-get update && apt-get install -y libgdiplus
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
# Auto copy to prevent 996
COPY ./**/*.csproj ./
RUN for file in $(ls *.csproj); do mkdir -p ./${file%.*}/ && mv $file ./${file%.*}/; done
RUN dotnet restore "Moonglade.Web/Moonglade.Web.csproj"
COPY . .
WORKDIR "/src/Moonglade.Web"
RUN dotnet build "Moonglade.Web.csproj" -p:Version=10.3.0-docker -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "Moonglade.Web.csproj" -p:Version=10.3.0-docker -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Moonglade.Web.dll"]

0 人点赞