07-数据持久化-容器里数据不丢失
容器删除,数据还在吗?
问题:容器默认的文件系统是临时的,容器删除后,所有数据都会丢失。
场景:
- 运行一个MySQL数据库,插入了大量数据
- 删除容器重新创建
- 数据全部丢失!
解决:使用数据卷(Volume)实现数据持久化。
数据卷(Volume)
什么是数据卷?
数据卷是Docker管理的数据存储机制,独立于容器生命周期。
特点:
- 独立于容器,容器删除后数据还在
- 可以在容器之间共享
- 性能好(直接读写宿主机文件系统)
- 备份和迁移方便
基本操作
1. 创建数据卷
# 创建数据卷
docker volume create my-data
# 查看数据卷
docker volume ls
# 查看数据卷详细信息
docker volume inspect my-data
输出示例:
[
{
"CreatedAt": "2023-01-01T00:00:00Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-data/_data",
"Name": "my-data",
"Options": {},
"Scope": "local"
}
]
Mountpoint:数据卷在宿主机的实际路径
2. 使用数据卷运行容器
# 挂载数据卷
docker run -d -v my-data:/data nginx
# 多个数据卷
docker run -d \
-v mysql-data:/var/lib/mysql \
-v mysql-logs:/var/log/mysql \
mysql:8.0
3. 查看数据卷
# 列出所有数据卷
docker volume ls
# 查看数据卷详细信息
docker volume inspect my-data
# 查看数据卷内容
sudo ls /var/lib/docker/volumes/my-data/_data
4. 删除数据卷
# 删除未使用的数据卷
docker volume prune
# 删除指定数据卷
docker volume rm my-data
# 删除容器时同时删除数据卷(慎用)
docker rm -v my-container
注意:删除数据卷会永久删除数据,无法恢复!
绑定挂载(Bind Mount)
什么是绑定挂载?
将宿主机的目录或文件挂载到容器中。
特点:
- 直接映射宿主机文件系统
- 适合开发环境(实时同步代码)
- 需要处理权限问题
基本用法
# 挂载宿主机目录到容器
docker run -d -v /path/on/host:/path/in/container nginx
# 相对路径(相对于当前目录)
docker run -d -v $(pwd)/html:/usr/share/nginx/html nginx
# 挂载单个文件
docker run -d -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf nginx
读写权限
# 读写(默认)
docker run -d -v /data:/app nginx
# 只读
docker run -d -v /data:/app:ro nginx
# 读写+指定用户
docker run -d -v /data:/app:ro -u 1000:1000 nginx
实战:同步开发代码
# 项目目录
cd my-flask-app
# 挂载当前目录到容器
docker run -d -p 8000:8000 -v $(pwd):/app python:3.11 \
python -u /app/app.py
修改代码后,容器内立即生效!
数据卷 vs 绑定挂载
| 特性 | 数据卷(Volume) | 绑定挂载(Bind Mount) |
|---|---|---|
| 创建方式 | Docker管理 | 用户指定 |
| 存储位置 | /var/lib/docker/volumes |
任意位置 |
| 性能 | 好 | 依赖宿主机文件系统 |
| 迁移 | 方便 | 需要手动处理 |
| 适合场景 | 生产环境 | 开发环境 |
建议:
- 生产环境:使用数据卷
- 开发环境:使用绑定挂载
实战:MySQL数据持久化
1. 创建数据卷
docker volume create mysql-data
2. 运行MySQL容器
docker run -d \
--name my-mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=mydb \
-v mysql-data:/var/lib/mysql \
-p 3306:3306 \
mysql:8.0
3. 插入测试数据
# 连接MySQL
docker exec -it my-mysql mysql -uroot -p123456
# 在MySQL中执行
USE mydb;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100)
);
INSERT INTO users (name, email) VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com'),
('Charlie', 'charlie@example.com');
SELECT * FROM users;
EXIT;
4. 删除容器
docker rm -f my-mysql
5. 重新创建容器(数据还在)
docker run -d \
--name my-mysql-new \
-e MYSQL_ROOT_PASSWORD=123456 \
-v mysql-data:/var/lib/mysql \
-p 3306:3306 \
mysql:8.0
6. 验证数据还在
docker exec -it my-mysql-new mysql -uroot -p123456 -e \
"USE mydb; SELECT * FROM users;"
输出:
+----+---------+---------------------+
| id | name | email |
+----+---------+---------------------+
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Charlie | charlie@example.com |
+----+---------+---------------------+
数据卷备份和恢复
备份数据卷
# 启动一个临时容器挂载数据卷
docker run --rm -v mysql-data:/data -v $(pwd):/backup \
alpine tar czf /backup/mysql-backup.tar.gz -C /data .
# 备份完成,当前目录生成 mysql-backup.tar.gz
恢复数据卷
# 创建新的数据卷
docker volume create mysql-data-new
# 恢复备份
docker run --rm -v mysql-data-new:/data -v $(pwd):/backup \
alpine sh -c "cd /data && tar xzf /backup/mysql-backup.tar.gz"
# 使用恢复的数据卷运行容器
docker run -d \
--name my-mysql-restored \
-e MYSQL_ROOT_PASSWORD=123456 \
-v mysql-data-new:/var/lib/mysql \
-p 3306:3306 \
mysql:8.0
使用docker volume命令备份
# 导出数据卷(需要安装docker-volume-backup插件)
docker run --rm -v mysql-data:/data -v $(pwd):/backup \
alpine tar czf /backup/backup.tar.gz /data
数据卷在容器间共享
场景:多个容器共享数据
# 创建数据卷
docker volume create shared-data
# 容器1写入数据
docker run --name writer -v shared-data:/data alpine sh -c \
"echo 'Hello from writer' > /data/message.txt"
# 容器2读取数据
docker run --name reader -v shared-data:/data alpine cat /data/message.txt
# 输出:Hello from writer
实战:Web应用和日志收集
# 1. 创建日志数据卷
docker volume create app-logs
# 2. Web应用容器写入日志
docker run -d \
--name myapp \
-v app-logs:/app/logs \
nginx
# 3. 日志分析容器读取日志
docker run --rm \
-v app-logs:/logs \
alpine sh -c "tail -f /logs/access.log"
数据卷驱动(Volume Driver)
本地驱动(默认)
# 默认使用local驱动
docker volume create my-data
第三方驱动
# 使用NFS驱动(需要先安装)
docker volume create -d local-persist \
-o mountpoint=/mnt/nfs/share \
--name nfs-data
清理未使用的资源
# 清理未使用的容器
docker container prune
# 清理未使用的镜像
docker image prune
# 清理未使用的数据卷
docker volume prune
# 清理所有未使用的资源
docker system prune
# 清理所有未使用的资源(包括未使用的镜像)
docker system prune -a
注意:清理操作不可逆,会永久删除数据!
最佳实践
1. 生产环境使用数据卷
# 生产环境推荐
docker run -d \
-v mysql-data:/var/lib/mysql \
mysql:8.0
2. 开发环境使用绑定挂载
# 开发环境推荐
docker run -d -v $(pwd):/app myapp
3. 使用.dockerignore避免敏感文件
.env
*.key
*.pem
4. 定期备份重要数据
# 定时备份数据卷
0 2 * * * docker run --rm -v mysql-data:/data -v /backup:/backup \
alpine tar czf /backup/mysql-$(date +\%Y\%m\%d).tar.gz -C /data .
5. 使用只读挂载保护重要数据
docker run -d -v /config:/config:ro nginx
6. 明确指定数据卷名称
# 好
docker run -d -v mysql-prod-data:/var/lib/mysql mysql:8.0
# 不好(自动命名,难以管理)
docker run -d -v /var/lib/mysql mysql:8.0
实战:完整的数据持久化方案
项目结构
myapp/
├── app.py
├── requirements.txt
├── Dockerfile
└── docker-compose.yml
docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8000:8000"
volumes:
- app-data:/app/data
- ./logs:/app/logs
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: mydb
volumes:
- mysql-data:/var/lib/mysql
ports:
- "3306:3306"
volumes:
app-data:
mysql-data:
使用
# 启动所有服务
docker-compose up -d
# 查看数据卷
docker volume ls
# 备份数据卷
docker run --rm -v myapp_mysql-data:/data -v $(pwd):/backup \
alpine tar czf /backup/mysql-backup.tar.gz -C /data .
本章小结
- 数据卷:独立于容器的数据存储,适合生产环境
- 绑定挂载:映射宿主机目录,适合开发环境
- 数据持久化:容器删除后数据不丢失
- 数据备份:定期备份数据卷
- 数据共享:多个容器可以共享同一数据卷
现在已经掌握了数据持久化,下一章我们来学习网络管理!
继续学下去,马上就能做复杂项目了!