Docker 完全指南 / 08 - 数据持久化
08 - 数据持久化
掌握 Docker 的数据管理方案:Volume、Bind Mount 和 tmpfs,确保数据安全与持久化。
8.1 容器存储概述
容器默认的文件系统是临时的。容器删除后,容器层中的所有数据都会丢失。
容器文件系统层次:
┌─────────────────────────────────┐
│ 容器可写层 (Container Layer) │ ← 容器删除后数据丢失!
├─────────────────────────────────┤
│ 镜像层 3 (只读) │
│ 镜像层 2 (只读) │
│ 镜像层 1 (只读) │
└─────────────────────────────────┘
解决数据持久化:
┌─────────────────────────────────┐
│ 容器可写层 │
├─────────────────────────────────┤
│ Volume / Bind Mount │ ← 数据持久化到宿主机
│ (独立于容器生命周期) │
└─────────────────────────────────┘
三种数据持久化方案
| 方案 | 管理方式 | 存储位置 | 适用场景 |
|---|---|---|---|
| Volume | Docker 管理 | /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 Mount | Volume |
|---|---|---|
| 存储位置 | 用户指定 | 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 + NFS | NFS 挂载 | 多主机共享 |
rexray/ebs | AWS EBS | AWS 环境 |
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"
要点回顾
| 要点 | 核心内容 |
|---|---|
| Volume | Docker 管理,推荐用于生产环境 |
| Bind Mount | 用户管理,适合开发环境和配置文件 |
| tmpfs | 内存存储,适合临时和敏感数据 |
| 备份恢复 | 通过临时容器 + tar 命令实现 |
| 命名卷 | 始终使用命名卷,便于管理和识别 |
注意事项
权限问题: Bind Mount 时,容器内的用户需要对挂载目录有读写权限。使用
--user或在 Dockerfile 中设置正确的文件权限。
数据安全:
docker volume prune会删除所有未使用的卷。生产环境务必谨慎使用,或使用--filter保护重要卷。
性能: Bind Mount 在 macOS/Windows 上性能较差(因为经过虚拟化层)。Linux 上性能最佳。
下一步
→ 09 - Compose 基础:学习使用 Docker Compose 编排多容器应用。