一、为什么需要 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 核心配置
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 |
七、生产环境优化建议
- 固定镜像版本:使用
mysql:8.0.33而非mysql:latest,避免意外升级 - 资源限制:为每个服务设置
deploy.resources.limits防止单容器耗尽宿主机资源 - 日志轮转:配置
logging驱动限制日志大小,防止磁盘写满 - 只读挂载:配置文件和静态资源使用
:ro只读挂载 - 敏感信息:使用 Docker Secrets 或外部 .env 管理密码,绝不硬编码
- 数据库备份:配置定时任务备份 mysql_data 卷到远程存储
💡 Docker Compose 黄金法则
一个 Compose 文件描述一个完整的应用栈,不要混入无关服务。使用 depends_on + condition: service_healthy 确保启动顺序。数据库网络设置 internal: true 防止外部直接访问。生产环境务必配置资源限制和日志轮转,避免单点故障影响整个宿主机。