
前言最近在进行一个基于.NET 10 ABP 框架的项目重构与容器化部署工作。在将本地运行良好的项目打包发布到Docker Desktop的过程中由于技术版本较新.NET 10以及多项目依赖架构下的一些隐蔽设计踩了几处关于“NuGet中央包管理”、“网络自动加速”以及“容器端口映射”的坑。特此整理成文希望能为同样在做 .NET 10 容器化发布和 ABP 部署的同行们提供一些实战参考。项目环境开发环境Visual Studio 2026 Windows 11目标框架.NET 10.0 (net10.0)数据库MySQL 8.0 Redis部署目标Docker Desktop for Windows (WSL2)脱敏项目命名GZ.WebApi包含 GZ.WebApi.Host、GZ.WebApi.Application 等 6 个子项目一、 Docker 发布踩坑与解决方案坑一中央包管理CPM导致 Docker 还原Restore大面积报错现象在编写多阶段构建的 Dockerfile 时为了优化构建缓存我们通常会先把各个子项目的 .csproj 文件单独 COPY 进容器进行 restore。但在执行 RUN dotnet restore 时系统抛出大面积的error NU1015: The following PackageReference item(s) do not have a version specified...错误。原因分析新版项目模板中默认启用了中央包管理Central Package Management, CPM机制。所有的 NuGet 包版本并不是写在各自的 .csproj 里而是统一托管在解决方案根目录下的Directory.Packages.props文件中。由于我们单独拷贝 .csproj 时漏掉了这个属性文件导致容器内的 NuGet 编译器找不到任何包的版本信息直接报错。解决方案对于多项目且启用了 CPM 的方案最保险、最不易出错的方式是在 Dockerfile 中直接使用“一键全拷贝”策略完整还原本地目录结构codeDockerfileWORKDIR /src # 直接全拷贝确保 Directory.Packages.props 一并带入容器 COPY . . RUN dotnet restore src/GZ.WebApi.Host/GZ.WebApi.Host.csproj坑二.NET SDK 编译版本与目标框架冲突NETSDK1045现象在执行 Docker 编译时报错error NETSDK1045: The current .NET SDK does not support targeting .NET 10.0.并且在国内直连拉取海外镜像极慢。原因分析最初在编写 Dockerfile 时误用了 .NET 9.0 的编译镜像sdk:9.0。当 9.0 的工具链去尝试编译目标框架为 net10.0 的项目时就会触发版本不支持的报错。微软官方的容器镜像中心mcr.microsoft.com物理服务器全部位于海外国内直连拉取 1GB 左右的 SDK 镜像极易发生网络超时和断流。解决方案将 Dockerfile 里的基础镜像和编译镜像版本统一提升至 .NET 10.0并直接替换为微软官方为中国区开发者提供的Azure 中国区官方托管高速源 mcr.azure.cn不仅解决了版本问题下载速度也瞬间拉满codeDockerfile# 替换为微软中国官方高速源并对齐 .NET 10.0 FROM mcr.azure.cn/dotnet/aspnet:10.0 AS base ... FROM mcr.azure.cn/dotnet/sdk:10.0 AS build坑三端口映射不一致导致容器运行但无法访问ERR_EMPTY_RESPONSE现象Docker 构建镜像成功并在 Docker Desktop 中顺利运行显示为绿色的 Running且日志里没有任何报错但浏览器访问 http://localhost:15888/swagger/index.html 时直接提示 ERR_EMPTY_RESPONSE。原因分析观察容器运行日志发现一行输出Now listening on: http://[::]:15888。原来我们在本地的 appsettings.json 中配置了 Kestrel 绑定端口为 15888。而我们在执行 docker run 时的启动命令是 -p 15888:8080将宿主机的 15888 映射到容器内的默认端口 8080。由于容器内没有进程在监听 8080Kestrel 实际跑在容器内的 15888导致端口通道落空。解决方案不需要重新打包镜像只需在运行容器时将端口映射调整为对齐容器内部真实的 15888 端口codeBashdocker run -d -p 15888:15888 --name gz-api-service gz-api二、 终极完整 Dockerfile在主项目 GZ.WebApi.Host 根目录下创建一个名为 Dockerfile无任何后缀的文件内容如下codeDockerfile# 1. 运行阶段基础镜像 (采用微软中国高速源对齐 .NET 10.0) FROM mcr.azure.cn/dotnet/aspnet:10.0 AS base WORKDIR /app EXPOSE 15888 # 2. 编译阶段 SDK 镜像 FROM mcr.azure.cn/dotnet/sdk:10.0 AS build ARG BUILD_CONFIGURATIONRelease WORKDIR /src # 3. 一键全拷贝直接复制本地所有物理文件完美还原您本地的目录结构确保中央包管理配置和子项目全部带入 COPY . . # 4. 执行依赖还原 RUN dotnet restore src/GZ.WebApi.Host/GZ.WebApi.Host.csproj # 5. 执行编译 RUN dotnet build src/GZ.WebApi.Host/GZ.WebApi.Host.csproj -c $BUILD_CONFIGURATION -o /app/build # 6. 执行发布 (自动将子项目 XML 物理文件打包输出) FROM build AS publish ARG BUILD_CONFIGURATIONRelease RUN dotnet publish src/GZ.WebApi.Host/GZ.WebApi.Host.csproj -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHostfalse # 7. 组装最终轻量运行镜像 FROM base AS final WORKDIR /app COPY --frompublish /app/publish . ENTRYPOINT [dotnet, GZ.WebApi.Host.dll]三、 终端实战编译与启动命令 在准备好 Dockerfile 后我们在终端中执行以下步骤来进行物理编译与启动运行1. 打开终端并进入解决方案根目录在 Visual Studio 中右键点击主项目 GZ.WebApi.Host - 选择“在终端中打开”。由于多项目依赖关系我们必须退回到**解决方案根目录包含 .sln 文件的那一层即 src 文件夹的外层**下执行编译。在终端中输入命令回退两级codeBashcd ../..2. 执行编译命令强行禁用缓存构建在终端中执行以下命令进行物理打包。由于之前可能存在历史缓存干扰建议加入 --no-cache 标志确保彻底应用全新的 COPY . . 配置codeBashdocker build --no-cache -t gz-api -f src/GZ.WebApi.Host/Dockerfile .3. 运行容器编译成功后我们通过以下命令在 Docker Desktop 中将容器跑起来。(注意在此之前确保您的开发机已经彻底退出了 IIS Express防止本地端口被占用)codeBash# 强制物理删除可能存在的历史旧容器名 docker rm -f gz-api-service # 启动并绑定 15888:15888 端口 docker run -d -p 15888:15888 --name gz-api-service gz-api四、 进阶部署生产/测试服务器离线发布 如果您的服务器部署在隔离的**企业局域网厂区内网**中无法直接连接外网。我们可以利用 Docker 的导出导入机制完成 100% 离线无缝平替部署1. 本地导出离线压缩包在您有网的开发机上成功执行 docker build 生成镜像后在终端执行以下命令将镜像导出为一个普通的压缩包文件codeBashdocker save -o gz-api.tar gz-api2. 上传并导入服务器使用文件传输工具如 MobaXterm将 gz-api.tar 拷贝到服务器的任意目录下如 /root/app/并在服务器终端执行导入命令codeBashdocker load -i gz-api.tar3. 服务器一键启动挂载物理配置文件为了解决开发环境与服务器数据库连接 IP 不同的问题我们直接在服务器的 /root/app/ 目录下放置一个服务器专属的 appsettings.json里面配置服务器真实的 MySQL IP然后通过 -v 参数强行挂载替换启动无需重新打包codeBashdocker run -d \ -p 15888:15888 \ -v /root/app/appsettings.json:/app/appsettings.json \ --name gz-api-service \ gz-api五、 总结将 .NET 10 ABP 复杂的多项目微服务框架发布部署到 Docker 时看似步骤简单但实操中对于像中央包管理CPM、中国区镜像源替换、端口对齐以及多项目依赖搬运这样的细节处理至关重要。通过这次实践我们完成了对老项目架构的高吞吐容器化升级。希望这篇简洁的发布避坑记录能帮到有需要的朋友