强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Docker 完全指南 / 08 - 数据持久化

08 - 数据持久化

掌握 Docker 的数据管理方案:Volume、Bind Mount 和 tmpfs,确保数据安全与持久化。


8.1 容器存储概述

容器默认的文件系统是临时的。容器删除后,容器层中的所有数据都会丢失。

容器文件系统层次:
  ┌─────────────────────────────────┐
  │  容器可写层 (Container Layer)    │  ← 容器删除后数据丢失!
  ├─────────────────────────────────┤
  │  镜像层 3 (只读)                │
  │  镜像层 2 (只读)                │
  │  镜像层 1 (只读)                │
  └─────────────────────────────────┘

解决数据持久化:
  ┌─────────────────────────────────┐
  │  容器可写层                      │
  ├─────────────────────────────────┤
  │  Volume / Bind Mount            │  ← 数据持久化到宿主机
  │  (独立于容器生命周期)             │
  └─────────────────────────────────┘

三种数据持久化方案

方案管理方式存储位置适用场景
VolumeDocker 管理/var/lib/docker/volumes/数据库、应用数据
Bind Mount用户管理任意宿主机路径开发环境、配置文件
tmpfs内存宿主机内存临时数据、敏感信息

8.2 Volume(数据卷)

Volume 是 Docker 推荐的数据持久化方式,由 Docker 管理。

创建与管理

# 创建数据卷
docker volume create my-data

# 创建带标签的卷
docker volume create --label env=production --label app=postgres pg-data

# 列出所有卷
docker volume ls

# 查看卷详情
docker volume inspect my-data

# 输出示例:
# {
#   "CreatedAt": "2024-01-01T00:00:00+08:00",
#   "Driver": "local",
#   "Labels": {},
#   "Mountpoint": "/var/lib/docker/volumes/my-data/_data",
#   "Name": "my-data"
# }

# 删除指定卷
docker volume rm my-data

# 删除所有未使用的卷
docker volume prune

# 按标签过滤删除
docker volume prune --filter "label!=keep"

使用 Volume

# 使用 -v 挂载卷
docker run -d --name db \
    -v pg-data:/var/lib/postgresql/data \
    -e POSTGRES_PASSWORD=secret \
    postgres:16

# 使用 --mount 语法(更明确)
docker run -d --name db \
    --mount type=volume,source=pg-data,target=/var/lib/postgresql/data \
    -e POSTGRES_PASSWORD=secret \
    postgres:16

# 只读挂载
docker run -d --name web \
    -v app-data:/usr/share/nginx/html:ro \
    nginx:alpine

Volume 的优势

优势说明
Docker 管理创建、备份、迁移方便
跨平台支持 Linux 和 Windows 容器
可共享多个容器可挂载同一卷
驱动支持支持远程存储(NFS、云存储等)
安全隔离容器无法直接访问宿主机文件系统

8.3 Bind Mount(绑定挂载)

Bind Mount 将宿主机上的任意目录或文件挂载到容器中。

# 基本语法 (-v)
docker run -d --name dev-web \
    -v /home/user/html:/usr/share/nginx/html \
    nginx:alpine

# 使用 --mount 语法
docker run -d --name dev-web \
    --mount type=bind,source=/home/user/html,target=/usr/share/nginx/html \
    nginx:alpine

# 挂载单个文件
docker run -d --name web \
    -v ./nginx.conf:/etc/nginx/nginx.conf:ro \
    nginx:alpine

# 只读挂载
docker run -d --name web \
    -v ./config:/etc/app/config:ro \
    my-app:latest

# 使用相对路径(相对于当前目录)
docker run -d -v ./data:/app/data my-app:latest

Bind Mount vs Volume 对比

特性Bind MountVolume
存储位置用户指定Docker 管理 (/var/lib/docker/volumes/)
初始化不会预填充从镜像预填充
可移植性依赖宿主机路径Docker 管理,可移植
备份用户自行备份Docker 工具支持
适用场景开发环境生产环境
安全性容器可访问宿主机文件受限于 Docker 管理的目录

注意: Bind Mount 时如果宿主机路径不存在,Docker 会自动创建该目录(使用 root 权限),可能导致权限问题。


8.4 tmpfs Mount(内存挂载)

tmpfs 将数据存储在宿主机内存中,不写入磁盘。

# 创建 tmpfs 挂载
docker run -d --name secure-app \
    --tmpfs /tmp:rw,size=100m,mode=1777 \
    nginx:alpine

# 使用 --mount 语法
docker run -d --name secure-app \
    --mount type=tmpfs,target=/tmp,tmpfs-size=100m,tmpfs-mode=1777 \
    nginx:alpine

# 同时使用 tmpfs 和 volume
docker run -d \
    --tmpfs /tmp:rw,size=100m \
    -v data:/var/lib/data \
    my-app:latest
特性tmpfs
存储位置宿主机内存
持久化❌ 容器停止后数据丢失
性能极快(内存速度)
适用场景临时文件、敏感数据(密钥、令牌)

8.5 匿名卷 vs 命名卷

# 匿名卷(自动生成随机名称)
docker run -d -v /var/lib/postgresql/data postgres:16

# 命名卷(有明确名称)
docker run -d -v pg-data:/var/lib/postgresql/data postgres:16

# 查看卷列表
docker volume ls
# DRIVER    VOLUME NAME
# local     pg-data                    ← 命名卷
# local     a1b2c3d4e5f6...           ← 匿名卷
# 清理匿名卷
docker volume prune

最佳实践: 始终使用命名卷(Named Volume),便于管理和识别。


