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
