Docker中.NET API调用Python脚本遇依赖缺失及进程启动错误
问题描述
项目文件夹结构
. └── root/ ├── ExRate_Service/ │ ├── MyOwnPythonScripts... │ └── Program.py ├── ExRate_API/ │ ├── ExRate_API/ │ │ ├── Classes... │ │ ├── ExRate_API.csproj │ │ └── Program.cs │ ├── ExRate_API.Tests/ │ │ └── Tests... │ └── ExRate_API.sln └── Dockerfile
Dockerfile内容
FROM python:3.9.10-slim-buster AS build WORKDIR /app RUN pip install numpy pandas requests python-dotenv tensorflow scikit-learn keras matplotlib COPY ./ExRate_Service . FROM mcr.microsoft.com/dotnet/sdk:6.0 AS package WORKDIR /app COPY ExRate_API . RUN dotnet publish -c Release -o out FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS final WORKDIR /app COPY --from=build /app ExRate_Service COPY --from=build /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages COPY --from=package /app/out ExRate_API ENTRYPOINT ["dotnet", "ExRate_API/ExRate_API.dll"] EXPOSE 80
API调用Python脚本的C#代码
public string getOutputInContainer(string targetCurrency, string baseCurrency) { _logger.LogInformation($"Container method running"); var scriptPath = "/app/ExRate_Service/Program.py"; var output = string.Empty; var process = new Process { StartInfo = new ProcessStartInfo { FileName = "/usr/local/lib/python3.9", Arguments = scriptPath + $" -b {targetCurrency} -t {baseCurrency}", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true }, EnableRaisingEvents = true }; process.ErrorDataReceived += (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) { _logger.LogInformation($"Error from process: {e.Data}"); } }; process.OutputDataReceived += (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) { _logger.LogInformation($"Output from process: {e.Data}"); output += e.Data; } }; process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); process.WaitForExit(); _logger.LogInformation($"Raw output: {output}"); var historicalData = JsonConvert.DeserializeObject<Dictionary<string, object>>(output.Substring(0, output.IndexOf("}") + 1)); var forecast = JsonConvert.DeserializeObject<Dictionary<string, object>>(output.Substring(output.IndexOf("}") + 1)); _logger.LogInformation($"Forecast: {JsonConvert.SerializeObject(forecast)}"); var result = new Dictionary<string, Dictionary<string, object>>(); result.Add("historicalData", historicalData ?? new Dictionary<string, object>()); result.Add("forecast", forecast ?? new Dictionary<string, object>()); _logger.LogInformation($"Result: {JsonConvert.SerializeObject(result)}"); var json = JsonConvert.SerializeObject(result, Formatting.Indented); _logger.LogInformation($"Final JSON: {json}"); return json; }
运行容器时的错误日志
info: Microsoft.Hosting.Lifetime[14] Now listening on: http://[::]:80 info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down. info: Microsoft.Hosting.Lifetime[0] Hosting environment: Production info: Microsoft.Hosting.Lifetime[0] Content root path: /app/ info: Microsoft.AspNetCore.Hosting.Diagnostics[1] Request starting HTTP/1.1 GET http://localhost:8080/api/GetExRateForecast/USD&EUR - - warn: Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware[3] Failed to determine the https port for redirect. info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0] Executing endpoint 'ExRate_API.Controllers.GetExRateForecastController.Get (ExRate_API)' info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[3] Route matched with {action = "Get", controller = "GetExRateForecast"}. Executing controller action with signature Microsoft.AspNetCore.Mvc.IActionResult Get(System.String, System.String) on controller ExRate_API.Controllers.GetExRateForecastController (ExRate_API). info: ExRate_API.Controllers.GetExRateForecastController[0] Request received for baseCurrency: USD, targetCurrency: EUR info: ExRate_API.Controllers.GetExRateForecastController[0] Container method running info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2] Executed action ExRate_API.Controllers.GetExRateForecastController.Get (ExRate_API) in 35.3081ms info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1] Executed endpoint 'ExRate_API.Controllers.GetExRateForecastController.Get (ExRate_API)' fail: Microsoft.AspNetCore.Server.Kestrel[13] Connection id "0HMOTGC8R42M3", Request id "0HMOTGC8R42M3:00000002": An unhandled exception was thrown by the application. System.ComponentModel.Win32Exception (0x80004005): The FileName property should not be a directory unless UseShellExecute is set. at System.Diagnostics.Process.StartCore(ProcessStartInfo startInfo) at System.Diagnostics.Process.Start() at ExRate_API.DataFromService.GetExRateForecast.getOutputInContainer(String targetCurrency, String baseCurrency) in /app/ExRate_API/DataFromService/GetExRateForecast.cs:line 97 at ExRate_API.Controllers.GetExRateForecastController.Get(String baseCurrency, String targetCurrency) in /app/ExRate_API/Controllers/GetExRateForecastController.cs:line 30 at lambda_method2(Closure , Object , Object[] ) at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync() at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker) at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application) info: Microsoft.AspNetCore.Hosting.Diagnostics[2] Request finished HTTP/1.1 GET http://localhost:8080/api/GetExRateForecast/USD&EUR - - - 500 0 - 155.0379ms
用户疑问
进入容器bash控制台后,能看到python3及所有依赖均已安装,但脚本无法找到numpy(假设最先失败的是它)或其他依赖,恳请提供帮助或指导。
解决方案
1. 修复Python可执行文件路径错误
错误日志明确提示:The FileName property should not be a directory unless UseShellExecute is set,这是因为你在ProcessStartInfo里把FileName设成了Python的目录/usr/local/lib/python3.9,而非可执行文件路径。
正确的Python可执行文件路径应为/usr/local/bin/python3或/usr/local/bin/python,修改C#代码中的FileName:
FileName = "/usr/local/bin/python3",
2. 确保Python依赖路径正确
虽然复制了site-packages目录,但可能存在路径权限或Python环境变量问题。可在Dockerfile的final阶段添加环境变量,明确Python模块路径:
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS final WORKDIR /app ENV PYTHONPATH="/usr/local/lib/python3.9/site-packages" COPY --from=build /app ExRate_Service COPY --from=build /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages COPY --from=package /app/out ExRate_API ENTRYPOINT ["dotnet", "ExRate_API/ExRate_API.dll"] EXPOSE 80
3. 验证Python环境是否正常
可在Dockerfile的final阶段添加验证命令,确保Python和依赖能正常加载:
RUN python3 -c "import numpy; print('Numpy imported successfully')"
如果构建时出错,说明依赖复制过程有问题,需检查site-packages的复制是否完整。
4. 优化Docker镜像构建(可选)
为避免手动复制site-packages可能带来的问题,可在多阶段构建时直接复制Python环境的完整目录,或使用pip install到指定目录再复制:
FROM python:3.9.10-slim-buster AS build WORKDIR /app RUN pip install --target=/python-packages numpy pandas requests python-dotenv tensorflow scikit-learn keras matplotlib COPY ./ExRate_Service . # ... 其他阶段 ... FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS final WORKDIR /app ENV PYTHONPATH="/python-packages" COPY --from=build /python-packages /python-packages COPY --from=build /app ExRate_Service # ... 其他复制步骤 ...
内容的提问来源于stack exchange,提问作者David Beesley
相关产品推荐
相关产品推荐

