Docker Compose 多服务编排实战:从零搭建微服务架构
目录
- [为什么需要 Docker Compose?](#为什么需要 Docker Compose?)
- 实战项目架构
- 环境准备
- 核心服务搭建
- 高级特性:负载均衡与服务发现
- [日志集中管理(EFK 栈)](#日志集中管理(EFK 栈))
- 生产环境最佳实践
- 常见问题排查
为什么需要 Docker Compose?
在单体应用时代,一个容器可能足以运行整个应用。但在微服务架构下,一个完整的应用通常由多个服务组成:Web 前端、API 网关、多个后端服务、数据库、缓存、消息队列等。
手动管理这些容器的痛点:
- 需要记住每个服务的启动顺序和依赖关系
- 网络配置复杂,服务间通信困难
- 环境不一致导致"在我机器上能跑"的问题
- 日志分散在各个容器,难以排查问题
Docker Compose 通过一个简单的 YAML 文件定义多容器应用,一条命令即可启动整个技术栈。
实战项目架构
我们将构建一个完整的电商微服务系统,包含以下组件:
┌─────────────────────────────────────────────────────────────┐
│ Nginx 网关 │
│ (负载均衡 + 反向代理) │
└─────────────┬───────────────────────────────────────────────┘
│
┌─────────┴──────────┐
│ │
┌───▼────┐ ┌────▼────┐
│前端服务 │ │ API 网关 │
│(React) │ │(Node.js)│
└────┬───┘ └────┬────┘
│ │
└─────────┬─────────┘
│
┌──────────┼──────────┐
│ │ │
┌───▼───┐ ┌──▼───┐ ┌───▼───┐
│订单服务 │ │用户服务│ │商品服务 │
│(Java) │ │(Java) │ │(Java) │
└───┬───┘ └──┬───┘ └───┬───┘
│ │ │
└─────────┼──────────┘
│
┌─────────▼──────────┐
│ │
┌───▼────┐ ┌────▼────┐
│PostgreSQL│ │ Redis │
│(主从集群)│ │ (缓存) │
└─────────┘ └─────────┘
环境准备
1. 安装 Docker Compose
bash
# 验证安装
docker-compose --version
# 如果未安装,使用以下命令(Linux)
curl -L "https://github.com/docker/compose/releases/download/v2.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
2. 项目目录结构
bash
mkdir -p ecommerce-platform/{services/{frontend,api-gateway,order-service,user-service,product-service},infra/{postgres,redis,nginx},logs,fluentd/conf}
cd ecommerce-platform
tree -L 2
预期输出:
.
├── docker-compose.yml # 主编排文件
├── docker-compose.prod.yml # 生产环境覆盖配置
├── docker-compose.efk.yml # 日志收集栈
├── services/
│ ├── frontend/ # React 前端
│ ├── api-gateway/ # Node.js API 网关
│ ├── order-service/ # Java 订单服务
│ ├── user-service/ # Java 用户服务
│ └── product-service/ # Java 商品服务
├── infra/
│ ├── nginx/ # Nginx 配置
│ ├── postgres/ # 数据库初始化脚本
│ └── redis/ # Redis 配置
├── logs/ # 日志挂载目录
└── fluentd/
└── conf/
└── fluent.conf # Fluentd 收集规则
核心服务搭建
步骤 1:数据库与基础设施
创建 docker-compose.yml 基础层:
yaml
version: '3.8'
services:
# PostgreSQL 主库
postgres-master:
image: postgres:15-alpine
container_name: ecommerce-postgres-master
environment:
POSTGRES_USER: ecommerce
POSTGRES_PASSWORD: ${DB_PASSWORD:-secure_password_123}
POSTGRES_DB: ecommerce_db
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- postgres-master-data:/var/lib/postgresql/data
- ./infra/postgres/init-master.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ecommerce -d ecommerce_db"]
interval: 10s
timeout: 5s
retries: 5
networks:
- backend-network
# Redis 缓存
redis:
image: redis:7-alpine
container_name: ecommerce-redis
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis-data:/data
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- backend-network
# 消息队列(RabbitMQ)
rabbitmq:
image: rabbitmq:3.12-management-alpine
container_name: ecommerce-rabbitmq
environment:
RABBITMQ_DEFAULT_USER: admin
RABBITMQ_DEFAULT_PASS: ${MQ_PASSWORD:-admin123}
ports:
- "5672:5672" # AMQP 端口
- "15672:15672" # 管理界面
volumes:
- rabbitmq-data:/var/lib/rabbitmq
networks:
- backend-network
volumes:
postgres-master-data:
redis-data:
rabbitmq-data:
networks:
backend-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
关键配置解析:
- 健康检查:确保数据库就绪后才启动依赖服务
- 环境变量 :使用
${VAR:-default}语法提供默认值,同时支持外部传入 - 命名卷:数据持久化,避免容器重启数据丢失
- 自定义网络:指定 IP 段,避免与现有网络冲突
步骤 2:Java 微服务
以订单服务为例,创建 services/order-service/Dockerfile:
dockerfile
# 多阶段构建优化镜像大小
FROM eclipse-temurin:17-jdk-alpine AS builder
WORKDIR /app
COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline
COPY src ./src
RUN ./mvnw package -DskipTests
# 运行阶段使用更小的 JRE 镜像
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# 创建非 root 用户提升安全性
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
在 docker-compose.yml 中添加服务定义:
yaml
# 订单服务
order-service:
build:
context: ./services/order-service
dockerfile: Dockerfile
container_name: ecommerce-order-service
environment:
- SPRING_PROFILES_ACTIVE=docker
- SPRING_DATASOURCE_URL=jdbc:postgresql://postgres-master:5432/ecommerce_db
- SPRING_DATASOURCE_USERNAME=ecommerce
- SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD:-secure_password_123}
- SPRING_REDIS_HOST=redis
- SPRING_RABBITMQ_HOST=rabbitmq
- SERVER_PORT=8080
depends_on:
postgres-master:
condition: service_healthy
redis:
condition: service_healthy
rabbitmq:
condition: service_started
deploy:
replicas: 2
resources:
limits:
cpus: '1'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
networks:
- backend-network
- service-network
# 用户服务(类似配置)
user-service:
build:
context: ./services/user-service
container_name: ecommerce-user-service
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://postgres-master:5432/ecommerce_db
depends_on:
postgres-master:
condition: service_healthy
networks:
- backend-network
- service-network
# 商品服务
product-service:
build:
context: ./services/product-service
container_name: ecommerce-product-service
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://postgres-master:5432/ecommerce_db
- SPRING_REDIS_HOST=redis
depends_on:
postgres-master:
condition: service_healthy
redis:
condition: service_healthy
networks:
- backend-network
- service-network
生产级优化点:
- 多阶段构建:将编译和运行分离,最终镜像体积减少 60% 以上
- 资源限制 :通过
deploy.resources限制 CPU 和内存,防止单个服务耗尽主机资源 - 非 root 用户:遵循安全最佳实践,减少攻击面
- JVM 容器感知 :使用
-XX:+UseContainerSupport让 JVM 正确识别容器内存限制
步骤 3:API 网关(Node.js)
创建 services/api-gateway/Dockerfile:
dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]
添加网关配置:
yaml
api-gateway:
build: ./services/api-gateway
container_name: ecommerce-api-gateway
environment:
- NODE_ENV=production
- PORT=3000
- ORDER_SERVICE_URL=http://order-service:8080
- USER_SERVICE_URL=http://user-service:8080
- PRODUCT_SERVICE_URL=http://product-service:8080
- REDIS_URL=redis://redis:6379
ports:
- "3000:3000"
depends_on:
- order-service
- user-service
- product-service
networks:
- backend-network
- service-network
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
步骤 4:前端与 Nginx 负载均衡
创建 infra/nginx/nginx.conf:
nginx
upstream api_gateway {
least_conn; # 最少连接数负载均衡算法
server api-gateway:3000 max_fails=3 fail_timeout=30s;
}
upstream order_service {
least_conn;
server order-service:8080;
}
server {
listen 80;
server_name localhost;
# 前端静态资源
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
# API 代理
location /api/ {
proxy_pass http://api_gateway/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 超时配置
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# 健康检查端点
location /nginx-health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
添加 Nginx 和前端服务:
yaml
# React 前端构建
frontend:
build:
context: ./services/frontend
dockerfile: Dockerfile
args:
- REACT_APP_API_URL=/api
container_name: ecommerce-frontend
networks:
- frontend-network
# Nginx 网关与负载均衡
nginx:
image: nginx:alpine
container_name: ecommerce-nginx
ports:
- "80:80"
volumes:
- ./infra/nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- frontend
- api-gateway
networks:
- frontend-network
- service-network
高级特性:负载均衡与服务发现
服务扩展与负载均衡
Docker Compose 支持快速扩展服务实例:
bash
# 扩展订单服务为 3 个实例
docker-compose up -d --scale order-service=3
修改 Nginx 配置以支持动态上游:
nginx
# 使用变量配合 resolver 实现动态服务发现
resolver 127.0.0.11 valid=30s; # Docker 内置 DNS
upstream backend {
zone upstream_backend 64k;
server order-service:8080 resolve;
}
网络隔离策略
创建分层网络提升安全性:
yaml
networks:
# 外部访问层
frontend-network:
driver: bridge
internal: false
# 服务间通信层
service-network:
driver: bridge
internal: true # 禁止外部访问
# 数据层(仅数据库和缓存)
backend-network:
driver: bridge
internal: true
网络隔离原则:
- 数据库和缓存只在
backend-network,不暴露端口到宿主机 - 微服务同时连接
backend-network(访问数据)和service-network(服务间通信) - Nginx 是唯一暴露 80 端口的入口
日志集中管理(EFK 栈)
当服务数量增加,分散的日志难以排查问题。我们集成 EFK(Elasticsearch + Fluentd + Kibana)实现日志中心化。
创建 docker-compose.efk.yml:
yaml
version: '3.8'
services:
elasticsearch:
image: elasticsearch:8.11.0
container_name: ecommerce-elasticsearch
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- es-data:/usr/share/elasticsearch/data
ports:
- "9200:9200"
networks:
- logging-network
fluentd:
image: fluent/fluentd:v1.16-1
container_name: ecommerce-fluentd
volumes:
- ./fluentd/conf:/fluentd/etc
- ./logs:/var/log/fluentd
ports:
- "24224:24224"
- "24224:24224/udp"
depends_on:
- elasticsearch
networks:
- logging-network
kibana:
image: kibana:8.11.0
container_name: ecommerce-kibana
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
ports:
- "5601:5601"
depends_on:
- elasticsearch
networks:
- logging-network
volumes:
es-data:
networks:
logging-network:
driver: bridge
创建 fluentd/conf/fluent.conf:
xml
<source>
@type forward
port 24224
bind 0.0.0.0
</source>
<filter **>
@type parser
format json
key_name log
reserve_data true
</filter>
<match **>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
logstash_prefix ecommerce-logs
flush_interval 10s
</match>
使用方式:
bash
# 启动业务服务 + EFK 栈
docker-compose -f docker-compose.yml -f docker-compose.efk.yml up -d
# 配置业务服务日志驱动
在主 docker-compose.yml 中添加日志驱动配置:
yaml
order-service:
logging:
driver: "fluentd"
options:
fluentd-address: localhost:24224
tag: "order-service"
labels: "service_name,environment"
访问 http://localhost:5601 查看 Kibana 界面,创建索引模式 ecommerce-logs-* 即可搜索所有服务日志。
生产环境最佳实践
1. 多环境配置分离
创建 docker-compose.prod.yml 覆盖生产配置:
yaml
version: '3.8'
services:
order-service:
deploy:
replicas: 3
resources:
limits:
cpus: '2'
memory: 1G
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "10"
restart: unless-stopped
postgres-master:
volumes:
- /mnt/nfs/postgres-data:/var/lib/postgresql/data # 使用 NFS 存储
command: >
postgres
-c max_connections=200
-c shared_buffers=2GB
-c effective_cache_size=6GB
启动命令:
bash
# 开发环境
docker-compose up -d
# 生产环境(合并配置)
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
2. 敏感信息管理
使用 Docker Secrets(Swarm 模式)或环境变量文件:
bash
# .env 文件(加入 .gitignore)
DB_PASSWORD=your_secure_password
JWT_SECRET=your_jwt_secret
yaml
order-service:
env_file:
- .env
secrets:
- db_password
- jwt_secret
secrets:
db_password:
file: ./secrets/db_password.txt
jwt_secret:
file: ./secrets/jwt_secret.txt
3. 健康检查与自动恢复
yaml
order-service:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s # 启动宽限期
restart: unless-stopped
4. 资源限制与日志轮转
yaml
nginx:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
labels: "service_name,environment"
env: "OS_VERSION"
常见问题排查
问题 1:服务启动顺序依赖
现象:应用服务启动失败,提示数据库连接超时。
解决 :使用 depends_on 配合健康检查,确保数据库就绪后才启动应用。
yaml
depends_on:
postgres-master:
condition: service_healthy # 需要 Docker Compose 2.20+
问题 2:容器间通信失败
现象 :服务 A 无法访问服务 B,提示 Connection refused。
排查步骤:
- 检查是否在同一网络:
docker network inspect backend-network - 检查服务名是否正确(使用容器名而非 localhost)
- 检查端口是否暴露(内部通信不需要
ports映射)
问题 3:数据卷权限错误
现象 :PostgreSQL 启动失败,Permission denied。
解决:
bash
# 宿主机目录赋予容器用户权限
sudo chown -R 999:999 ./postgres-data # 999 是 postgres 容器用户 ID
问题 4:内存不足 OOM
现象 :容器频繁重启,Exit Code 137。
解决:添加 JVM 参数和资源限制:
yaml
environment:
- JAVA_OPTS=-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0
deploy:
resources:
limits:
memory: 1G
完整启动流程
bash
# 1. 克隆项目并进入目录
git clone <your-repo> && cd ecommerce-platform
# 2. 创建环境变量文件
cat > .env << EOF
DB_PASSWORD=SecureP@ssw0rd123
JWT_SECRET=your-256-bit-secret
EOF
# 3. 构建所有镜像(首次)
docker-compose build
# 4. 启动基础设施(数据库、缓存)
docker-compose up -d postgres-master redis rabbitmq
# 5. 等待数据库就绪
docker-compose exec postgres-master pg_isready -U ecommerce
# 6. 启动业务服务
docker-compose up -d order-service user-service product-service
# 7. 启动网关和前端
docker-compose up -d api-gateway frontend nginx
# 8. 验证状态
docker-compose ps
# 9. 查看日志
docker-compose logs -f order-service
# 10. 压力测试(可选)
docker-compose up -d --scale order-service=3
总结
通过本实战项目,我们掌握了:
✅ 多服务编排 :使用 Docker Compose 管理 8+ 个服务的依赖关系
✅ 网络设计 :分层网络架构实现安全隔离
✅ 负载均衡 :Nginx 反向代理 + 服务扩展
✅ 日志收集 :EFK 栈实现中心化日志管理
✅ 生产优化:资源限制、健康检查、多阶段构建
Docker Compose 是微服务开发测试阶段的利器,配合 Swarm 或 Kubernetes 可实现生产级容器编排。建议将 Compose 文件纳入版本控制,并通过 CI/CD 流水线自动化部署。
💡 提示:本实战代码已整理为可运行模板,你可以基于此架构快速搭建自己的微服务平台。生产环境建议结合 Docker Swarm 或 Kubernetes 实现更强大的编排能力。