使用 dotnet publish 实现跨平台部署需面向 net6.0+、避免 Windows API,推荐 -r linux-x64 --self-contained false;Docker 构建应分阶段、用官方 SDK/ASP.NET 镜像、正确分层缓存,并注意 RID、权限、路径与环境一致性。

用 dotnet publish 生成跨平台输出
发布前必须确认项目是面向 net6.0 或更高版本(net7.0、net8.0),且未硬编码 Windows 特有 API。低版本 .NET Framework 或 .NET Core 2.x 无法真正跨平台运行。
-
dotnet publish -c Release -r linux-x64 --self-contained false:推荐方式,依赖系统已安装的 .NET 运行时,镜像体积小 - 避免
--self-contained true,除非你明确需要隔离运行时——它会让镜像增大 100MB+,且不同 RID(如linux-musl-x64)需单独发布 - 若用 Alpine 镜像,必须指定
-r linux-musl-x64,普通linux-x64在 musl 环境下会报System.DllNotFoundException: libhostfxr.so
Dockerfile 要按阶段分层写,别直接 COPY bin/Debug
很多人把本地 bin/Release/net8.0/publish/ 打包进 Docker,这会导致构建不可复现、缓存失效、环境不一致。正确做法是让 Docker 自己跑 dotnet publish。
- 基础镜像选
mcr.microsoft.com/dotnet/sdk:8.0(构建阶段) +mcr.microsoft.com/dotnet/aspnet:8.0(运行阶段),别用runtime-deps手动装依赖 - COPY 只传
.csproj和Directory.Packages.props(如有),先dotnet restore再 COPY 源码,利用 Docker 层缓存 - 最后
COPY --from=build /src/bin/Release/net8.0/publish/ /app/,确保路径和 publish 输出严格一致
运行时权限和路径问题常导致容器启动失败
镜像跑起来但立刻退出?大概率是工作目录、用户权限或入口点配置错。ASP.NET Core 默认监听 http://+:80,而非 root 用户无法绑定 80 端口。
- 在 Dockerfile 里加
WORKDIR /app,并确保ENTRYPOINT ["dotnet", "MyApp.dll"]中的 DLL 名与dotnet publish输出名完全一致(大小写敏感) - 非必要别用
USER 1001,.NET 官方 aspnet 镜像默认以非 root 用户运行,但会自动处理端口绑定;若自己改 USER,得配ENV ASPNETCORE_URLS=http://+:8080 - 挂载配置文件时,别依赖
appsettings.json在根目录——用--mount type=bind,source=$(pwd)/appsettings.prod.json,target=/app/appsettings.json显式覆盖
调试时别只看 docker logs -f
容器退出后日志可能为空,尤其因配置缺失或环境变量未传入导致进程秒退时。
- 临时改
ENTRYPOINT为["sh", "-c", "dotnet MyApp.dll || sh"],崩溃后留个 shell 查/app下文件是否存在、权限是否正确 - 本地验证镜像:运行
docker run --rm -it -p 8080:8080 myapp:latest后,立刻docker exec -it <id> ls -l /app确认 DLL 和依赖都在 - 若用 GitHub Actions 构建,注意
actions/checkout默认不拉 submodules,引用了 Git Submodule 的项目会 publish 失败,且错误不明显
跨平台不是“能编译就行”,RID、运行时链、glibc/musl 差异、甚至时区和字符编码都可能在某个 Linux 发行版上突然暴露——最稳妥的做法,是在目标部署环境(比如客户用的 CentOS 7 或 AWS ECS 的 Amazon Linux 2)里跑一次 docker build 验证。


















