02 加速构建netcore react app docker镜像 - xiaoxin01/Blog GitHub Wiki

本文介绍如何加速构建.netcore创建的react app 镜像

问题

.net core react app默认创建的dockerfile有2个问题:

  1. build镜像没有安装node
  2. 每次都是在publish动作中同时构建react app,即使js没有变化也会重新构建,非常耗时

默认的dockerfile大致如下:

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY ["src/projecta/projecta.csproj", "src/projecta/"]
RUN dotnet restore "src/projecta/projecta.csproj"
COPY ./src ./src
WORKDIR /src/src/projecta
RUN dotnet build "projecta.csproj" -c Release -o /app

FROM build AS publish
RUN dotnet publish "projecta.csproj" -c Release -o /app

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "projecta.dll"]

检查.csproj文件,发现每次在publish的时候会执行npm build动作:

  <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
    <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build" />

    <!-- Include the newly-built files in the publish output -->
    <ItemGroup>
      <DistFiles Include="$(SpaRoot)build\**" />
      <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
        <RelativePath>%(DistFiles.Identity)</RelativePath>
        <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
        <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
      </ResolvedFileToPublish>
    </ItemGroup>
  </Target>

为了优化构建过程,我们从上述的2个问题出发:

  1. 创建新的build stage,用来构建前端react app
  2. 移除默认的在publish的时候构建动作

为此我们修改dockerfile如下:

LABEL maintainer="Jinxin Chen <[email protected]>"

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM node:12.14.0-alpine3.9 AS nodeBuild
# add node registry for speed up
RUN npm config set registry https://registry.npm.taobao.org --global && \
    npm config set disturl https://npm.taobao.org/dist --global
WORKDIR /app
COPY ./src/projecta/ClientApp/package.json /app/package.json
RUN npm install --production
COPY ./src/projecta/ClientApp /app
RUN npm run build

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY ["src/projecta/projecta.csproj", "src/projecta/"]
RUN dotnet restore "src/projecta/projecta.csproj"
COPY ./src ./src
WORKDIR /src/src/projecta
RUN dotnet build "projecta.csproj" -c Release -o /app

FROM build AS publish
RUN dotnet publish "projecta.csproj" -c Release -o /app

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
COPY --from=nodeBuild /app/build/. ./ClientApp/build/
ENTRYPOINT ["dotnet", "projecta.dll"]

移除.csproj工程文件的<Target Name="PublishRunWebpack"节点

最后别忘了加上.dockerignore文件:

.dockerignore
**/Dockerfile
**/obj
**/bin
**/*.md
.vs/
.vscode/
.git/

进一步优化

在构建后端时有如下步骤:

COPY ./src ./src

这步也会把前端的ClientApp也加入进去,导致如果只是修改前端的code,也会让后端的这段重新build,这里是可以再继续优化的地方,未来再进行

后续

有尝试做进一步优化,希望能够缓存node_modules的内容,让每次构建的时候不用重复下载。

首先,在构建的时候为nodeBuild stage创建一个tag:

docker build --target nodeBuild -t projecta-build:latest .

然后在构建脚本中,每次构建完成,都将node_modules从build镜像中复制出来,以供下次build使用

CID=$(docker create projecta-build:latest)
docker cp ${CID}:/app/node_modules/. node_modules/
docker rm ${CID}

在dockerfile中添加:

COPY node_modules/. /app/node_modules/

参考

⚠️ **GitHub.com Fallback** ⚠️