第5章:从零搭建多服务.NET应用:亲手组装你的第一个“微服务乐高”
告别手动管理多个容器的混乱,像指挥交响乐一样,用一份配置文件一键编排、启动、管理由多个服务(你的.NET应用、数据库、缓存等)组成的完整应用栈。这是从“玩容器”到“用容器架构”的关键一步。经过前几章的修炼,你已经能把单个.NET应用熟练地装进容器。但真实的微服务世界,从来不是“单打独斗”。现在,你需要同时管理这4个容器。docker run:一份声明式的蓝图,能定义。这个总指挥,就是。你可以将文件
第5章:从零搭建多服务.NET应用:亲手组装你的第一个“微服务乐高”
本章学完你将能:告别手动管理多个容器的混乱,像指挥交响乐一样,用一份配置文件一键编排、启动、管理由多个服务(你的.NET应用、数据库、缓存等)组成的完整应用栈。这是从“玩容器”到“用容器架构”的关键一步。
一、开篇:当微服务从“一个”变成“一群”
经过前几章的修炼,你已经能把单个.NET应用熟练地装进容器。但真实的微服务世界,从来不是“单打独斗”。
设想一个最简单的电商场景,你的系统可能包含:
- 订单服务(处理下单、付款)
- 商品服务(管理商品信息)
- MySQL数据库(存储核心数据)
- Redis缓存(加速热点访问)
现在,你需要同时管理这4个容器。这意味着你要:
- 打开多个终端,分别执行复杂的
docker run命令。 - 手动创建网络,确保它们能互相“找到”对方。
- 容器挂了?手动重启。配置改了?手动更新。
我们需要一个“总指挥”:一份声明式的蓝图,能定义谁是谁、谁需要谁、以及它们如何交谈。这个总指挥,就是 Docker Compose。
二、认识总指挥:Docker Compose 核心概念
你可以将 docker-compose.yml 文件视作你的应用架构图和自动化运维手册的合体。理解这三个核心概念,就掌握了编排的精髓:
| 概念 | 通俗理解 | 在蓝图中的角色 | 关键影响 |
|---|---|---|---|
| 服务 | 一个独立的、可运行的容器实例。 | 蓝图中的一个 “功能模块”,如订单服务、MySQL。 | 每个服务对应一个 container,是编排的基本单位。 |
| 项目 | 由一组关联服务组成的完整应用。 | 整栋 “建筑”,默认以当前目录名命名(如 ecommerce-lego)。 |
执行 docker-compose 命令时,所有操作都基于整个项目。 |
| 网络 | 服务间通信的私有、安全通道。 | 建筑内部的 “专用通信线路”,与外界隔离。 | 核心特性:在此网络内,服务间可直接使用服务名称作为主机名互相访问(服务发现)。 |
一句话心法:Compose为你的所有服务创建了一个专属聊天群。在这个群聊中,大家不叫IP地址这种冷冰冰的编号,而是直接喊服务名,比如“嘿,product-service,给我商品信息!”
三、实战:亲手绘制你的第一份微服务蓝图
让我们以“电商乐高”为例,搭建包含订单服务、商品服务、MySQL和Redis的完整栈。
步骤1:规划项目结构
创建清晰的项目目录:
ecommerce-lego/ # 项目根目录
├── docker-compose.yml # 【核心】编排蓝图
├── order-service/ # 订单服务项目
│ ├── OrderService.csproj
│ ├── Dockerfile # 订单服务的“集装箱说明书”
│ ├── Program.cs
│ └── ...
├── product-service/ # 商品服务项目
│ ├── ProductService.csproj
│ ├── Dockerfile
│ └── ...
└── .env # (可选)环境变量配置文件,用于管理密码等敏感信息
行动:创建两个基础的.NET Web API项目作为我们的服务原型。
# 创建订单服务项目
dotnet new webapi -n OrderService -o order-service -f net10.0
# 创建商品服务项目
dotnet new webapi -n ProductService -o product-service -f net10.0

步骤2:编写核心蓝图 docker-compose.yml
在项目根目录创建 docker-compose.yml:
version: '3.8'
# 1. 定义所有服务(我们的乐高积木)
services:
# 积木A:MySQL数据库
mysql-db:
image: mysql:8.0
container_name: ecommerce-mysql
environment:
MYSQL_ROOT_PASSWORD: root123456
MYSQL_DATABASE: orderdb
volumes:
- mysql-data:/var/lib/mysql # 数据持久化
ports:
- "3306:3306"
networks:
- app-network
healthcheck: # 健康检查,确保数据库就绪后其他服务再启动
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
# 积木B:Redis缓存
redis-cache:
image: redis:7-alpine
container_name: ecommerce-redis
ports:
- "6379:6379"
networks:
- app-network
# 积木C:商品服务 (.NET)
product-service:
build: ./product-service # 根据目录下的Dockerfile构建镜像
container_name: product-service
environment:
- ConnectionStrings__Redis=redis-cache:6379
- ConnectionStrings__MySql=Server=mysql-db;Database=orderdb;Uid=root;Pwd=root123456;
depends_on:
mysql-db:
condition: service_healthy # 等待数据库健康
redis-cache:
condition: service_started
ports:
- "5005:8080"
networks:
- app-network
# 积木D:订单服务 (.NET)
order-service:
build: ./order-service
container_name: order-service
environment:
- ProductService__BaseUrl=http://product-service:8080 # 关键!通过服务名调用
- ConnectionStrings__MySql=Server=mysql-db;Database=orderdb;Uid=root;Pwd=root123456;
depends_on:
- product-service # 等待商品服务启动
ports:
- "5000:80"
networks:
- app-network
# 2. 定义所有服务共用的资源
networks:
app-network: # 创建一个专用网络,所有服务加入其中
driver: bridge
volumes:
mysql-data: # 声明一个数据卷,供MySQL使用
driver: local
步骤3:在.NET服务中实现服务间通信
蓝图定义了“道路”,代码实现“运输”。订单服务需要调用商品服务,关键在于使用Compose网络提供的服务名作为主机名。
改造订单服务(OrderApi):调用商品服务接口
在 order-service 中,通常通过 HttpClient 调用,我们需要:
-
进入OrderApi目录,安装HttpClient包。修改
Program.cs,配置HttpClient(用于调用商品服务)。cd ..\order-service && dotnet add package Microsoft.Extensions.Http -
修改
Program.cs,配置HttpClient(用于调用商品服务)。
-
在Controller或Service中注入IHttpClientFactory并使用

