当前位置: 首页 > news >正文

【实战分享】.NET 10 + ABP WebAPI 项目发布部署至 Docker Desktop 避坑与实践记录

前言

最近在进行一个基于.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 中直接使用“一键全拷贝”策略,完整还原本地目录结构:

    codeDockerfile

    WORKDIR /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.,并且在国内直连拉取海外镜像极慢。

  • 原因分析

    1. 最初在编写 Dockerfile 时,误用了 .NET 9.0 的编译镜像(sdk:9.0)。当 9.0 的工具链去尝试编译目标框架为 net10.0 的项目时,就会触发版本不支持的报错。

    2. 微软官方的容器镜像中心(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)。
    由于容器内没有进程在监听 8080(Kestrel 实际跑在容器内的 15888),导致端口通道落空。

  • 解决方案
    不需要重新打包镜像,只需在运行容器时,将端口映射调整为对齐容器内部真实的 15888 端口:

    codeBash

    docker 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_CONFIGURATION=Release 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_CONFIGURATION=Release RUN dotnet publish "src/GZ.WebApi.Host/GZ.WebApi.Host.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false # 7. 组装最终轻量运行镜像 FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "GZ.WebApi.Host.dll"]

三、 终端实战编译与启动命令 🌟

在准备好 Dockerfile 后,我们在终端中执行以下步骤来进行物理编译与启动运行:

1. 打开终端并进入解决方案根目录

在 Visual Studio 中右键点击主项目 GZ.WebApi.Host -> 选择“在终端中打开”
由于多项目依赖关系,我们必须退回到**解决方案根目录(包含 .sln 文件的那一层,即 src 文件夹的外层)**下执行编译。在终端中输入命令回退两级:

codeBash

cd ../..

2. 执行编译命令(强行禁用缓存构建)

在终端中执行以下命令进行物理打包。由于之前可能存在历史缓存干扰,建议加入 --no-cache 标志确保彻底应用全新的 COPY . . 配置:

codeBash

docker 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 生成镜像后,在终端执行以下命令,将镜像导出为一个普通的压缩包文件:

codeBash

docker save -o gz-api.tar gz-api

2. 上传并导入服务器

使用文件传输工具(如 MobaXterm)将 gz-api.tar 拷贝到服务器的任意目录下(如 /root/app/),并在服务器终端执行导入命令:

codeBash

docker load -i gz-api.tar

3. 服务器一键启动(挂载物理配置文件)

为了解决开发环境与服务器数据库连接 IP 不同的问题,我们直接在服务器的 /root/app/ 目录下放置一个服务器专属的 appsettings.json(里面配置服务器真实的 MySQL IP),然后通过 -v 参数强行挂载替换启动,无需重新打包:

codeBash

docker run -d \ -p 15888:15888 \ -v /root/app/appsettings.json:/app/appsettings.json \ --name gz-api-service \ gz-api

五、 总结

将 .NET 10 + ABP 复杂的多项目微服务框架发布部署到 Docker 时,看似步骤简单,但实操中对于像中央包管理(CPM)中国区镜像源替换端口对齐以及多项目依赖搬运这样的细节处理至关重要。

通过这次实践,我们完成了对老项目架构的高吞吐容器化升级。希望这篇简洁的发布避坑记录能帮到有需要的朋友!

http://www.gsyq.cn/news/1618545.html

相关文章:

  • Java毕业设计-基于 SpringBoot 的宠物医院医疗设备与疫苗管理系统的设计与实现 基于 SpringBoot 的宠物医院综合管理系统(源码+LW+部署文档+全bao+远程调试+代码讲解等)
  • 半导体硅片制造|纯技术专家线晋升 CTO 完整路径 薪资 关键领域
  • 数据中台建设中“平台优先“vs“治理优先“的技术路线之争
  • 如何完全掌握Cursor Pro破解工具:终极免费使用AI编程助手指南
  • 下载 | Windows Server 2022官方原版ISO映像!(6月更新、标准版、数据中心版、20348.5256)
  • AI工程实践:从问题定义到baseline模型的落地链路
  • 2026中考英语词汇用什么 App 复习?重点看课标词汇、错词巩固和复习反馈
  • vllm与sgLang
  • 机器人即服务(RaaS)时代来了:机器人租赁平台的技术架构与落地实践
  • 90%的iPhone用户都踩过的坑:弹窗、发烫、掉电池,根源全在这
  • unordered_map 与 unordered_set 使用技巧(C++哈希容器高性能实战全解)
  • 2026年门店小程序平台怎么选?预约、核销和会员储值能力对比
  • 景观设计师转型AI:2个月掌握大模型的实战路径
  • STM32与AD74413R构建高精度数据采集系统
  • 把AI流式响应当成编译问题:用状态机消灭200空白
  • 从成本中心到价值引擎:License许可优化的进阶之路
  • 【硬核详解】基于 CH340G 的 STM32 一键下载电路设计:从数据手册到参数计算全流程指南(一)
  • 2026年腾讯云秒杀活动抢购攻略
  • 西城微科SIC8833高精度胎压计芯片方案
  • AS717芯片规格 8K@60Hz外围少 AS717电路图参考
  • 鸿蒙原生 ArkTS 布局容器切换:Column ↔ Row 的响应式转换深度实践
  • 安卓手机远程控制另一部手机 怎么远程控制安卓手机
  • 2026年企业办公与数字员工工具实测:五个平台的搭建路径与适用场景
  • 2026年中药洗发水代工:非遗传承工厂如何重塑品质标准
  • 易元 AI 深度解析:一站式 AI 电商素材与内容生产全链路工具,赋能信息流广告批量制作
  • AI模型门控发布机制与安全治理实践
  • 【AI全栈】日常内存管理 SOP-Windows + WSL2 + Docker Desktop 组合中最经典的“内存黑洞”问题!
  • windows远程桌面下载教程 如何远程控制win桌面
  • 实操笔记:vscode+opencode+deepseek
  • 什么企业需要上线机房磁控U位管理?