Docker Compose 一键编排微服务:从开发环境到生产部署的完整方案


阿里云推广

Docker Compose 一键编排微服务:从开发环境到生产部署的完整方案

一个真实的微服务架构通常包含 8-15 个容器:Web 应用、数据库、缓存、消息队列、日志收集……本文分享一套从开发到生产都可复用的 Docker Compose 编排方案。

1. 为什么需要 Docker Compose?

1.1 没有编排的痛点

我之前接手一个 Python 微服务项目,新人入职第一天的任务是:

# 手动启动 7 个服务,顺序还不能错
redis-server &
mongod &
python -m celery worker &
python -m gunicorn app:app &
# ... 还要改 3 个配置文件里的 IP 地址

结果:新人第一天啥也没干成,光配环境就花了 4 小时。

1.2 Docker Compose 的核心价值

场景 没有 Compose 有 Compose
—— ————- ———–
新人入职 半天配环境 `docker compose up -d`,5 分钟跑起来
多服务依赖 手动控制启动顺序 `depends_on` 自动管理
环境一致性 开发/测试/生产环境不一致 同一份 YAML,处处运行
服务通信 硬编码 IP 内置 DNS,服务名直连

2. 开发一个完整的微服务栈

2.1 架构图

                    ┌─────────────┐
                    │   Nginx    │  (反向代理/负载均衡)
                    │   :80      │
                    └──────┬──────┘
                           │
            ┌──────────────┼──────────────┐
            │              │              │
      ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
      │ Web-API 1 │ │ Web-API 2 │ │ Web-API 3 │
      │  :5000    │ │  :5000    │ │  :5000    │
      └─────┬─────┘ └─────┬─────┘ └─────┬─────┘
            │              │              │
            └──────────────┼──────────────┘
                           │
            ┌──────────────┼──────────────┐
            │              │              │
      ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
      │  Redis     │ │  MySQL    │ │  RabbitMQ │
      │  :6379    │ │  :3306    │ │  :5672    │
      └────────────┘ └────────────┘ └────────────┘

2.2 完整的 docker-compose.yml

# docker-compose.yml
version: '3.8'

services:
  # ========== 数据库层 ==========
  mysql:
    image: mysql:8.0
    container_name: microservice-mysql
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: root123456
      MYSQL_DATABASE: app_db
      MYSQL_USER: appuser
      MYSQL_PASSWORD: apppass
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - microservice-net
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7.2-alpine
    container_name: microservice-redis
    restart: unless-stopped
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    networks:
      - microservice-net
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 5

  rabbitmq:
    image: rabbitmq:3-management
    container_name: microservice-rabbitmq
    restart: unless-stopped
    environment:
      RABBITMQ_DEFAULT_USER: guest
      RABBITMQ_DEFAULT_PASS: guest
    ports:
      - "5672:5672"   # AMQP 端口
      - "15672:15672"  # 管理界面
    volumes:
      - rabbitmq_data:/var/lib/rabbitmq
    networks:
      - microservice-net

  # ========== 应用层 ==========
  web-api:
    build:
      context: ./web-api
      dockerfile: Dockerfile
    container_name: microservice-web
    restart: unless-stopped
    environment:
      - DATABASE_URL=mysql+pymysql://appuser:apppass@mysql:3306/app_db
      - REDIS_URL=redis://redis:6379/0
      - RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672/
    ports:
      - "5000:5000"
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - microservice-net
    deploy:
      replicas: 3  # 生产环境可扩展为多个实例

  # ========== 异步任务 Worker ==========
  worker:
    build:
      context: ./worker
      dockerfile: Dockerfile
    container_name: microservice-worker
    restart: unless-stopped
    environment:
      - DATABASE_URL=mysql+pymysql://appuser:apppass@mysql:3306/app_db
      - REDIS_URL=redis://redis:6379/0
      - RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672/
    depends_on:
      - mysql
      - redis
      - rabbitmq
    networks:
      - microservice-net

  # ========== 反向代理 ==========
  nginx:
    image: nginx:alpine
    container_name: microservice-nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
    depends_on:
      - web-api
    networks:
      - microservice-net

volumes:
  mysql_data:
  redis_data:
  rabbitmq_data:

networks:
  microservice-net:
    driver: bridge

3. Python Web 应用 Dockerfile

3.1 多阶段构建(优化镜像大小)

# web-api/Dockerfile
# 阶段 1:构建依赖
FROM python:3.12-slim AS builder

WORKDIR /app
RUN pip install --user poetry

COPY pyproject.toml poetry.lock ./
RUN poetry export -f requirements.txt --output requirements.txt

# 阶段 2:运行环境
FROM python:3.12-slim