-
编写OrderApi的Dockerfile(放在OrderApi根目录):
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build WORKDIR /src COPY ["OrderService.csproj", "."] RUN dotnet restore "OrderService.csproj" COPY . . RUN dotnet publish -c Release -o /app/publish --no-self-contained FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS runtime WORKDIR /app COPY --from=build /app/publish . EXPOSE 80 ENV ASPNETCORE_URLS=http://0.0.0.0:80 ENTRYPOINT ["dotnet", "OrderService.dll"]
改造商品服务(ProductApi):提供商品查询接口
在 product-service 中,使用Redis缓存商品,我们需要:
-
修改
Program.cs,添加Redis缓存配置(先安装NuGet包):cd product-service && dotnet add package StackExchange.Redis
-
创建并实现
Controllers/ProductController.cs:

-
编写ProductApi的Dockerfile(放在ProductApi根目录):
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build WORKDIR /src COPY ["ProductService.csproj", "."] RUN dotnet restore "ProductService.csproj" COPY . . RUN dotnet publish -c Release -o /app/publish --no-self-contained FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS runtime WORKDIR /app COPY --from=build /app/publish . EXPOSE 8080 ENV ASPNETCORE_URLS=http://0.0.0.0:8080 ENTRYPOINT ["dotnet", "ProductService.dll"]
步骤4:一键启动与运维你的“乐团”
一切就绪,感受总指挥的力量:
# 启动所有服务(-d 表示后台运行)
docker-compose up -d
# 查看所有服务的运行状态和日志
docker-compose ps
docker-compose logs -f order-service # 跟踪订单服务日志

# 验证服务
curl http://localhost:5005/api/products # 测试商品服务
curl http://localhost:5000/api/orders # 测试订单服务
# 验证服务间通信
curl -X POST http://localhost:5000/api/orders -H "Content-Type: application/json" -d '{"productId": 1, "quantity": 1}'

# 一键停止并清理所有服务
docker-compose down
# 如果想同时删除数据卷(谨慎!)
# docker-compose down -v
四、进阶技巧与避坑指南
-
环境变量与配置分离:永远不要将密码等硬编码在yml文件中。可以使用
.env文件。# 创建 .env 文件(并加入 .gitignore) MYSQL_ROOT_PASSWORD=YourSuperStrongPassword!在
docker-compose.yml中引用:${MYSQL_ROOT_PASSWORD} -
理解
depends_on的局限:它只控制容器启动顺序,不保证应用就绪状态。对于数据库,必须结合healthcheck使用。 -
开发效率技巧:在开发时,可以使用绑定挂载实现代码热重载,避免重复构建镜像。
services: order-service: build: ./order-service volumes: - ./order-service:/app:ro # 将本地代码目录挂载到容器,实时同步 - ~/.nuget/packages:/root/.nuget/packages:ro # 挂载NuGet缓存,加速还原 environment: - ASPNETCORE_ENVIRONMENT=Development -
镜像构建优化:在CI/CD中,可以先单独构建镜像,再使用
image而非build指令,提升编排启动速度。
五、本章心法总结
- 基础设施即代码:
docker-compose.yml是你应用运行环境的权威声明,应纳入版本控制。 - 网络即服务发现:Compose的私有网络是微服务通信的基石,服务名即域名,简化了服务间调用。
- 状态分离:通过
volumes将数据库等有状态数据外置,确保应用容器本身无状态,可随时销毁和重建,这是云原生的核心思想。
下章预告
现在,你的订单服务和商品服务已经可以通过HTTP(REST)优雅地“打电话”沟通了。但“打电话”要求对方必须实时在线并立即接听,这在复杂的分布式场景中并非总是最佳选择。如果对方忙线、或者操作需要耗时很久,该怎么办?
下一章:《微服务对话艺术:何时用REST“打电话”,何时用消息队列“发邮件”?》,我们将深入对比同步与异步通信模式,并引入消息队列这位强大的“异步信使”,解决服务解耦、流量削峰和最终一致性等核心难题。
卡在环境配置?想要一个开箱即用的完整项目参考?
我们为你打包好了本章的全套可运行源码(含Dockerfile与编排脚本)。
微信搜索关注「黑棠会长」,后台回复 「微服务乐高」 ,一键获取完整资源包,快速复现所有操作。
上节指路:第4章:Docker 数据“生存指南”:容器销毁,你的文件如何“永生”?
原创文章,未经授权禁止转载。
更多推荐
所有评论(0)