.NET 6发布模式下Docker容器自动退出问题求助
ASP.NET Core 6 MVC Docker Release模式容器自动退出问题
在我的ASP.NET Core 6 MVC项目中添加Docker支持后,调试(Debug)模式下一切运行正常,但切换为Release模式(在Visual Studio中)并选择Docker作为执行选项时,容器会自动关闭。输出窗口显示:
The program 'dotnet' has exited with code 0 (0x0)
项目代码
Program.cs
using System.Reflection; using System.Text; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.FileProviders; using Serilog; using WhoWantsToBeAMillionaireGame.Business.ServicesImplementations; using WhoWantsToBeAMillionaireGame.Core.Abstractions; using WhoWantsToBeAMillionaireGame.Data.Abstractions; using WhoWantsToBeAMillionaireGame.Data.Abstractions.Repositories; using WhoWantsToBeAMillionaireGame.Data.Repositories; using WhoWantsToBeAMillionaireGame.DataBase; using WhoWantsToBeAMillionaireGame.DataBase.Entities; namespace WhoWantsToBeAMillionaireGame; public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); var configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .Build(); Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(configuration) .CreateLogger(); // 向容器添加服务 var connectionString = builder.Configuration.GetConnectionString("Default"); builder.Services.AddDbContext<WhoWantsToBeAMillionaireGameDbContext>( optionBuilder => optionBuilder.UseSqlServer(connectionString)); builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); builder.Services.AddScoped<IUnitOfWork, UnitOfWork>(); builder.Services.AddDistributedMemoryCache(); builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(10); options.Cookie.Name = ".Game.Session"; options.Cookie.IsEssential = true; }); builder.Services.AddControllersWithViews(); builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(x => x.LoginPath = "/admin/Auth/Login"); // 添加业务服务 builder.Services.AddScoped<IQuestionService, QuestionService>(); builder.Services.AddScoped<IAnswerService, AnswerService>(); builder.Services.AddScoped<ISourceService, JsonFileSourceService>(); builder.Services.AddScoped<IGameService, GameService>(); builder.Services.AddScoped<IPrizeService, PrizeService>(); builder.Services.AddScoped<IColorPrizeService, PrizeColorService>(); builder.Services.AddScoped<ILoginUserService, LoginUserService>(); builder.Services.AddScoped<IAdvertiseService, AdvertiseService>(); builder.Services.AddScoped<IClickedAdService, ClickedAdService>(); builder.Services.AddScoped<IGameTimer, GameTimerService>(); builder.Services.AddScoped<ISocialMediaLinkService, SocialMediaLinkService>(); // 添加仓储 builder.Services.AddScoped<IRepository<Question>, Repository<Question>>(); builder.Services.AddScoped<IRepository<Answer>, Repository<Answer>>(); builder.Services.AddScoped<IRepository<Game>, Repository<Game>>(); builder.Services.AddScoped<IRepository<GameQuestion>, Repository<GameQuestion>>(); builder.Services.AddScoped<IRepository<Prize>, Repository<Prize>>(); builder.Services.AddScoped<IRepository<ColorPrize>, Repository<ColorPrize>>(); builder.Services.AddScoped<IRepository<LoginUser>, Repository<LoginUser>>(); builder.Services.AddScoped<IRepository<Advertise>, Repository<Advertise>>(); builder.Services.AddScoped<IRepository<ClickedAd>, Repository<ClickedAd>>(); builder.Services.AddScoped<IRepository<GameTimer>, Repository<GameTimer>>(); builder.Services.AddScoped<IRepository<SocialMediaLink>, Repository<SocialMediaLink>>(); Log.Information("Start !!!"); try { var app = builder.Build(); // 配置HTTP请求管道 if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider( Path.Combine(app.Environment.ContentRootPath, "uploads")), RequestPath = "/uploads" }); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseSession(); app.UseMiddleware<PageLoadMiddleware>(); app.UseEndpoints(endpoints => { endpoints.MapAreaControllerRoute( name: "AdminGame", areaName: "AdminGame", pattern: "admin/{controller=Auth}/{action=Login}/{id?}" ); endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); app.Run(); } catch (Exception ex) { StringBuilder errorMessage = new StringBuilder(); errorMessage.Append("应用启动失败。异常信息: "); errorMessage.Append(ex.Message); errorMessage.Append(" 堆栈跟踪: "); errorMessage.Append(ex.StackTrace); if (ex.InnerException != null) { errorMessage.Append(" 内部异常: "); errorMessage.Append(ex.InnerException.Message); errorMessage.Append(" 内部异常堆栈跟踪: "); errorMessage.Append(ex.InnerException.StackTrace); } Log.Fatal(errorMessage.ToString()); } finally { Log.CloseAndFlush(); } } private static string GetPathToLogFile() { var sb = new StringBuilder(); sb.Append(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); sb.Append(@"\logs\"); sb.Append($"{DateTime.Now:yyyyMMddhhmmss}"); sb.Append("data.log"); return sb.ToString(); } }
Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["WhoWantsToBeAMillionaireGame/WhoWantsToBeAMillionaireGame.csproj", "WhoWantsToBeAMillionaireGame/"] COPY ["WhoWantsToBeAMillionaireGame.Business/WhoWantsToBeAMillionaireGame.Business.csproj", "WhoWantsToBeAMillionaireGame.Business/"] COPY ["WhoWantsToBeAMillionaireGame.Core/WhoWantsToBeAMillionaireGame.Core.csproj", "WhoWantsToBeAMillionaireGame.Core/"] COPY ["WhoWantsToBeAMillionaireGame.Data.Abstractions/WhoWantsToBeAMillionaireGame.Data.Abstractions.csproj", "WhoWantsToBeAMillionaireGame.Data.Abstractions/"] COPY ["WhoWantsToBeAMillionaireGame.DataBase/WhoWantsToBeAMillionaireGame.DataBase.csproj", "WhoWantsToBeAMillionaireGame.DataBase/"] COPY ["WhoWantsToBeAMillionaireGame.Data.Repositories/WhoWantsToBeAMillionaireGame.Data.Repositories.csproj", "WhoWantsToBeAMillionaireGame.Data.Repositories/"] RUN dotnet restore "WhoWantsToBeAMillionaireGame/WhoWantsToBeAMillionaireGame.csproj" COPY . . WORKDIR "/src/WhoWantsToBeAMillionaireGame" RUN dotnet build "WhoWantsToBeAMillionaireGame.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "WhoWantsToBeAMillionaireGame.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "WhoWantsToBeAMillionaireGame.dll"]
排查与解决方法
1. 确认配置文件是否被正确复制
Release模式下,appsettings.json或appsettings.Release.json可能未被包含在发布包中:
- 进入容器查看文件是否存在:执行
docker exec -it <容器ID> ls /app - 在项目.csproj文件中添加配置,确保配置文件始终复制到输出目录:
<ItemGroup> <Content Update="appsettings.json"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> <Content Update="appsettings.Release.json"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> </ItemGroup>
2. 检查数据库连接字符串
Release模式下,容器无法访问本地数据库,需确认:
- 连接字符串是否指向容器可访问的地址(如Docker内部服务名、公网IP)
- 数据库是否允许容器所在网络的访问请求
3. 查看Serilog日志定位问题
当前日志写入容器内的/app/logs/目录,可通过以下命令查看:
docker exec -it <容器ID> cat /app/logs/<日志文件名>.log- 若日志目录未创建,程序启动会失败,可在
Program.cs中添加目录创建逻辑:var logPath = GetPathToLogFile(); var logDir = Path.GetDirectoryName(logPath); if (!Directory.Exists(logDir)) { Directory.CreateDirectory(logDir); }
4. 配置容器环境变量与启动命令
- 在Dockerfile的
final阶段添加环境变量,确保程序运行在Release环境:ENV ASPNETCORE_ENVIRONMENT=Release - 确认
ENTRYPOINT中的dll文件名与Release发布的文件名一致
5. 确保静态文件与uploads目录被复制
在项目.csproj中添加配置,保证uploads目录文件被复制到容器:
<ItemGroup> <Content Update="uploads/**/*"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> </ItemGroup>
内容的提问来源于stack exchange,提问作者FarHard112
相关产品推荐
相关产品推荐

