Docker Compose 多文件配置
概述
Docker Compose 支持使用多个配置文件来管理复杂的应用部署。通过多文件配置,可以实现环境分离、配置复用、模块化管理等功能。本文将详细介绍 Docker Compose 多文件配置的各种方法和最佳实践。
多文件配置方法
1. Override 文件
Docker Compose 会自动查找并合并以下文件:
docker-compose.yml(基础配置)docker-compose.override.yml(覆盖配置)
2. 指定多个文件
# 使用 -f 参数指定多个文件
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up
# 文件顺序很重要,后面的文件会覆盖前面的配置
docker-compose -f base.yml -f override.yml -f local.yml up
3. 环境变量指定
# 设置 COMPOSE_FILE 环境变量
export COMPOSE_FILE=docker-compose.yml:docker-compose.override.yml:docker-compose.local.yml
docker-compose up
基础配置文件
docker-compose.yml
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80"
volumes:
- ./html:/usr/share/nginx/html:ro
depends_on:
- app
networks:
- frontend
- backend
app:
build:
context: .
dockerfile: Dockerfile
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
volumes:
- ./app:/usr/src/app
- node_modules:/usr/src/app/node_modules
depends_on:
- db
- redis
networks:
- backend
db:
image: postgres:13-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- backend
redis:
image: redis:6-alpine
volumes:
- redis_data:/data
networks:
- backend
volumes:
postgres_data:
redis_data:
node_modules:
networks:
frontend:
backend:
Override 文件配置
docker-compose.override.yml(开发环境)
version: '3.8'
services:
web:
ports:
- "8080:80" # 明确指定端口映射
volumes:
- ./nginx/dev.conf:/etc/nginx/conf.d/default.conf:ro
app:
build:
target: development # 使用开发阶段的 Dockerfile
environment:
- NODE_ENV=development
- DEBUG=app:*
- HOT_RELOAD=true
volumes:
- ./app:/usr/src/app # 开发时挂载源码
- /usr/src/app/node_modules # 排除 node_modules
ports:
- "3000:3000" # 暴露调试端口
command: npm run dev
db:
ports:
- "5432:5432" # 开发时暴露数据库端口
environment:
- POSTGRES_DB=myapp_dev
volumes:
- ./db/init-dev.sql:/docker-entrypoint-initdb.d/init.sql:ro
redis:
ports:
- "6379:6379" # 开发时暴露 Redis 端口
# 开发环境专用服务
mailhog:
image: mailhog/mailhog
ports:
- "1025:1025" # SMTP
- "8025:8025" # Web UI
networks:
- backend
环境特定配置
docker-compose.prod.yml(生产环境)
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/prod.conf:/etc/nginx/conf.d/default.conf:ro
- ./ssl:/etc/nginx/ssl:ro
- static_files:/usr/share/nginx/html/static:ro
restart: unless-stopped
deploy:
replicas: 2
resources:
limits:
cpus: '0.5'
memory: 512M
app:
image: myapp:${APP_VERSION:-latest}
environment:
- NODE_ENV=production
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- SECRET_KEY=${SECRET_KEY}
restart: unless-stopped
deploy:
replicas: 3
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
db:
image: postgres:13-alpine
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
- ./backup:/backup
restart: unless-stopped
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
redis:
image: redis:6-alpine
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
restart: unless-stopped
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
volumes:
postgres_data:
driver: local
driver_opts:
type: none
o: bind
device: /opt/myapp/data/postgres
redis_data:
driver: local
driver_opts:
type: none
o: bind
device: /opt/myapp/data/redis
static_files:
driver: local
driver_opts:
type: none
o: bind
device: /opt/myapp/static
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
docker-compose.test.yml(测试环境)
version: '3.8'
services:
app:
build:
target: test
environment:
- NODE_ENV=test
- DATABASE_URL=postgresql://test:test@test_db:5432/test_db
command: npm test
depends_on:
- test_db
- test_redis
test_db:
image: postgres:13-alpine
environment:
- POSTGRES_DB=test_db
- POSTGRES_USER=test
- POSTGRES_PASSWORD=test
tmpfs:
- /var/lib/postgresql/data # 使用内存存储提高测试速度
test_redis:
image: redis:6-alpine
tmpfs:
- /data
# 测试工具
selenium:
image: selenium/standalone-chrome
ports:
- "4444:4444"
shm_size: 2gb
模块化配置
docker-compose.base.yml(基础服务)
version: '3.8'
services:
db:
image: postgres:13-alpine
environment:
- POSTGRES_DB=${POSTGRES_DB:-myapp}
- POSTGRES_USER=${POSTGRES_USER:-user}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- backend
redis:
image: redis:6-alpine
volumes:
- redis_data:/data
networks:
- backend
rabbitmq:
image: rabbitmq:3-management-alpine
environment:
- RABBITMQ_DEFAULT_USER=${RABBITMQ_USER:-admin}
- RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASS:-password}
volumes:
- rabbitmq_data:/var/lib/rabbitmq
networks:
- backend
volumes:
postgres_data:
redis_data:
rabbitmq_data:
networks:
backend:
docker-compose.web.yml(Web 服务)
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "${WEB_PORT:-80}:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- static_files:/usr/share/nginx/html/static:ro
depends_on:
- app
networks:
- frontend
- backend
app:
build:
context: .
dockerfile: Dockerfile
target: ${BUILD_TARGET:-production}
environment:
- NODE_ENV=${NODE_ENV:-production}
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
volumes:
- static_files:/usr/src/app/static
networks:
- backend
volumes:
static_files:
networks:
frontend:
backend:
external: true
docker-compose.monitoring.yml(监控服务)
version: '3.8'
services:
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
networks:
- monitoring
- backend
grafana:
image: grafana/grafana
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}
volumes:
- grafana_data:/var/lib/grafana
- ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
- ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources:ro
networks:
- monitoring
node_exporter:
image: prom/node-exporter
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'
networks:
- monitoring
volumes:
prometheus_data:
grafana_data:
networks:
monitoring:
backend:
external: true
配置继承和扩展
使用 extends(已弃用,但仍可参考)
# common.yml
version: '3.8'
services:
web_base:
image: nginx:alpine
volumes:
- ./html:/usr/share/nginx/html:ro
networks:
- frontend
# docker-compose.yml
version: '3.8'
services:
web:
extends:
file: common.yml
service: web_base
ports:
- "80:80"
networks:
frontend:
现代化的配置复用
# docker-compose.yml
version: '3.8'
x-common-variables: &common-variables
NODE_ENV: ${NODE_ENV:-production}
DATABASE_URL: ${DATABASE_URL}
REDIS_URL: ${REDIS_URL}
x-app-base: &app-base
build:
context: .
dockerfile: Dockerfile
environment:
<<: *common-variables
networks:
- backend
restart: unless-stopped
services:
web_app:
<<: *app-base
ports:
- "3000:3000"
environment:
<<: *common-variables
SERVICE_TYPE: web
api_app:
<<: *app-base
ports:
- "3001:3000"
environment:
<<: *common-variables
SERVICE_TYPE: api
worker_app:
<<: *app-base
environment:
<<: *common-variables
SERVICE_TYPE: worker
command: npm run worker
networks:
backend:
环境管理脚本
部署脚本
#!/bin/bash
# deploy.sh
set -e
ENVIRONMENT=${1:-development}
ACTION=${2:-up}
case $ENVIRONMENT in
"development")
COMPOSE_FILES="-f docker-compose.yml -f docker-compose.override.yml"
;;
"staging")
COMPOSE_FILES="-f docker-compose.yml -f docker-compose.staging.yml"
;;
"production")
COMPOSE_FILES="-f docker-compose.yml -f docker-compose.prod.yml"
;;
"test")
COMPOSE_FILES="-f docker-compose.yml -f docker-compose.test.yml"
;;
*)
echo "Unknown environment: $ENVIRONMENT"
exit 1
;;
esac
echo "Deploying to $ENVIRONMENT environment..."
# 加载环境变量
if [ -f ".env.$ENVIRONMENT" ]; then
export $(cat .env.$ENVIRONMENT | xargs)
fi
# 执行 Docker Compose 命令
docker-compose $COMPOSE_FILES $ACTION -d
echo "Deployment completed!"
环境切换脚本
#!/bin/bash
# switch-env.sh
ENVIRONMENT=$1
if [ -z "$ENVIRONMENT" ]; then
echo "Usage: $0 <environment>"
echo "Available environments: development, staging, production, test"
exit 1
fi
# 停止当前环境
docker-compose down
# 切换环境变量文件
if [ -f ".env.$ENVIRONMENT" ]; then
cp ".env.$ENVIRONMENT" ".env"
echo "Switched to $ENVIRONMENT environment"
else
echo "Environment file .env.$ENVIRONMENT not found"
exit 1
fi
# 启动新环境
./deploy.sh $ENVIRONMENT
配置验证
配置检查脚本
#!/bin/bash
# validate-config.sh
ENVIRONMENT=${1:-development}
echo "Validating $ENVIRONMENT configuration..."
# 检查配置文件语法
case $ENVIRONMENT in
"development")
docker-compose -f docker-compose.yml -f docker-compose.override.yml config > /dev/null
;;
"production")
docker-compose -f docker-compose.yml -f docker-compose.prod.yml config > /dev/null
;;
*)
echo "Unknown environment: $ENVIRONMENT"
exit 1
;;
esac
if [ $? -eq 0 ]; then
echo "✓ Configuration is valid"
else
echo "✗ Configuration has errors"
exit 1
fi
# 检查必需的环境变量
required_vars=("DATABASE_URL" "REDIS_URL" "SECRET_KEY")
for var in "${required_vars[@]}"; do
if [ -z "${!var}" ]; then
echo "✗ Missing required environment variable: $var"
exit 1
else
echo "✓ $var is set"
fi
done
echo "All validations passed!"
配置差异检查
#!/bin/bash
# diff-configs.sh
ENV1=${1:-development}
ENV2=${2:-production}
echo "Comparing $ENV1 and $ENV2 configurations..."
# 生成配置文件
docker-compose -f docker-compose.yml -f docker-compose.$ENV1.yml config > /tmp/config-$ENV1.yml
docker-compose -f docker-compose.yml -f docker-compose.$ENV2.yml config > /tmp/config-$ENV2.yml
# 比较配置
diff -u /tmp/config-$ENV1.yml /tmp/config-$ENV2.yml
# 清理临时文件
rm /tmp/config-$ENV1.yml /tmp/config-$ENV2.yml
高级配置技巧
1. 条件配置
version: '3.8'
services:
app:
image: myapp
profiles:
- production
- staging
app_dev:
build: .
profiles:
- development
volumes:
- ./src:/app/src
debug_tools:
image: debug-tools
profiles:
- debug
depends_on:
- app
# 启用特定 profile
docker-compose --profile development up
docker-compose --profile production --profile monitoring up
2. 动态配置生成
#!/bin/bash
# generate-config.sh
ENVIRONMENT=$1
TEMPLATE_FILE="docker-compose.template.yml"
OUTPUT_FILE="docker-compose.$ENVIRONMENT.yml"
# 使用 envsubst 替换模板中的变量
envsubst < $TEMPLATE_FILE > $OUTPUT_FILE
echo "Generated $OUTPUT_FILE for $ENVIRONMENT environment"
# docker-compose.template.yml
version: '3.8'
services:
app:
image: myapp:${APP_VERSION}
replicas: ${APP_REPLICAS}
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
deploy:
resources:
limits:
cpus: '${APP_CPU_LIMIT}'
memory: ${APP_MEMORY_LIMIT}
3. 配置加密
version: '3.8'
services:
app:
image: myapp
environment:
- DATABASE_URL_FILE=/run/secrets/database_url
- API_KEY_FILE=/run/secrets/api_key
secrets:
- database_url
- api_key
secrets:
database_url:
file: ./secrets/database_url.txt
api_key:
external: true
name: myapp_api_key
最佳实践
1. 文件组织结构
project/
├── docker-compose.yml # 基础配置
├── docker-compose.override.yml # 开发环境覆盖
├── docker-compose.prod.yml # 生产环境配置
├── docker-compose.staging.yml # 预发布环境配置
├── docker-compose.test.yml # 测试环境配置
├── compose/
│ ├── base.yml # 基础服务
│ ├── web.yml # Web 服务
│ ├── monitoring.yml # 监控服务
│ └── logging.yml # 日志服务
├── env/
│ ├── .env.development
│ ├── .env.staging
│ ├── .env.production
│ └── .env.test
├── scripts/
│ ├── deploy.sh
│ ├── switch-env.sh
│ └── validate-config.sh
└── README.md
2. 命名规范
# 使用一致的命名规范
services:
# 环境前缀
prod_web:
prod_app:
prod_db:
# 或者使用标签
web:
labels:
- "environment=production"
- "service.type=web"
- "service.version=1.0.0"
3. 配置验证
# 在每个配置文件中添加验证
version: '3.8'
# 必需的环境变量检查
x-required-env: &required-env
- DATABASE_URL
- REDIS_URL
- SECRET_KEY
services:
app:
image: myapp
environment: *required-env
4. 文档化
# Docker Compose 配置说明
## 文件说明
- `docker-compose.yml`: 基础配置,包含所有服务的通用设置
- `docker-compose.override.yml`: 开发环境配置,自动加载
- `docker-compose.prod.yml`: 生产环境配置
- `docker-compose.test.yml`: 测试环境配置
## 使用方法
### 开发环境
```bash
docker-compose up # 自动加载 override 文件
生产环境
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
测试环境
docker-compose -f docker-compose.yml -f docker-compose.test.yml up --abort-on-container-exit
环境变量
| 变量名 | 描述 | 默认值 | 必需 |
|---|---|---|---|
| DATABASE_URL | 数据库连接字符串 | - | 是 |
| REDIS_URL | Redis 连接字符串 | - | 是 |
| SECRET_KEY | 应用密钥 | - | 是 |
```
总结
Docker Compose 多文件配置是管理复杂应用部署的强大工具。通过合理使用多文件配置,可以实现:
- 环境分离: 不同环境使用不同的配置文件
- 配置复用: 基础配置可以被多个环境共享
- 模块化管理: 将不同功能的服务分离到不同文件
- 灵活部署: 根据需要组合不同的配置文件
- 安全管理: 敏感配置可以单独管理
关键要点:
- 合理组织配置文件结构
- 使用环境变量进行配置参数化
- 建立配置验证和部署脚本
- 遵循命名规范和最佳实践
- 完善的文档和使用说明
通过这些方法,可以构建一个可维护、可扩展的 Docker Compose 配置体系。