最稳定可控的方式是 dotnet publish + 多阶段 dockerfile:使用 sdk 镜像构建、runtime 镜像运行,严格对齐 .net 主版本,设 .dockerignore,entrypoint 指向 publish 输出的 .dll,并用 -c release 发布。

直接用 dotnet publish + 多阶段 Dockerfile 构建镜像,是最稳定、最可控的方式。VS 自带的“启用 Docker 支持”能省事,但生成的 Dockerfile 常默认用 Debug 配置、没设 .dockerignore、端口硬编码,上线前必须手动改。
ENTRYPOINT 必须指向 publish 后的 .dll 文件
写成 ENTRYPOINT ["dotnet", "MyApp.csproj"] 会直接启动失败——容器里没有 SDK,.csproj 不是可执行体。真正该用的是发布目录里的 .dll,它才是 .NET 运行时能加载的入口。
- 确保
dotnet publish -c Release -o ./publish执行成功,输出目录中存在MyApp.dll(文件名与项目名一致,除非显式改过AssemblyName) -
Dockerfile中最后阶段的COPY --from=build-env /app/publish .后,ENTRYPOINT ["dotnet", "MyApp.dll"]才有效 - 如果应用监听非默认端口(比如
http://+:5000),加一行ENV ASPNETCORE_URLS=http://+:5000,别在代码里硬写UseUrls
SDK 和 Runtime 镜像版本必须严格对齐
常见错误是 FROM mcr.microsoft.com/dotnet/sdk:8.0 搭配 FROM mcr.microsoft.com/dotnet/aspnet:7.0,结果容器一启动就报 Could not load file or assembly 或 JSON 序列化异常——.NET 主版本不一致会导致运行时行为差异,不是“差不多能跑”。
- 查
.csproj里的<targetframework>net8.0</targetframework>,所有镜像标签都按这个来 - ASP.NET Core Web API 必须用
aspnet:8.0镜像(不是runtime:8.0),它内置了 HTTPS 支持和 Kestrel 优化 - 永远不要用
latest标签,它可能跨主版本,破坏构建确定性
构建前必须加 .dockerignore,否则镜像臃肿还带密钥
不设 .dockerignore,docker build 会把整个项目目录(含 bin/、obj/、.git/、.user)打包进构建上下文,上传慢、层大、还可能泄露本地 NuGet 缓存路径或用户密钥。
- 在项目根目录建
.dockerignore,内容至少包括:.git、bin/、obj/、.vs/、.user、.env - 别指望用
RUN rm -rf /app/obj补救——这些文件已存在于上层镜像层,rm不会减小最终镜像体积 - 检查构建日志里有没有
Sending build context to Docker daemon后面的大小,超过 100MB 就大概率漏了 ignore
容器内路径和端口配置容易踩坑
开发时写的 "Uploads/image.jpg" 在容器里会变成 /Uploads/image.jpg,找不到;本地 localhost:5000 能访问,容器里必须绑定 0.0.0.0:5000 才能被宿主机映射到。
- 所有文件路径改用
Path.Combine(Directory.GetCurrentDirectory(), "Uploads", "image.jpg"),避免相对路径失效 - 端口暴露靠
EXPOSE 5000只是文档作用,真正生效的是docker run -p 8080:5000的映射参数 - HTTPS 开发阶段建议关掉:删掉
app.UseHttpsRedirection(),注释Kestrel中的证书配置,等上生产再配反向代理或正式证书
最常被忽略的是 dotnet publish 的 -c Release 参数——漏掉它,镜像里就会塞满调试符号、未裁剪依赖,体积翻倍且存在安全风险。还有就是 .dockerignore,很多人直到镜像推到私仓才发现里面混着 obj/ 下的临时文件和本地密钥。











