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 数据持久化是确保容器化应用数据安全和可靠性的关键技术。通过合理配置卷、绑定挂载和临时文件系统,可以满足不同场景的数据存储需求。

关键要点:

  1. 选择合适的持久化方案(卷 vs 绑定挂载 vs tmpfs)
  2. 实施完善的备份和恢复策略
  3. 考虑性能优化和安全性
  4. 建立监控和维护机制
  5. 遵循最佳实践和命名规范

正确的数据持久化配置是构建可靠生产环境的基础,需要根据具体业务需求和技术要求进行合理设计。

powered by Gitbook© 2025 编外计划 | 最后修改: 2025-08-29 15:40:15

results matching ""

    No results matching ""