Dockerize ASP.NET Core Angular application (Solving error: The command “npm install” exited with code 127.)

When you try do dockerize ASP.NET Core application with Angular, you most probably faced with this error.

The command “npm install” exited with code 127.

So, why it happens ?

Because of publish  stage happens on based microsoft/dotnet:2.1-sdk image. So, this dockerfile contains only dotnet sdk but not npm installed this base image.

 

What is the solution ?

We should use other image in our dockerfile too. In order to be able build our frontend.

I seperated npm side as different build stage on dockerfile. So, firstly you should remove npm commands on publish in *.csproj file. So, i removed all red lines entirely  from my *.csproj file.

 

<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
 <TargetFramework>netcoreapp2.1</TargetFramework>
 <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
 <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
 <IsPackable>false</IsPackable>
 <SpaRoot>ClientApp\</SpaRoot>
 <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>

<!-- Set this to true if you enable server-side prerendering -->
 <BuildServerSideRenderer>false</BuildServerSideRenderer>
 <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
 <UserSecretsId>780680aa-e49b-4ae0-b3bc-868cb1e482aa</UserSecretsId>
 </PropertyGroup>

<ItemGroup>
 <PackageReference Include="Microsoft.AspNetCore.App" />
 <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.1.2" PrivateAssets="All" />
 <PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="2.1.1" />
 <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.0.2105168" />
 </ItemGroup>

<ItemGroup>
 <!-- Don't publish the SPA source files, but do show them in the project files list -->
 <Content Remove="$(SpaRoot)**" />
 <None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
 </ItemGroup>

<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
 <!-- Ensure Node.js is installed -->
 <Exec Command="node --version" ContinueOnError="true">
 <Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
 </Exec>
 <Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
 <Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
 <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
 </Target>

<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 -- --prod" />
 <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build:ssr -- --prod" Condition=" '$(BuildServerSideRenderer)' == 'true' " />

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

</Project>

 

So, try to build docker file again, it should be build without any error.

docker build -t angularapp2 .

Yes, it was successfull. But on this way we builded dockerfile only with backend but not with frontend side. We should build and publish our frontend in dockerfile. So, my Dockerfile looks like this:

FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY ["angularApp2.csproj", "angularApp2/"]
RUN dotnet restore "angularApp2/angularApp2.csproj"
COPY . angularApp2/
WORKDIR "/src/angularApp2"
RUN dotnet build "angularApp2.csproj" -c Release -o /app

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

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

 

I added frontend build stage to this file, after changes my code will look like below:

FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY ["angularApp2.csproj", "angularApp2/"]
RUN dotnet restore "angularApp2/angularApp2.csproj"
COPY . angularApp2/
WORKDIR "/src/angularApp2"
RUN dotnet build "angularApp2.csproj" -c Release -o /app

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

#Angular build
FROM node as nodebuilder

# set working directory
RUN mkdir /usr/src/app
WORKDIR /usr/src/app

# add `/usr/src/app/node_modules/.bin` to $PATH
ENV PATH /usr/src/app/node_modules/.bin:$PATH


# install and cache app dependencies
COPY ClientApp/package.json /usr/src/app/package.json
RUN npm install
RUN npm install -g @angular/cli@1.7.0 --unsafe

# add app

COPY ClientApp/. /usr/src/app

RUN npm run build

#End Angular build

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
RUN mkdir -p /app/ClientApp/dist
COPY --from=nodebuilder /usr/src/app/dist/. /app/ClientApp/dist/
ENTRYPOINT ["dotnet", "angularApp2.dll"]

 

And build you docker image again and run it.

docker build -t angularapp2 .
docker run -p 80:80 -d angularapp2

 

yaay it works 😊

 

 

Join the Conversation

5 Comments

  1. For some reason the `ng build` creates a folder named `ClientApp` in the dist folder.

    So my last copy command looks like:
    “`
    COPY –from=nodebuilder /usr/src/app/dist/ClientApp/. /app/ClientApp/dist/
    “`

Leave a comment

Your email address will not be published.