存储与数据持久化

7.1 三种存储方式

方式说明持久性性能
VolumeDocker 管理的存储容器删除后保留
Bind Mount挂载宿主机目录取决于宿主机最好
tmpfs内存临时存储容器删除后丢失最快

7.2 Volume 详解

创建与管理

# 创建命名卷
docker volume create mydata
 
# 列出所有卷
docker volume ls
 
# 卷详情
docker volume inspect mydata
 
# 查看存储位置
docker volume inspect mydata --format '{{.Mountpoint}}'
# 默认:/var/lib/docker/volumes/mydata/_data
 
# 删除卷
docker volume rm mydata
 
# 清理未使用卷
docker volume prune

使用 Volume

# 命名卷
docker run -d -v mydata:/var/lib/mysql --name db mysql
 
# 匿名卷(自动生成随机名)
docker run -d -v /var/lib/mysql --name db mysql
 
# 使用 --mount(更明确)
docker run -d \
  --mount type=volume,source=mydata,target=/var/lib/mysql \
  --name db mysql
 
# 多容器共享同一卷
docker run -d -v shared:/data --name writer \
  alpine sh -c "while true; do date >> /data/log.txt; sleep 5; done"
 
docker run -it -v shared:/data --name reader \
  alpine tail -f /data/log.txt

Volume 备份与恢复

# 备份 Volume 到宿主机
docker run --rm \
  -v mydata:/source:ro \
  -v $(pwd):/backup \
  alpine tar czf /backup/mydata-backup.tar.gz -C /source .
 
# 从备份恢复
docker run --rm \
  -v mydata:/target \
  -v $(pwd):/backup \
  alpine tar xzf /backup/mydata-backup.tar.gz -C /target
 
# 迁移 Volume(从旧服务器到新服务器)
# 旧服务器
docker run --rm -v mydata:/data -v $(pwd):/backup \
  alpine tar czf /backup/mydata.tar.gz -C /data .
 
# 传输文件到新服务器
scp mydata.tar.gz newserver:/path/
 
# 新服务器
docker volume create mydata
docker run --rm -v mydata:/data -v $(pwd):/backup \
  alpine tar xzf /backup/mydata.tar.gz -C /data

7.3 Bind Mount 详解

基本用法

# 挂载当前目录
docker run -d -v $(pwd)/app:/app node:alpine node /app/server.js
 
# 使用 --mount
docker run -d \
  --mount type=bind,source=$(pwd)/app,target=/app \
  node:alpine node /app/server.js
 
# 只读挂载
docker run -d \
  --mount type=bind,source=$(pwd)/config,target=/etc/nginx/nginx.conf,readonly \
  nginx
 
# 开发环境常用:挂载代码目录 + 命令覆盖
docker run -it --rm \
  -v $(pwd):/app \
  -w /app \
  node:alpine sh

权限问题

# 容器内用户 UID/GID 与宿主机不匹配
# 容器内 root (0:0) 写入的文件,宿主机可能显示为 root
 
# 解决:指定 UID/GID
docker run --user $(id -u):$(id -g) -v $(pwd):/app node:alpine
 
# 或在 Dockerfile 中创建匹配的用户
# RUN useradd -u 1000 appuser
# USER appuser

开发 vs 生产

# 开发环境:Bind Mount(代码实时同步)
docker run -it --rm -v $(pwd):/app -w /app node:alpine npm run dev
 
# 生产环境:COPY 到镜像中(不依赖宿主机)
# COPY . /app
# CMD ["node", "server.js"]
# 或使用 Volume 存储配置文件

7.4 tmpfs(内存存储)

# 临时文件存储在内存中
docker run -d \
  --tmpfs /tmp:size=100m \
  --tmpfs /var/log:size=50m \
  alpine
 
# 使用 --mount
docker run -d \
  --mount type=tmpfs,target=/tmp,tmpfs-size=100m \
  alpine
 
# 适用场景:敏感数据、临时缓存、高频 I/O

7.5 数据持久化最佳实践

数据库容器

# MySQL + 持久化
docker run -d \
  -e MYSQL_ROOT_PASSWORD=secret \
  -v mysql_data:/var/lib/mysql \
  -p 3306:3306 \
  --restart unless-stopped \
  mysql:8
 
# PostgreSQL + 持久化 + 初始化脚本
docker run -d \
  -e POSTGRES_PASSWORD=secret \
  -v pg_data:/var/lib/postgresql/data \
  -v $(pwd)/init.sql:/docker-entrypoint-initdb.d/init.sql:ro \
  -p 5432:5432 \
  postgres:16-alpine

配置管理

# 配置文件用 Bind Mount(修改后重启容器生效)
docker run -d \
  -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
  -v $(pwd)/html:/usr/share/nginx/html:ro \
  -p 80:80 \
  nginx
 
# 敏感配置用 Docker Secret(Swarm 模式)
docker secret create db_password password.txt
docker service create --secret db_password mysql

存储驱动与性能

# 查看当前存储驱动
docker info | grep "Storage Driver"
 
# 推荐使用 overlay2
# /etc/docker/daemon.json
# {
#   "storage-driver": "overlay2"
# }
 
# 重启 Docker 使配置生效
sudo systemctl restart docker

7.6 卷标签与过滤

# 创建带标签的卷
docker volume create --label env=dev --label project=myapp mydata
 
# 按标签过滤卷
docker volume ls --filter label=env=dev
docker volume ls --filter label=project=myapp
 
# 删除特定标签的卷
docker volume prune --filter label=env=dev

7.7 Docker Compose 中的存储

# docker-compose.yml
version: '3.8'
 
services:
  db:
    image: mysql:8
    environment:
      MYSQL_ROOT_PASSWORD: secret
    volumes:
      - mysql_data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    ports:
      - "3306:3306"
 
  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes
 
  nginx:
    image: nginx
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - static_files:/usr/share/nginx/html
    ports:
      - "80:80"
 
volumes:
  mysql_data:
  redis_data:
  static_files:
# Compose 卷操作
docker-compose up -d
 
# 查看 Compose 创建的卷
docker volume ls --filter label=com.docker.compose.project=myapp
 
# 删除项目所有卷
docker-compose down -v

7.8 数据迁移策略

从主机迁移到容器

# 方法一:先复制到 Volume,再挂载
docker run --rm \
  -v mydata:/data \
  -v /host/path:/host-data \
  alpine cp -r /host-data/* /data/
 
# 方法二:使用 tar 管道
tar czf - -C /host/path . | docker run --rm -i \
  -v mydata:/data \
  alpine tar xzf - -C /data

容器间数据迁移

# 将容器 A 的数据复制到容器 B
docker cp container_a:/app/data/. /tmp/transfer/
docker cp /tmp/transfer/. container_b:/app/data/
 
# 使用共享卷方式
docker run --rm \
  -v source_data:/source:ro \
  -v target_data:/target \
  alpine sh -c "cp -r /source/* /target/"

7.9 常见问题排查

权限与所有权

# 查看容器内文件所有权
docker exec <container> ls -la /data
 
# 修复权限问题
docker run --rm \
  -v mydata:/data \
  alpine chown -R 1000:1000 /data
 
# 使用 --user 运行容器避免权限问题
docker run --user 1000:1000 -v mydata:/data alpine ls -la /data

磁盘空间管理

# 查看 Docker 磁盘使用详情
docker system df
 
# 查看各卷占用空间
docker system df -v
 
# 清理已停止容器的卷
docker volume prune
 
# 清理所有未使用资源
docker system prune --all --volumes
 
# 查看容器内大文件占用
docker exec <container> du -sh /* | sort -rh | head -10