Docker Compose 数据持久化
概述
数据持久化是容器化应用的关键需求之一。Docker Compose 提供了多种数据持久化方案,包括卷(Volumes)、绑定挂载(Bind Mounts)和临时文件系统(tmpfs)。本文将详细介绍这些持久化方案的配置方法、使用场景和最佳实践。
数据持久化类型
1. Docker 卷(Volumes)
- 管理方式: 由 Docker 完全管理
- 存储位置: Docker 主机的特定目录
- 特点: 最推荐的持久化方式
- 适用场景: 数据库数据、应用状态、共享数据
2. 绑定挂载(Bind Mounts)
- 管理方式: 直接映射主机文件系统
- 存储位置: 主机的任意位置
- 特点: 直接访问主机文件系统
- 适用场景: 配置文件、源代码、日志文件
3. 临时文件系统(tmpfs)
- 管理方式: 存储在内存中
- 存储位置: 主机内存
- 特点: 高性能,容器停止后数据丢失
- 适用场景: 临时数据、缓存、敏感数据
Docker 卷配置
1. 基本卷配置
version: '3.8'
services:
db:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
postgres_data:
2. 卷驱动配置
volumes:
postgres_data:
driver: local
driver_opts:
type: none
o: bind
device: /host/path/to/data
nfs_data:
driver: local
driver_opts:
type: nfs
o: addr=192.168.1.100,rw
device: ":/path/to/nfs/share"
encrypted_data:
driver: local
driver_opts:
type: ext4
o: loop,encryption=aes256
device: /host/encrypted.img
3. 外部卷
version: '3.8'
services:
app:
image: myapp
volumes:
- shared_data:/data
- backup_data:/backup
volumes:
shared_data:
external: true
backup_data:
external:
name: production_backup_volume
4. 卷标签和元数据
volumes:
app_data:
driver: local
labels:
- "com.example.description=Application data volume"
- "com.example.department=IT"
- "com.example.environment=production"
driver_opts:
type: ext4
device: /dev/sdb1
绑定挂载配置
1. 基本绑定挂载
version: '3.8'
services:
web:
image: nginx
volumes:
- ./html:/usr/share/nginx/html:ro # 只读挂载
- ./config:/etc/nginx/conf.d:rw # 读写挂载(默认)
- /var/log/nginx:/var/log/nginx # 绝对路径
- ~/data:/data # 用户目录
2. 高级绑定挂载
services:
app:
image: myapp
volumes:
- type: bind
source: ./app
target: /usr/src/app
read_only: true
bind:
propagation: rprivate
- type: bind
source: ./config
target: /etc/app
bind:
propagation: shared
create_host_path: true
3. 条件挂载
services:
dev_app:
image: myapp
volumes:
- ./src:/usr/src/app # 开发时挂载源码
- /usr/src/app/node_modules # 排除 node_modules
profiles:
- development
prod_app:
image: myapp:production
# 生产环境不挂载源码
profiles:
- production
数据库持久化
1. PostgreSQL 持久化
version: '3.8'
services:
postgres:
image: postgres:13-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=secretpassword
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
- ./backup:/backup
ports:
- "5432:5432"
restart: unless-stopped
volumes:
postgres_data:
driver: local
driver_opts:
type: none
o: bind
device: /opt/postgres/data
2. MySQL 持久化
services:
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=rootpassword
- MYSQL_DATABASE=myapp
- MYSQL_USER=appuser
- MYSQL_PASSWORD=apppassword
volumes:
- mysql_data:/var/lib/mysql
- ./mysql/conf.d:/etc/mysql/conf.d:ro
- ./mysql/init:/docker-entrypoint-initdb.d:ro
- mysql_logs:/var/log/mysql
command: --default-authentication-plugin=mysql_native_password
volumes:
mysql_data:
mysql_logs:
3. MongoDB 持久化
services:
mongodb:
image: mongo:4.4
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=password
- MONGO_INITDB_DATABASE=myapp
volumes:
- mongodb_data:/data/db
- mongodb_config:/data/configdb
- ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
ports:
- "27017:27017"
volumes:
mongodb_data:
mongodb_config:
4. Redis 持久化
services:
redis:
image: redis:6-alpine
command: redis-server --appendonly yes --requirepass mypassword
volumes:
- redis_data:/data
- ./redis.conf:/usr/local/etc/redis/redis.conf:ro
ports:
- "6379:6379"
volumes:
redis_data:
应用数据持久化
1. 文件上传存储
version: '3.8'
services:
app:
image: myapp
volumes:
- uploads:/app/uploads
- static_files:/app/static
- user_data:/app/data
environment:
- UPLOAD_PATH=/app/uploads
- STATIC_PATH=/app/static
nginx:
image: nginx
volumes:
- uploads:/usr/share/nginx/html/uploads:ro
- static_files:/usr/share/nginx/html/static:ro
- ./nginx.conf:/etc/nginx/nginx.conf:ro
ports:
- "80:80"
volumes:
uploads:
static_files:
user_data:
2. 日志持久化
services:
app:
image: myapp
volumes:
- app_logs:/var/log/app
- ./logrotate.conf:/etc/logrotate.d/app:ro
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
logrotate:
image: alpine
volumes:
- app_logs:/logs
command: |
sh -c '
while true; do
logrotate -f /etc/logrotate.d/app
sleep 3600
done
'
volumes:
app_logs:
3. 配置文件管理
services:
app:
image: myapp
volumes:
- ./config/app.yml:/etc/app/config.yml:ro
- ./config/database.yml:/etc/app/database.yml:ro
- app_runtime_config:/etc/app/runtime
- ./secrets:/etc/app/secrets:ro
environment:
- CONFIG_PATH=/etc/app/config.yml
- DATABASE_CONFIG_PATH=/etc/app/database.yml
volumes:
app_runtime_config:
数据备份策略
1. 数据库备份
version: '3.8'
services:
postgres:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data
- postgres_backup:/backup
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
backup:
image: postgres:13
volumes:
- postgres_backup:/backup
- ./backup-scripts:/scripts:ro
environment:
- PGPASSWORD=password
command: |
sh -c '
while true; do
pg_dump -h postgres -U postgres -d myapp > /backup/backup_$$(date +%Y%m%d_%H%M%S).sql
find /backup -name "*.sql" -mtime +7 -delete
sleep 86400
done
'
depends_on:
- postgres
volumes:
postgres_data:
postgres_backup:
2. 文件系统备份
services:
app:
image: myapp
volumes:
- app_data:/data
- app_backup:/backup
backup_service:
image: alpine
volumes:
- app_data:/source:ro
- app_backup:/backup
- ./backup.sh:/backup.sh:ro
command: |
sh -c '
while true; do
tar -czf /backup/backup_$$(date +%Y%m%d_%H%M%S).tar.gz -C /source .
find /backup -name "*.tar.gz" -mtime +30 -delete
sleep 86400
done
'
volumes:
app_data:
app_backup:
3. 增量备份
services:
rsync_backup:
image: alpine
volumes:
- app_data:/source:ro
- backup_data:/backup
- ./rsync.sh:/rsync.sh:ro
command: |
sh -c '
apk add --no-cache rsync
while true; do
rsync -av --delete /source/ /backup/current/
cp -al /backup/current /backup/snapshot_$$(date +%Y%m%d_%H%M%S)
find /backup -maxdepth 1 -name "snapshot_*" -mtime +7 -exec rm -rf {} \;
sleep 3600
done
'
volumes:
app_data:
backup_data:
性能优化
1. 卷性能调优
volumes:
high_performance_db:
driver: local
driver_opts:
type: ext4
device: /dev/nvme0n1p1 # 使用 NVMe SSD
o: noatime,nodiratime # 禁用访问时间更新
memory_cache:
driver: local
driver_opts:
type: tmpfs
device: tmpfs
o: size=1G,uid=1000,gid=1000
2. 缓存策略
services:
app:
image: myapp
volumes:
- app_data:/data
- cache_data:/cache
tmpfs:
- /tmp:size=100M
- /var/cache:size=200M
environment:
- CACHE_DIR=/cache
- TEMP_DIR=/tmp
volumes:
app_data:
cache_data:
driver: local
driver_opts:
type: tmpfs
device: tmpfs
o: size=500M
3. 读写分离
services:
app:
image: myapp
volumes:
- readonly_data:/data:ro # 只读数据
- readwrite_data:/workspace # 读写工作区
- logs:/var/log # 日志目录
tmpfs:
- /tmp:size=100M # 临时文件
volumes:
readonly_data:
readwrite_data:
logs:
数据迁移
1. 卷数据迁移
#!/bin/bash
# migrate-volume.sh
SOURCE_VOLUME="old_postgres_data"
TARGET_VOLUME="new_postgres_data"
# 创建临时容器进行数据迁移
docker run --rm -v $SOURCE_VOLUME:/source -v $TARGET_VOLUME:/target alpine sh -c "
cp -a /source/. /target/
echo 'Migration completed'
"
2. 跨主机数据迁移
# 源主机
version: '3.8'
services:
data_export:
image: alpine
volumes:
- source_data:/data:ro
command: |
sh -c '
tar -czf - -C /data . | nc target-host 9999
'
volumes:
source_data:
external: true
# 目标主机
version: '3.8'
services:
data_import:
image: alpine
volumes:
- target_data:/data
command: |
sh -c '
nc -l -p 9999 | tar -xzf - -C /data
'
ports:
- "9999:9999"
volumes:
target_data:
监控和维护
1. 卷使用监控
services:
volume_monitor:
image: alpine
volumes:
- postgres_data:/data/postgres:ro
- app_data:/data/app:ro
- logs:/data/logs:ro
command: |
sh -c '
while true; do
echo "=== Volume Usage Report ==="
df -h /data/*
echo "=== Large Files ==="
find /data -type f -size +100M -exec ls -lh {} \;
sleep 3600
done
'
volumes:
postgres_data:
external: true
app_data:
external: true
logs:
external: true
2. 数据完整性检查
services:
integrity_check:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data:ro
environment:
- PGPASSWORD=password
command: |
sh -c '
while true; do
echo "Starting integrity check..."
pg_checksums -D /var/lib/postgresql/data --check
echo "Integrity check completed"
sleep 86400
done
'
volumes:
postgres_data:
external: true
安全考虑
1. 权限管理
services:
app:
image: myapp
user: "1000:1000" # 非 root 用户
volumes:
- app_data:/data
- readonly_config:/config:ro
security_opt:
- no-new-privileges:true
volumes:
app_data:
driver: local
driver_opts:
type: none
o: bind,uid=1000,gid=1000
device: /opt/app/data
readonly_config:
driver: local
driver_opts:
type: none
o: bind,ro
device: /opt/app/config
2. 加密存储
volumes:
encrypted_data:
driver: local
driver_opts:
type: ext4
device: /dev/mapper/encrypted-volume
o: defaults
3. 访问控制
services:
app:
image: myapp
volumes:
- sensitive_data:/data
cap_drop:
- ALL
cap_add:
- DAC_OVERRIDE # 只添加必要的权限
volumes:
sensitive_data:
driver: local
labels:
- "security.level=high"
- "access.restricted=true"
最佳实践
1. 卷命名规范
volumes:
# 使用描述性名称
myapp_postgres_data:
myapp_redis_cache:
myapp_upload_files:
myapp_application_logs:
# 包含环境信息
prod_myapp_postgres_data:
dev_myapp_postgres_data:
staging_myapp_postgres_data:
2. 数据生命周期管理
services:
cleanup:
image: alpine
volumes:
- temp_data:/temp
- logs:/logs
command: |
sh -c '
# 清理临时文件
find /temp -type f -mtime +1 -delete
# 清理旧日志
find /logs -name "*.log" -mtime +30 -delete
# 压缩旧日志
find /logs -name "*.log" -mtime +7 -exec gzip {} \;
'
restart: unless-stopped
volumes:
temp_data:
logs:
3. 备份验证
services:
backup_verify:
image: postgres:13
volumes:
- postgres_backup:/backup:ro
environment:
- PGPASSWORD=password
command: |
sh -c '
for backup in /backup/*.sql; do
echo "Verifying backup: $backup"
psql -h test-db -U postgres -d test_db < "$backup"
if [ $? -eq 0 ]; then
echo "Backup $backup is valid"
else
echo "Backup $backup is corrupted"
fi
done
'
volumes:
postgres_backup:
external: true
总结
Docker Compose 数据持久化是确保容器化应用数据安全和可靠性的关键技术。通过合理配置卷、绑定挂载和临时文件系统,可以满足不同场景的数据存储需求。
关键要点:
- 选择合适的持久化方案(卷 vs 绑定挂载 vs tmpfs)
- 实施完善的备份和恢复策略
- 考虑性能优化和安全性
- 建立监控和维护机制
- 遵循最佳实践和命名规范
正确的数据持久化配置是构建可靠生产环境的基础,需要根据具体业务需求和技术要求进行合理设计。