Docker 数据持久化
为什么需要数据持久化
容器是临时的,当容器被删除时,容器内的数据也会丢失。数据持久化用于保存重要数据(如数据库文件、用户上传的文件)。
Docker 存储方式
| 方式 | 说明 | 适用场景 |
|---|---|---|
| Volume | Docker 管理的数据卷 | 生产环境,推荐 |
| Bind Mount | 挂载主机目录 | 开发环境 |
| tmpfs Mount | 存储在内存中 | 临时数据,敏感数据 |
| 容器内存储 | 容器自带存储层 | 临时测试 |
Volume(数据卷)
Volume 是 Docker 管理的存储区域,数据独立于容器生命周期。
创建和使用 Volume
bash
# 创建数据卷
docker volume create mydata
# 列出所有数据卷
docker volume ls
# 查看数据卷详情
docker volume inspect mydata
# 输出:
# {
# "Name": "mydata",
# "Mountpoint": "/var/lib/docker/volumes/mydata/_data",
# ...
# }1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
挂载 Volume
bash
# 方式1:-v 参数
docker run -d \
--name mysql \
-v mydata:/var/lib/mysql \
mysql:8
# 方式2:--mount 参数(更明确)
docker run -d \
--name mysql \
--mount type=volume,source=mydata,target=/var/lib/mysql \
mysql:81
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
Volume 实战:MySQL 数据持久化
bash
# 创建数据卷
docker volume create mysql_data
# 运行 MySQL 容器
docker run -d \
--name mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=myapp \
-v mysql_data:/var/lib/mysql \
-p 3306:3306 \
mysql:8
# 测试数据持久化
# 1. 进入容器创建数据库
docker exec -it mysql bash
mysql -u root -p
# CREATE DATABASE test;
# EXIT
# 2. 删除容器
docker rm -f mysql
# 3. 重新创建容器(使用相同的数据卷)
docker run -d \
--name mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=myapp \
-v mysql_data:/var/lib/mysql \
-p 3306:3306 \
mysql:8
# 4. 验证数据是否保留
docker exec -it mysql mysql -u root -p -e "SHOW DATABASES;"
# test 数据库应该还在1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
管理 Volume
bash
# 删除未使用的卷
docker volume prune
# 删除指定卷
docker volume rm mydata
# 删除所有未使用的卷
docker volume prune -a
# 批量删除
docker volume ls -q | xargs docker volume rm1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
Bind Mount(绑定挂载)
将主机目录直接挂载到容器,文件修改立即生效。
基本用法
bash
# 挂载主机目录
docker run -d \
--name nginx \
-v /host/html:/usr/share/nginx/html:ro \
-p 80:80 \
nginx
# 参数说明:
# /host/html - 主机路径
# /usr/share/nginx/html - 容器路径
# :ro - 只读模式(可选)1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
实战:开发环境热重载
bash
# 项目结构
# /home/user/myapp/
# ├── src/
# ├── package.json
# └── ...
# 挂载项目目录到容器
docker run -d \
--name node-app \
-v $(pwd):/app \
-v /app/node_modules \
-p 3000:3000 \
node:20 \
npm run dev
# 说明:
# -v $(pwd):/app 挂载项目代码(修改会同步)
# -v /app/node_modules 保留容器内的 node_modules(避免覆盖)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Bind Mount 权限
bash
# 只读挂载
-v /host/data:/container/data:ro
# 读写挂载(默认)
-v /host/data:/container/data:rw
# 指定 UID/GID
docker run -d \
--name app \
-v $(pwd):/app \
-u 1000:1000 \
node:201
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Windows/Mac 注意事项
在 Windows 和 Mac 上,Docker Desktop 中的 Bind Mount 性能较差:
bash
# 推荐:使用 Volume 获得更好性能
docker volume create app_data
docker run -v app_data:/app node:20
# 或者:将代码复制到容器内
docker cp ./app node-app:/app
docker exec node-app npm run build1
2
3
4
5
6
7
2
3
4
5
6
7
tmpfs Mount
存储在内存中,速度快但容器删除后数据丢失,适合敏感数据。
bash
# 挂载 tmpfs
docker run -d \
--name secure-app \
--tmpfs /run/secrets \
nginx
# 参数说明:
# --tmpfs 挂载路径 - 不需要指定源路径
# tmpfs 存储在宿主机的内存中
# 使用 --mount(更详细)
docker run -d \
--name secure-app \
--mount type=tmpfs,destination=/run/secrets \
nginx1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
数据共享
容器间共享数据
bash
# 创建共享数据卷
docker volume create shared_data
# 容器 A 写入数据
docker run -d --name container-a -v shared_data:/data busybox sh -c "echo hello > /data/file.txt"
# 容器 B 读取数据
docker run -d --name container-b -v shared_data:/data busybox cat /data/file.txt
# 查看容器 B 输出
docker logs container-b # 输出:hello1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
使用 --volumes-from 共享
bash
# 基于已有容器创建新容器
docker run -d --name web1 nginx
docker run -d --name web2 --volumes-from web1 nginx
# web2 可以访问 web1 的数据卷1
2
3
4
2
3
4
Docker Compose 数据卷
yaml
version: '3.8'
services:
mysql:
image: mysql:8
volumes:
- mysql_data:/var/lib/mysql
- ./mysql.conf:/etc/mysql/conf.d # Bind Mount
environment:
MYSQL_ROOT_PASSWORD: secret
redis:
image: redis:7
volumes:
- redis_data:/data
- ./redis.conf:/usr/local/etc/redis/redis.conf
volumes:
mysql_data:
redis_data:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
权限问题
用户权限
容器默认以 root 用户运行,可能导致文件权限问题:
bash
# 解决方案1:指定用户
docker run -u 1000:1000 -v $(pwd):/app node:20
# 解决方案2:修改主机目录权限
chmod 777 /host/data
# 解决方案3:在 Dockerfile 中创建用户
FROM node:20
RUN useradd -m appuser
USER appuser
COPY --chown=appuser:appuser . /app1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
SELinux 问题
bash
# 如果遇到 SELinux 权限问题
docker run -d \
--name app \
-v $(pwd):/app:z \
node:20
# :z 或 :Z 标记让 SELinux 允许容器访问1
2
3
4
5
6
2
3
4
5
6
备份和恢复
备份 Volume
bash
# 备份 MySQL 数据卷
docker run --rm \
-v mysql_data:/data \
-v $(pwd):/backup \
alpine \
tar czf /backup/mysql_backup.tar.gz -C /data .
# 查看备份
ls -la mysql_backup.tar.gz1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
恢复 Volume
bash
# 创建临时容器
docker volume create mysql_data_new
# 解压备份
docker run --rm \
-v mysql_data_new:/data \
-v $(pwd):/backup \
alpine \
tar xzf /backup/mysql_backup.tar.gz -C /data
# 使用恢复的数据卷
docker run -d \
--name mysql \
-v mysql_data_new:/var/lib/mysql \
mysql:81
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
常见问题
1. 挂载目录为空
如果挂载的主机目录不存在,Docker 会创建一个空目录:
bash
# 主机目录不存在
-v /host/notexist:/container/data
# Docker 会在主机创建 /host/notexist
# 容器内写入的数据会在主机出现1
2
3
4
2
3
4
2. 性能对比
| 类型 | 性能 | 适用场景 |
|---|---|---|
| Volume | 高 | 生产环境,Linux 主机 |
| Bind Mount | 中 | 开发环境 |
| tmpfs | 最高 | 临时数据,敏感信息 |
3. 清理孤立卷
bash
# 列出孤立数据卷
docker volume ls -f dangling=true
# 删除孤立数据卷
docker volume prune1
2
3
4
5
2
3
4
5
[[返回 Docker 首页|docker/index]]