You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 15:12:10