当前位置: 首页 > 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.jsqmd.com/news/1106573/

相关文章:

  • Java毕业设计-基于 SpringBoot 的宠物医院医疗设备与疫苗管理系统的设计与实现 基于 SpringBoot 的宠物医院综合管理系统(源码+LW+部署文档+全bao+远程调试+代码讲解等)
  • 基于ICM-42605和PIC18F87J11的6DOF运动追踪系统设计
  • 用 OpenCode 理解陌生代码库:3 个实用命令让你快速上手任何项目
  • Selenium反检测实战:让Chrome与Firefox浏览器绕过网站自动化识别
  • 数据中台建设中“平台优先“vs“治理优先“的技术路线之争
  • 嵌入式系统设计师重要100条知识点速记
  • 半导体硅片制造|纯技术专家线晋升 CTO 完整路径 薪资 关键领域
  • 如何完全掌握Cursor Pro破解工具:终极免费使用AI编程助手指南
  • 认知降维打击:为什么有钱的交易员反而更爱自营交易平台?
  • 浏览器指纹一致性怎么检查?IP、时区、语言和 Session 要分层看
  • 靠谱的基因检测企业有哪些
  • Codex 额度总是不够用?先判断是任务问题,还是套餐问题
  • 下载 | Windows Server 2022官方原版ISO映像!(6月更新、标准版、数据中心版、20348.5256)
  • 如何查询海外服饰达人营销带货数据?海外探款实操攻略
  • 新手入门:基于SRC平台的Web漏洞挖掘实战指南
  • 不能加事务的场景
  • OpenCV案例——光流估计
  • 从 Flash Attention 到 Speculative Decoding:大模型推理加速最全解读
  • AI FDE与AI产品经理:AI职场两大核心成长赛道
  • 2026年企业License许可优化指南:如何高效管理软件授权成本
  • Anthropic三款新品上线,除了性价比还能给资本市场讲什么新故事?
  • 2款老人最值得安装的软件工具APP,免费又实用!
  • AgentBrowser获取最上层元素
  • 基于STM32单片机汽车疲劳驾驶检测设计车载酒精 醉驾酒驾监测成品2(设计源文件+万字报告+讲解)(支持资料、图片参考_降重降ai)
  • 零壹教育:语义距离在大模型事实一致性校验中的应用
  • 软件定义汽车时代:从“年”到“周”,研发团队如何高效驾驭复杂度?
  • PHP安全漏洞报错深度解析:从错误处理到主动防御实战指南
  • AI工程实践:从问题定义到baseline模型的落地链路
  • 2026企业网盘安全合规选型指南:避开数据处罚大坑,主流产品深度测评
  • 物流机器人效率优化:4 个核心方向与落地方法