
Docker Compose 生产环境实战
虽然 Kubernetes 擅长处理大规模部署,但 Docker Compose 对于中小型生产工作负载同样表现出色。本指南将介绍生产级配置。
生产就绪的 docker-compose.yml
services:
api:
image: myapp/api:${TAG:-latest}
restart: unless-stopped
# 资源限制
deploy:
resources:
limits:
cpus: '2'
memory: 512M
reservations:
memory: 256M
replicas: 2
# 健康检查
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
environment:
NODE_ENV: production
PORT: 3000
# 密钥(不在环境变量中!)
secrets:
- db_password
- jwt_secret
networks:
- frontend
- backend
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
postgres:
image: postgres:15-alpine
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
environment:
POSTGRES_DB: myapp
POSTGRES_USER: myapp
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
secrets:
- db_password
networks:
- backend
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --appendonly yes --requirepass-file /run/secrets/redis_password
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
volumes:
- redis_data:/data
secrets:
- redis_password
networks:
- backend
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
networks:
- frontend
depends_on:
- api
secrets:
db_password:
file: ./secrets/db_password.txt
jwt_secret:
file: ./secrets/jwt_secret.txt
redis_password:
file: ./secrets/redis_password.txt
volumes:
postgres_data:
redis_data:
networks:
frontend:
backend:
internal: true # 外部不可访问

在应用中读取密钥
// Node.js: 从 /run/secrets/ 读取 Docker 密钥
const fs = require('fs');
function getSecret(name) {
const secretPath = `/run/secrets/${name}`;
try {
return fs.readFileSync(secretPath, 'utf8').trim();
} catch {
// 本地开发时回退到环境变量
return process.env[name.toUpperCase()];
}
}
const config = {
dbPassword: getSecret('db_password'),
jwtSecret: getSecret('jwt_secret'),
};
滚动更新
# 拉取新镜像
docker compose pull api
# 滚动重启(每次更新一个副本)
docker compose up -d --no-deps --scale api=2 api
# 或使用更新顺序
docker compose up -d api
# Compose 会在新容器健康后停止旧容器

备份与恢复
# 备份 PostgreSQL
docker compose exec postgres pg_dump -U myapp myapp | gzip > backup-$(date +%Y%m%d).sql.gz
# 恢复
gunzip -c backup-20240101.sql.gz | docker compose exec -T postgres psql -U myapp myapp
# 备份卷
docker run --rm \
-v myapp_postgres_data:/data \
-v $(pwd)/backups:/backup \
alpine tar czf /backup/postgres-$(date +%Y%m%d).tar.gz /data
环境特定覆盖
# docker-compose.override.yml(开发环境自动加载)
services:
api:
build: . # 开发时从源码构建
volumes:
- .:/app # 热重载
environment:
NODE_ENV: development
ports:
- "9229:9229" # 调试端口
postgres:
ports:
- "5432:5432" # 开发时暴露端口
# 生产环境:显式指定文件
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 开发环境:自动使用覆盖文件
docker compose up -d

使用 Compose 进行监控
# 添加 Prometheus + Grafana
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
networks:
- backend
grafana:
image: grafana/grafana
ports:
- "3001:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD_FILE: /run/secrets/grafana_password
secrets:
- grafana_password
networks:
- backend
总结
生产环境 Docker Compose 模式:
- 始终使用健康检查进行依赖排序
- 将密钥存储为文件,而非环境变量
- 使用内部网络隔离后端服务
- 设置资源限制以防止容器资源耗尽
- 使用环境特定的覆盖文件
- 对有状态服务自动化备份