关键在于镜像选型、多阶段构建、端口与环境配置:生产用aspnet/runtime镜像而非sdk;Dockerfile分build/final两阶段,COPY发布产物并EXPOSE 80;启动时设ASPNETCORE_URLS=http://*:80且-p映射端口,避免localhost绑定和镜像体积过大。

在 Docker 中运行基于 C# 的 .NET 高性能后端服务,关键不是“能不能跑”,而是“启动快、体积小、监听对、不报错”。核心落在镜像选型、多阶段构建、端口与环境配置这三件事上。用对方法,一个 Web API 服务从构建到可访问,2 分钟内就能完成。
选对基础镜像:别让 SDK 镜像进生产
生产环境必须用 runtime 或 aspnet 镜像,而非 sdk 镜像:
-
mcr.microsoft.com/dotnet/aspnet:8.0:适用于 ASP.NET Core Web API、MVC 等含 HTTP 托管的项目(已内置 Kestrel 和依赖) -
mcr.microsoft.com/dotnet/runtime:8.0:适用于控制台、Worker Service、gRPC Server 等轻量后台服务 - 绝对避免
FROM mcr.microsoft.com/dotnet/sdk:8.0作为最终运行镜像——它带编译器、体积超 700MB、有安全风险
写好 Dockerfile:多阶段构建是默认动作
一个高性能服务的 Dockerfile 应该分三段:构建 → 发布 → 运行。这样能利用层缓存、减小最终镜像体积(通常压到 150MB 左右):
- 第一阶段(build)用 sdk 镜像:
COPY *.csproj . && dotnet restore,再COPY . . && dotnet publish -c Release -o /app/publish - 第二阶段(final)用 aspnet 镜像:
COPY --from=build /app/publish .,WORKDIR /app - 必须设
EXPOSE 80,并确保启动时监听通配地址:ASPNETCORE_URLS=http://*:80(环境变量方式最稳妥) - 启动命令用
ENTRYPOINT ["dotnet", "YourApp.dll"],不用 CMD,防止被 run 命令覆盖
启动容器时:端口映射和监听地址要配对
容器内监听 http://*:80 ≠ 宿主机能通过 localhost:5000 访问。必须同步做两件事:
- 运行命令加
-p 5000:80(左边宿主机端口,右边容器内端口,即 EXPOSE 的值) - 确认应用没卡在 localhost 绑定:检查
Program.cs是否调用了.UseUrls("http://*:80"),或更推荐直接设环境变量-e ASPNETCORE_URLS=http://*:80 - Linux 服务器需确认防火墙放行目标端口;Windows + Docker Desktop(WSL2 后端)下 localhost 默认可达
排查常见失败:先看日志,再查端口
容器启动后立刻退出?访问返回 404 或连接被拒绝?90% 是配置问题,按顺序查:
- 执行
docker logs <container-id></container-id>,重点找FileNotFoundException(DLL 没拷全)、InvalidOperationException(配置缺失)、或 Kestrel 启动失败提示 - 确认
dotnet publish输出路径与COPY指令完全一致(比如 publish 到./publish,Dockerfile 就得COPY ./publish /app/) - 用
docker exec -it <container-id> ls /app</container-id>进容器看文件是否真在、DLL 名称是否拼写正确(区分大小写)



