WORKDIR /app

# 复制依赖清单并安装
COPY --from=builder /app/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD python -c "import requests; requests.get('http://localhost:5000/health')" || exit 1

EXPOSE 5000
CMD ["gunicorn", "app:app", "-b", "0.0.0.0:5000", "-w", "4"]

3.2 Python 应用代码(Flask 示例)

# web-api/app.py
from flask import Flask, jsonify
import redis
import pymysql
from celery import Celery

app = Flask(__name__)

# Redis 连接
redis_client = redis.Redis(host='redis', port=6379, decode_responses=True)

# Celery 配置
celery = Celery('app', broker='amqp://guest:guest@rabbitmq:5672//')

@app.route('/health')
def health():
    """健康检查接口"""
    return jsonify({"status": "ok"}), 200

@app.route('/api/users')
def get_users():
    """示例接口:从 MySQL 读取数据"""
    conn = pymysql.connect(
        host='mysql', user='appuser', password='apppass',
        database='app_db', charset='utf8mb4'
    )
    cursor = conn.cursor()
    cursor.execute("SELECT id, username FROM users LIMIT 10")
    users = [{"id": r[0], "username": r[1]} for r in cursor.fetchall()]
    conn.close()
    return jsonify(users)

@app.route('/api/cache/<key>')
def get_cache(key):
    """示例接口:从 Redis 读取缓存"""
    value = redis_client.get(key)
    return jsonify({"key": key, "value": value})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

4. Nginx 反向代理配置

# nginx/nginx.conf
events {
    worker_connections 1024;
}

http {
    upstream web_backend {
        # 负载均衡:轮询分发到 3 个 Web 实例
        server web-api:5000;
        # Docker Compose 的 deploy.replicas 会启动多个实例
        # 如果有服务发现,可以用 DNS 轮询
    }

    server {
        listen 80;
        server_name api.example.com;

        location / {
            proxy_pass http://web_backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        location /health {
            proxy_pass http://web_backend/health;
        }
    }
}

5. 常用操作命令速查

# 启动所有服务(后台运行)
docker compose up -d

# 查看服务状态
docker compose ps

# 查看某个服务的日志
docker compose logs web-api -f

# 扩容 Web 服务到 5 个实例
docker compose up -d --scale web-api=5

# 停止所有服务(保留数据卷)
docker compose stop

# 完全清理(⚠️ 会删除数据卷)
docker compose down -v

# 重新构建并启动(代码更新后)
docker compose up -d --build

# 进入某个容器调试
docker compose exec web-api bash

# 查看资源占用
docker compose top

6. 生产环境进阶配置

6.1 资源限制(防止单个服务吃掉所有内存)

# 在 docker-compose.prod.yml 中覆盖
services:
  mysql:
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 4G
        reservations:
          cpus: '1'
          memory: 2G

  web-api:
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '1'
          memory: 1G

6.2 环境变量管理(敏感信息不入库)

# .env 文件(加入 .gitignore)
MYSQL_ROOT_PASSWORD=super_secure_password
REDIS_PASSWORD=redis_pass_2026
API_SECRET_KEY=your-secret-key

# docker-compose.yml 中引用
environment:
  - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}

6.3 生产环境启动命令

# 使用覆盖文件合并配置
docker compose -f docker-compose.yml \
               -f docker-compose.prod.yml \
               up -d

7. 常见问题排查

问题 原因 解决方案
—— —— ———
`depends_on` 不生效,服务启动顺序还是乱的 `depends_on` 只控制启动顺序,不等待「就绪」 加上 `healthcheck`,用 `condition: service_healthy`
容器之间无法通信 不在同一网络 确保所有服务都在同一个 `networks` 下
数据卷权限错误 容器内用户 ID 和宿主机不一致 Dockerfile 中加 `USER root` 或用命名卷
生产环境端口冲突 宿主机端口被占用 用 Nginx 反向代理,容器端口不对外暴露

8. 总结

开发环境:docker compose up -d          → 一键启动所有依赖
测试环境:复用同一份 YAML               → 环境一致性保证
生产环境:+ 资源限制 + 副本扩容        → 高可用部署

核心价值

1. ✅ 新人 5 分钟跑起完整环境

2. ✅ 开发/测试/生产环境完全一致

3. ✅ 服务依赖关系清晰可维护

4. ✅ 配合 deploy.replicas 轻松扩容


👤 作者简介

一枚在大中原腹地(河南)卖公有云的从业者,主营腾讯云/阿里云/火山云,曾踩坑无数,现专注AI大模型应用落地。关注公众号「公有云cloud」,围观AI前沿动态~

博客:yunduancloud.icu

发表评论