Docker Compose 多服务编排实战:从零搭建微服务架构

Docker Compose 多服务编排实战:从零搭建微服务架构

目录

  1. [为什么需要 Docker Compose?](#为什么需要 Docker Compose?)
  2. 实战项目架构
  3. 环境准备
  4. 核心服务搭建
  5. 高级特性:负载均衡与服务发现
  6. [日志集中管理(EFK 栈)](#日志集中管理(EFK 栈))
  7. 生产环境最佳实践
  8. 常见问题排查

为什么需要 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

排查步骤:

  1. 检查是否在同一网络:docker network inspect backend-network
  2. 检查服务名是否正确(使用容器名而非 localhost)
  3. 检查端口是否暴露(内部通信不需要 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 实现更强大的编排能力。

相关推荐
美狐美颜SDK开放平台7 小时前
视频美颜SDK是什么?直播APP开发中美颜功能实现方式介绍
深度学习·架构·实时互动·音视频·视频美颜sdk
灰阳阳8 小时前
Docker-数据卷命令解析
java·docker·eureka
画中有画9 小时前
论多源异构数据集成与数据架构设计
架构
zhangfeng11339 小时前
AMD Instinct MI50(gfx906)上为 Qwen 系列模型优化并可用的 vLLM 相关仓库、Docker 镜像与实践指南。
人工智能·docker·ai编程·qwen·算子开发·vllm·mi50
风萧何10 小时前
架构艺术,是权衡的艺术。
前端·javascript·架构
aaaBsBsBsB10 小时前
k8s pod管理
运维·docker·容器
她的男孩11 小时前
我把管理系统接给AI,它改条数据都要先问我
java·后端·架构
AI探索派11 小时前
Agent Teams和Agent Swarm是什么?多Agent协作原理实战拆解
人工智能·架构·agent
这个DBA有点耶11 小时前
MySQL主从延迟的“最后一公里”:如何把延迟压到极限
数据库·程序员·架构