一、为什么需要 Docker Compose?

现代应用极少是孤立的。一个典型的 Web 应用通常包括:

  • Web 服务器(Nginx / Caddy)
  • 应用服务器(Node.js / Python / Go)
  • 数据库(MySQL / PostgreSQL)
  • 缓存(Redis / Memcached)
  • 消息队列(RabbitMQ / Kafka)

Docker Compose 是 Docker 官方提供的多容器编排工具。通过一个 docker-compose.yml 文件,你可以定义、配置和启动所有服务,实现"一键部署"

docker-compose.yml Nginx 反向代理 :80/:443 app-network Node.js API 应用服务器 :3004 app-network + db-network Worker 后台任务处理 app-network + db-network MySQL 8.0 主数据库 :3306 db-network Redis 7 缓存 + Session db-network Named Volumes mysql_data / redis_data Docker Compose 微服务编排架构 — 多网络隔离 + 数据卷持久化

二、docker-compose.yml 核心配置

version: '3.8'

services:
  # ========== Nginx 反向代理 ==========
  nginx:
    image: nginx:1.25-alpine
    container_name: prod-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
      - ./public:/usr/share/nginx/html:ro
    depends_on:
      api:
        condition: service_healthy
    networks:
      - app-network
    restart: unless-stopped

  # ========== Node.js API ==========
  api:
    build:
      context: ./api
      dockerfile: Dockerfile
    container_name: prod-api
    environment:
      - NODE_ENV=production
      - DATABASE_URL=mysql://user:pass@mysql:3306/finboost
      - REDIS_URL=redis://redis:6379
    volumes:
      - ./api/logs:/app/logs
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - app-network
      - db-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3004/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 15s

  # ========== MySQL 数据库 ==========
  mysql:
    image: mysql:8.0
    container_name: prod-mysql
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: finboost
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - mysql_data:/var/lib/mysql
      - ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    ports:
      - "127.0.0.1:3306:3306"   # 仅本地访问
    networks:
      - db-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 3s
      retries: 5
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --default-authentication-plugin=mysql_native_password

  # ========== Redis 缓存 ==========
  redis:
    image: redis:7-alpine
    container_name: prod-redis
    volumes:
      - redis_data:/data
    ports:
      - "127.0.0.1:6379:6379"
    networks:
      - db-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 5
    command: redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru

# ========== 网络定义 ==========
networks:
  app-network:
    driver: bridge
  db-network:
    driver: bridge
    internal: true    # 数据库网络不与外部通信

# ========== 数据卷 ==========
volumes:
  mysql_data:
    driver: local
  redis_data:
    driver: local

三、常用操作命令

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

# 查看服务运行状态
docker compose ps

# 查看实时日志
docker compose logs -f

# 查看特定服务日志
docker compose logs -f api

# 重启某个服务
docker compose restart api

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

# 零停机更新(滚动更新)
docker compose up -d --no-deps --build api

# 停止并删除所有容器/网络
docker compose down

# 停止并同时删除数据卷(⚠️ 谨慎使用)
docker compose down -v

# 进入服务容器
docker compose exec api sh

# 扩展服务实例(需去除端口映射)
docker compose up -d --scale api=3

四、网络配置与通信

Docker Compose 默认创建一个 bridge 网络,所有服务可以通过服务名作为主机名互相通信:

# API 服务中连接 MySQL — 使用服务名而非 IP
DATABASE_URL=mysql://user:pass@mysql:3306/finboost

# API 服务中连接 Redis
REDIS_URL=redis://redis:6379

网络隔离最佳实践:

  • app-network:Nginx + API(对外可见)
  • db-network:API + MySQL + Redis(internal: true,外部不可访问)
  • API 同时连接两个网络,充当桥梁角色

五、环境变量管理

# .env 文件(提交到 .gitignore)
MYSQL_ROOT_PASSWORD=SuperSecret123
MYSQL_USER=finboost
MYSQL_PASSWORD=f1nb00stP@ss
JWT_SECRET=your-256-bit-secret

# .env.example(提交到 Git 作为模板)
MYSQL_ROOT_PASSWORD=changeme
MYSQL_USER=changeme
MYSQL_PASSWORD=changeme
JWT_SECRET=changeme

六、健康检查详解

健康检查确保依赖服务真正就绪后再启动下游服务,避免"启动成功但连接失败"的经典问题:

参数 说明 推荐值
interval检查间隔10-30s
timeout单次检查超时3-5s
retries失败重试次数3-5次
start_period启动缓冲时间15-30s

七、生产环境优化建议

  1. 固定镜像版本:使用 mysql:8.0.33 而非 mysql:latest,避免意外升级
  2. 资源限制:为每个服务设置 deploy.resources.limits 防止单容器耗尽宿主机资源
  3. 日志轮转:配置 logging 驱动限制日志大小,防止磁盘写满
  4. 只读挂载:配置文件和静态资源使用 :ro 只读挂载
  5. 敏感信息:使用 Docker Secrets 或外部 .env 管理密码,绝不硬编码
  6. 数据库备份:配置定时任务备份 mysql_data 卷到远程存储
💡 Docker Compose 黄金法则

一个 Compose 文件描述一个完整的应用栈,不要混入无关服务。使用 depends_on + condition: service_healthy 确保启动顺序。数据库网络设置 internal: true 防止外部直接访问。生产环境务必配置资源限制和日志轮转,避免单点故障影响整个宿主机。