もろもろ

もろもろ書いていきます。気軽にコメントください^_^

ASP.NET Core 3.1 + React.js + Docker で npm install や npm start が失敗する件

↓のページを参考に ASP.NET Core 3.1 + React.js + Docker を試していますが、Debug 構成での実行は (何故か) 出来るものの、Dockerfile のビルドを直接行ったり Release 構成での実行や発行を行うと失敗してしまいました。

docs.microsoft.com

原因は2つあって、一つは .NET Core SDK の Docker イメージに node.js が含まれていないことだと思われます。

1>Microsoft (R) Build Engine version 16.4.0+e901037fe for .NET Core
1>
1>Copyright (C) Microsoft Corporation. All rights reserved.
1>  Restore completed in 48.96 ms for /src/WebApplication1/WebApplication1.csproj.
1>  WebApplication1 -> /src/WebApplication1/bin/Release/netcoreapp3.1/WebApplication1.dll
1>  WebApplication1 -> /src/WebApplication1/bin/Release/netcoreapp3.1/WebApplication1.Views.dll
1>  /bin/sh: 2: /tmp/tmpa4ca4172ac674b57a69cab6fbb18beea.exec.cmd: npm: not found
1>/src/WebApplication1/WebApplication1.csproj(38,5): error MSB3073: The command "npm install" exited with code 127.
1>Removing intermediate container 965d653d9ca2
1>The command '/bin/sh -c dotnet publish "WebApplication1.csproj" -c Release -o /app/publish' returned a non-zero code: 1
1>D:\source\repos\WebApplication1\WebApplication1\dockerfile : error CTC1014: Docker コマンドが終了コード 1 で失敗しました。
1>D:\source\repos\WebApplication1\WebApplication1\dockerfile : error CTC1014: The command '/bin/sh -c dotnet publish "WebApplication1.csproj" -c Release -o /app/publish' returned a non-zero code: 1
1>プロジェクト "WebApplication1.csproj" のビルドが終了しました -- 失敗。
========== すべてリビルド: 0 正常終了、1 失敗、0 スキップ ==========

なので "base" ステージだけでなく "build" ステージでも node.js をインストールすることで解決します。
(関係無いですが、ついでに Node.js のバージョンは 12.x 系に上げてます。)

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
RUN curl -sL https://deb.nodesource.com/setup_12.x |  bash -
RUN apt-get install -y nodejs

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
RUN curl -sL https://deb.nodesource.com/setup_12.x |  bash -
RUN apt-get install -y nodejs
COPY ["WebApplication1/WebApplication1.csproj", "WebApplication1/"]
RUN dotnet restore "WebApplication1/WebApplication1.csproj"
COPY . .
WORKDIR "/src/WebApplication1"
RUN dotnet build "WebApplication1.csproj" -c Release -o /app/build

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

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

もう一つは、React の開発用サーバー起動時に Node.js が package.json を見つけられないことです。

      An unhandled exception has occurred while executing the request.
System.AggregateException: One or more errors occurred. (One or more errors occurred. (The NPM script 'start' exited without indicating that the create-react-app server was listening for requests. The error output was: npm ERR! code ENOENT

npm ERR! syscall open
npm ERR! path /app/ClientApp/package.json
npm ERR! errno -2
npm ERR! enoent ENOENT: no such file or directory, open '/app/ClientApp/package.json'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent 


npm ERR! A complete log of this run can be found in:

))
 ---> System.AggregateException: One or more errors occurred. (The NPM script 'start' exited without indicating that the create-react-app server was listening for requests. The error output was: npm ERR! code ENOENT

npm ERR! syscall open
npm ERR! path /app/ClientApp/package.json
npm ERR! errno -2
npm ERR! enoent ENOENT: no such file or directory, open '/app/ClientApp/package.json'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent 


npm ERR! A complete log of this run can be found in:

)
 ---> System.InvalidOperationException: The NPM script 'start' exited without indicating that the create-react-app server was listening for requests. The error output was: npm ERR! code ENOENT

npm ERR! syscall open
npm ERR! path /app/ClientApp/package.json
npm ERR! errno -2
npm ERR! enoent ENOENT: no such file or directory, open '/app/ClientApp/package.json'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent 


npm ERR! A complete log of this run can be found in:


 ---> System.IO.EndOfStreamException: Attempted to read past the end of the stream.
   at Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer.ReactDevelopmentServerMiddleware.StartCreateReactAppServerAsync(String sourcePath, String npmScriptName, ILogger logger)
   --- End of inner exception stack trace ---
   at Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer.ReactDevelopmentServerMiddleware.StartCreateReactAppServerAsync(String sourcePath, String npmScriptName, ILogger logger)
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at System.Threading.Tasks.Task`1.get_Result()
   at Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer.ReactDevelopmentServerMiddleware.<>c.<Attach>b__2_0(Task`1 task)
   at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
   at System.Threading.Tasks.Task.<>c.<.cctor>b__274_0(Object obj)
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at System.Threading.Tasks.Task`1.get_Result()
   at Microsoft.AspNetCore.SpaServices.Extensions.Util.TaskTimeoutExtensions.WithTimeout[T](Task`1 task, TimeSpan timeoutDelay, String message)
   at Microsoft.AspNetCore.SpaServices.Extensions.Proxy.SpaProxy.PerformProxyRequest(HttpContext context, HttpClient httpClient, Task`1 baseUriTask, CancellationToken applicationStoppingToken, Boolean proxy404s)
   at Microsoft.AspNetCore.Builder.SpaProxyingExtensions.<>c__DisplayClass2_0.<<UseProxyToSpaDevelopmentServer>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

こちらは今のところ直接的な解決方法はわかっていませんが、React の開発用サーバーを使わないよう Startup.cs を変更することで回避は可能なようです。

app.UseSpa(spa =>
{
    spa.Options.SourcePath = "ClientApp";
    //if (env.IsDevelopment())
    //{
    //    spa.UseReactDevelopmentServer(npmScript: "start");
    //}
});