8.6 卷驱动(Volume Driver)

Docker 支持通过卷驱动挂载远程存储。

常用卷驱动

驱动说明用途
local本地存储(默认)单机环境
local + NFSNFS 挂载多主机共享
rexray/ebsAWS EBSAWS 环境
flocker多后端支持跨平台
convoy快照支持备份恢复

NFS 卷示例

# 使用 NFS 挂载
docker volume create \
    --driver local \
    --opt type=nfs \
    --opt o=addr=192.168.1.100,rw,nfsvers=4 \
    --opt device=:/shared/data \
    nfs-data

docker run -d -v nfs-data:/app/data my-app:latest

8.7 数据备份与恢复

备份 Volume

# 方法: 使用临时容器挂载卷并导出
docker run --rm \
    -v pg-data:/source:ro \
    -v $(pwd):/backup \
    alpine:latest \
    tar czf /backup/pg-data-backup-$(date +%Y%m%d).tar.gz -C /source .

# 验证备份文件
ls -lh pg-data-backup-*.tar.gz
tar tzf pg-data-backup-20240101.tar.gz

恢复 Volume

# 创建新卷
docker volume create pg-data-restored

# 恢复数据
docker run --rm \
    -v pg-data-restored:/target \
    -v $(pwd):/backup:ro \
    alpine:latest \
    tar xzf /backup/pg-data-backup-20240101.tar.gz -C /target

# 验证恢复
docker run --rm -v pg-data-restored:/data alpine:latest ls -la /data

迁移 Volume 到其他主机

# 主机 A: 导出
docker run --rm -v my-data:/source:ro alpine \
    tar czf - -C /source . > my-data.tar.gz

# 传输到主机 B
scp my-data.tar.gz user@host-b:/tmp/

# 主机 B: 导入
docker volume create my-data
docker run --rm -v my-data:/target alpine \
    sh -c 'cd /target && tar xzf -' < /tmp/my-data.tar.gz

8.8 实战:数据库持久化

PostgreSQL 数据持久化

# 运行 PostgreSQL 并持久化数据
docker run -d --name postgres \
    -v pg-data:/var/lib/postgresql/data \
    -e POSTGRES_USER=admin \
    -e POSTGRES_PASSWORD=secret \
    -e POSTGRES_DB=myapp \
    -p 5432:5432 \
    postgres:16

# 验证数据持久化
docker exec -it postgres psql -U admin -d myapp -c "CREATE TABLE test (id serial PRIMARY KEY);"
docker exec -it postgres psql -U admin -d myapp -c "INSERT INTO test VALUES (1);"

# 停止并删除容器
docker stop postgres && docker rm postgres

# 重新创建容器,挂载同一卷
docker run -d --name postgres-new \
    -v pg-data:/var/lib/postgresql/data \
    -e POSTGRES_USER=admin \
    -e POSTGRES_PASSWORD=secret \
    -e POSTGRES_DB=myapp \
    -p 5432:5432 \
    postgres:16

# 数据仍然存在!
docker exec -it postgres-new psql -U admin -d myapp -c "SELECT * FROM test;"

MySQL 数据持久化

docker run -d --name mysql \
    -v mysql-data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=secret \
    -e MYSQL_DATABASE=myapp \
    -p 3306:3306 \
    mysql:8.0

Redis 数据持久化

docker run -d --name redis \
    -v redis-data:/data \
    -p 6379:6379 \
    redis:7-alpine \
    redis-server --appendonly yes

8.9 实战:开发环境的 Bind Mount

# 开发模式: 源代码实时同步
docker run -d --name dev-app \
    -v $(pwd)/src:/app/src \
    -v $(pwd)/package.json:/app/package.json:ro \
    -v /app/node_modules \
    -p 3000:3000 \
    node:20-alpine \
    sh -c "cd /app && npm install && npm run dev"

技巧: 使用匿名卷 -v /app/node_modules 防止本地 node_modules 覆盖容器内的依赖。


8.10 存储最佳实践

选择指南

数据类型判断:
  ├── 数据库数据 → Named Volume
  ├── 应用上传文件 → Named Volume
  ├── 配置文件 → Bind Mount (只读)
  ├── 源代码 → Bind Mount (开发环境)
  ├── 日志文件 → Bind Mount 或日志驱动
  ├── 临时缓存 → tmpfs
  └── 密钥/令牌 → tmpfs 或 Docker Secrets

清理策略

# 查看存储使用情况
docker system df -v

# 清理所有未使用的资源(容器、网络、卷、镜像)
docker system prune --volumes -a

# 仅清理未使用的卷
docker volume prune

# 按标签保护重要卷
docker volume create --label keep=true important-data
docker volume prune --filter "label!=keep"

要点回顾

要点核心内容
VolumeDocker 管理,推荐用于生产环境
Bind Mount用户管理,适合开发环境和配置文件
tmpfs内存存储,适合临时和敏感数据
备份恢复通过临时容器 + tar 命令实现
命名卷始终使用命名卷,便于管理和识别

注意事项

权限问题: Bind Mount 时,容器内的用户需要对挂载目录有读写权限。使用 --user 或在 Dockerfile 中设置正确的文件权限。

数据安全: docker volume prune 会删除所有未使用的卷。生产环境务必谨慎使用,或使用 --filter 保护重要卷。

性能: Bind Mount 在 macOS/Windows 上性能较差(因为经过虚拟化层)。Linux 上性能最佳。


下一步

09 - Compose 基础:学习使用 Docker Compose 编排多容器应用。